Я использую Spring Boot 2 WebSocket + SockJS + STOMP.
Я настроил JWT авторизацию когда клиент подключается по вебсоку в классе типа ChannelInterceptor, в методе preSend
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
@Component
public class WebSocketChannelInterceptor implements ChannelInterceptor {
private final TokenUtils tokenUtils;
private final AuthenticationManager websocketAuthenticationManager;
private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketChannelInterceptor.class);
public WebSocketChannelInterceptor(TokenUtils tokenUtils, AuthenticationManager websocketAuthenticationManager) {
this.tokenUtils = tokenUtils;
this.websocketAuthenticationManager = websocketAuthenticationManager;
}
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
LOGGER.info("WEBSOCKETCHANNELINTERCEPTOR -> "+message.toString());
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if (accessor != null && StompCommand.CONNECT.equals(accessor.getCommand())) {
List<String> headers = accessor.getNativeHeader(AUTHORIZATION);
accessor.setUser(websocketAuthenticationManager.authenticate(new JWTTokenAuthentication(tokenUtils.resolveToken(headers != null ? headers.get(0) : null))));
}
return message;
}
@Override
public boolean preReceive(MessageChannel channel) {
LOGGER.info("preReceive");
return true;
}
}
AuthenticationManager:
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
@Component("websocketAuthenticationManager")
@AllArgsConstructor
public class WebsocketAuthenticationManager implements AuthenticationManager {
private final TokenUtils tokenUtils;
private final ClientRepository clientRepository;
private final OperatorRepository operatorRepository;
@Override
@Transactional(readOnly = true)
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
JWTTokenAuthentication jwtTokenAuthentication = (JWTTokenAuthentication) authentication;
String token = jwtTokenAuthentication.getToken();
if(tokenUtils.validateToken(token)) {
RoleType role = RoleType.get(tokenUtils.get("role", token));
if(role == null) {
throw throwBadCredentialsException("Unknown role");
}
switch (role) {
case OPERATOR:
Optional<OperatorEntity> operator = operatorRepository.findByLogin(tokenUtils.get("login", token));
if(operator.isPresent()) {
return new WebsocketOperatorDetails(token, OperatorDetails.builder().role(role.toString()).operator(operator.get()).build());
} else {
throw throwBadCredentialsException("Operator not found");
}
case CLIENT:
Optional<ClientEntity> client = clientRepository.findByHash(tokenUtils.get("hash", token));
if(client.isPresent()) {
return new WebsocketClientDetails(token, ClientDetails.builder().role(role.toString()).client(client.get()).build());
} else {
throw throwBadCredentialsException("Client not found");
}
case OWNER:
throw throwBadCredentialsException("Access denied");
default:
throw throwBadCredentialsException("Unknown role");
}
} else {
throw throwBadCredentialsException("Invalid token");
}
}
private MessagingException throwBadCredentialsException(String message) {
Map<String, Object> headers = new HashMap<>();
headers.put("status", StatusType.UNAUTHORIZED);
headers.put("message", message);
return new MessagingException(new MutableMessage<>(new MessageHeaders(headers)));
}
}
В AuthenticationManager Я бросаю ошибку, если что-то не так.
На стороне клиента я получаю это:
{
"command":"ERROR",
"headers":{
"content-length":"0",
"message":"Failed to send message to ExecutorSubscribableChannel[clientInboundChannel]; nested exception is org.springframework.security.authentication.AuthenticationCredentialsNotFoundException\\c Invalid token"
},
"_binaryBody":{
},
"isBinaryBody":true,
"escapeHeaderValues":false,
"skipContentLengthHeader":false,
"_body":""
}
Что мне сделать что бы сообщение об ошибке отправлялось с payload-ом? Я хочу отправить своё кастомное сообщение.
Спасибо.