i am currently working on a spring boot project. i am using spring boot vv3.1.4 and spring security v6.1.4. i have a JWT authentication set up in my project which works fine for HTTP requests. I also have websocket communication in the app. When establishing the websocket handshake i am sending the token as a query parameter and getting it in the JWT service which works fine for now. My issue is withe websocket messages. I have this preSend channel interceptor:
public class CustomChannelInterceptor implements ChannelInterceptor {
private final JwtService jwtService;
private final UserService userService;
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor =
MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
String authorizationHeader = accessor.getFirstNativeHeader("Authorization");
if (authorizationHeader != null) {
if (authorizationHeader.startsWith("Bearer ")) {
String token = authorizationHeader.substring(7);
String username = jwtService.extractUsername(token);
UserDetails userDetails = userService.loadUserByUsername(username);
if(!jwtService.isTokenValid(token, userDetails)) {
throw new AccessDeniedException("member");
}
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
accessor.setUser(usernamePasswordAuthenticationToken);
}
}
return message;
}
}
i have read about the preSend execution context, as it executed in a different thread, so the SecurityContextProvider resides in another thread in the controller and service layers.
My issue here that i also have a DatabaseAuditing class:
@Component
@EnableMongoAuditing
@RequiredArgsConstructor
public class DatabaseAuditing implements AuditorAware<String> {
private final UserRepository userRepository;
// Problem here with the websocket
// check the socket config. the Authentication is set there but not accessible here
@Override
public Optional<String> getCurrentAuditor() {
if(SecurityContextHolder.getContext().getAuthentication() != null) {
User user = userRepository.findByUsername(SecurityContextHolder.getContext().getAuthentication().getName()).orElseThrow(() -> new EntityNotFoundException());
return Optional.of(user.getId().toString());
}
return null;
}
}
here i also need the authenticated user in order to update the DB.
Is there a best practice how to provide the authentication accross the app.