Привет всем, весной я написал бэкенд API и добавил аутентификацию jwt. Проблема в том, что я написал все асинхронно, и теперь аутентификация не работает с асинхронными методами. Токен правильный и работает с неасинхронными методами. Кажется, что есть два запроса, но он только один. Это логи:
Привет всем, весной я написал бэкенд API и добавил аутентификацию jwt. Проблема в том, что я написал все асинхронно, и теперь аутентификация не работает с асинхронными методами. Токен правильный и работает с неасинхронными методами. Кажется, что есть два запроса, но он только один. Это логи: [code]2024-12-01T14:06:14.483+01:00 DEBUG 24364 --- [nio-8010-exec-1] o.s.security.web.FilterChainProxy : Securing POST /api/videos/057aed00-6ac6-4353-aec7-be0faf5835c1/like 2024-12-01T14:06:14.490+01:00 DEBUG 24364 --- [nio-8010-exec-1] c.d.d.JwtAuthenticationFilter : Start of doFilterInternal: Request URL: http://localhost:8010/api/videos/057aed00-6ac6-4353-aec7-be0faf5835c1/like 2024-12-01T14:06:14.565+01:00 DEBUG 24364 --- [nio-8010-exec-1] c.d.d.JwtAuthenticationFilter : Extracted username from JWT: 1234 Hibernate: select u1_0.uuid,u1_0.banned,u1_0.beta_key,u1_0.daily_quest_id,u1_0.email,u1_0.enabled,u1_0.followers,u1_0.password,u1_0.profile_picture,u1_0.username,u1_0.verification_code,u1_0.verification_expiration from users u1_0 where u1_0.email=? Hibernate: select q1_0.uuid,q1_0.description,q1_0.dislikes,q1_0.likes,q1_0.title from quest q1_0 where q1_0.uuid=? 2024-12-01T14:06:14.701+01:00 INFO 24364 --- [nio-8010-exec-1] c.d.d.JwtAuthenticationFilter : Authentication successfully set for user: 1234 2024-12-01T14:06:14.704+01:00 DEBUG 24364 --- [nio-8010-exec-1] o.s.security.web.FilterChainProxy : Secured POST /api/videos/057aed00-6ac6-4353-aec7-be0faf5835c1/like 2024-12-01T14:06:14.784+01:00 DEBUG 24364 --- [nio-8010-exec-1] c.d.d.JwtAuthenticationFilter : End of doFilterInternal: Authentication: UsernamePasswordAuthenticationToken [Principal=com.dayquest.dayquestbackend.user.User@99881f6, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=null], Granted Authorities=[]] Hibernate: select u1_0.uuid,u1_0.banned,u1_0.beta_key,dq1_0.uuid,dq1_0.description,dq1_0.dislikes,dq1_0.likes,dq1_0.title,u1_0.email,u1_0.enabled,u1_0.followers,u1_0.password,u1_0.profile_picture,u1_0.username,u1_0.verification_code,u1_0.verification_expiration from users u1_0 left join quest dq1_0 on dq1_0.uuid=u1_0.daily_quest_id where u1_0.uuid=? Hibernate: select v1_0.uuid,v1_0.description,v1_0.down_votes,v1_0.file_path,v1_0.quest_uuid,v1_0.thumbnail,v1_0.title,v1_0.up_votes,v1_0.user_id from video v1_0 where v1_0.uuid=? Hibernate: select lv1_0.user_id,lv1_0.video_id from liked_videos lv1_0 where lv1_0.user_id=? 2024-12-01T14:06:15.144+01:00 DEBUG 24364 --- [nio-8010-exec-3] o.s.security.web.FilterChainProxy : Securing POST /api/videos/057aed00-6ac6-4353-aec7-be0faf5835c1/like 2024-12-01T14:06:15.144+01:00 DEBUG 24364 --- [nio-8010-exec-3] o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to anonymous SecurityContext [/code] и это код: [code]@PostMapping("/{uuid}/like") @Async public CompletableFuture likeVideo( @PathVariable UUID uuid, @RequestBody UuidDTO userUuid, Principal principal) {
return CompletableFuture.supplyAsync(() -> { try { // Use the current authentication directly Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth == null || !auth.isAuthenticated()) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); }
User currentUser = (User) auth.getPrincipal(); Optional user = userRepository.findById(UUID.fromString(userUuid.getUuid())); Optional video = videoRepository.findById(uuid);
// Verify the user matches the authenticated user if (user.isEmpty() || !user.get().getEmail().equals(currentUser.getEmail())) { return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); }
if (user.get().getLikedVideos().contains(uuid)) { return ResponseEntity.status(HttpStatus.NOT_ACCEPTABLE).build(); }
if (user.get().getDislikedVideos().contains(uuid)) { user.get().getDislikedVideos().remove(uuid); video.get().setDownVotes(video.get().getDownVotes() - 1); videoRepository.save(video.get()); }
@Bean public UserDetailsService userDetailsService(UserRepository userRepository) { return username -> { User user = userRepository.findByEmail(username); if (user == null) { throw new UsernameNotFoundException("User not found with email: " + username); } return org.springframework.security.core.userdetails.User .withUsername(user.getEmail()) .password(user.getPassword()) .authorities(Collections.emptyList()) .build(); }; }
@Bean public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
@PostConstruct public void init() { SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL); } }
@Component public class JwtAuthenticationFilter extends OncePerRequestFilter { private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
private final JwtService jwtService; private final UserDetailsService userDetailsService;
if (authHeader == null || !authHeader.startsWith("Bearer ")) { logger.debug("No valid Authorization header found. Proceeding without authentication."); filterChain.doFilter(request, response); return; }
try { final String jwt = authHeader.substring(7); final String username = jwtService.extractUsername(jwt);
logger.debug("Extracted username from JWT: {}", username);
// Proceed only if username is not null and no authentication is set if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (jwtService.isTokenValid(jwt, userDetails)) { UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities() ); authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// Set SecurityContext SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); securityContext.setAuthentication(authToken); SecurityContextHolder.setContext(securityContext);
logger.info("Authentication successfully set for user: {}", username); } else { logger.warn("Invalid JWT token for user: {}", username); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.getWriter().write("Invalid token"); return; } }
// Proceed with the filter chain filterChain.doFilter(request, response); } catch (Exception e) { logger.error("Error occurred during JWT processing", e); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.getWriter().write("Authentication failed: " + e.getMessage()); } finally { logger.debug("End of doFilterInternal: Authentication: {}", SecurityContextHolder.getContext().getAuthentication()); } } }
[/code] Я пробовал использовать делегирующийSecurityContextAsyncTaskExecutor в AsyncConfig, но это не сработало.