将 JWT 令牌传递给 SockJS
Passing JWT token to SockJS
我需要在与 SockJS 握手时发送令牌。我已经尝试了许多建议的实现,但相同的异常被称为
java.lang.IllegalArgumentException: JWT String argument cannot be null or empty.
在back-endWebSocketConfig
@Configuration
@EnableWebSocketMessageBroker
@CrossOrigin
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/socket");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/websocket").withSockJS();
}
}
尝试与套接字建立连接的函数。普通 javascript.
function connect() {
var socket = new SockJS('http://localhost:8889/websocket',
null,
{
transports: ['xhr-streaming'],
headers: {'Authorization': 'Bearer eyJhbGciOiJIUzUxMiJ9...' }
});
stompClient = Stomp.over(socket);
stompClient.connect({},function (frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/socket/event', function (greeting) {
showGreeting(JSON.parse(greeting.body).content);
});
});
}
问题出在握手上,那些 headers 似乎没有正确传递令牌。我已经尝试了多种握手方式,但在我的情况下找不到正确的方式。
我从这里得到了实施思路,在我尝试在握手后使用 headers 之前,但我发现它立即需要令牌。
https://github.com/sockjs/sockjs-client/issues/196#issuecomment-61469141
编辑:添加 WebSecurityConfig
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.cors()
.configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues())
.and()
.csrf()
.disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers("/login/**").permitAll()
.antMatchers("/websocket/**").permitAll()
.anyRequest().authenticated();
// Custom JWT based security filter
JwtAuthorizationTokenFilter authenticationTokenFilter = new JwtAuthorizationTokenFilter(userDetailsService(), jwtTokenUtil, tokenHeader);
httpSecurity
.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
}//end configure(HttpSecurity)
创建身份验证令牌
@ApiOperation(value = "Login with the user credentials",
response = JwtAuthenticationResponse.class)
@ApiResponses(value = {
@ApiResponse(code = 401, message = "Unauthorized"),
@ApiResponse(code = 404, message = "Not Found",response = ExceptionResponse.class),
@ApiResponse(code = 400, message = "Bad Request",response = ExceptionResponse.class),
@ApiResponse(code = 200 , message = "OK", response = JwtAuthenticationResponse.class)
})
@RequestMapping(value = "${jwt.route.authentication.path}", method = RequestMethod.POST)
public ResponseEntity<?> createAuthenticationToken(
@ApiParam(value = "User's email and password", required = true)
@RequestBody JwtAuthenticationRequest authenticationRequest)
throws AuthenticationException {
ResponseEntity<?> response;
//authenticate the user
final User user = userService.getByEmail(authenticationRequest.getEmail());
try {
authenticate(user.getUsername(), authenticationRequest.getPassword(),user.getId(),user.getAuthority().getName());
// Reload password post-security so we can generate the token
final UserDetails userDetails = userDetailsService.loadUserByUsername(user.getUsername());
final String token = jwtTokenUtil.generateToken(userDetails);
// Return the token
response = ResponseEntity.ok(new JwtAuthenticationResponse(token,user.getUsername(),user.getFirstName(),user.getLastName(),
user.getEmail(),user.getId(),user.getAuthority().getName(),jwtTokenUtil.getExpirationTime(token)));
}catch(NullPointerException e) {
response = new ResponseEntity<>(new ExceptionResponse(404,"User Not Found","Authentication Failure"),HttpStatus.NOT_FOUND);
}catch(AuthenticationException e) {
response = new ResponseEntity<>(new ExceptionResponse(400,"Invalid E-mail or Password","Authentication Failure"),HttpStatus.BAD_REQUEST);
}//end try
return response;
}//end createAuthenticationToken(JwtAuthenticationRequest)
堆栈跟踪(当使用 back-end 从 websocket 发生握手和连接时,相同的异常已被捕获四次)。我将它添加到 pastebin 上,因为它会破坏 post。
异常
2019-05-16 11:36:17.936 WARN 11584 --- [nio-8889-exec-9] a.d.s.JwtAuthorizationTokenFilter : couldn't find bearer string, will ignore the header
2019-05-16 11:36:17.937 ERROR 11584 --- [nio-8889-exec-9] a.d.s.JwtAuthorizationTokenFilter : an error occured during getting username from token
java.lang.IllegalArgumentException: JWT String argument cannot be null or empty.
at io.jsonwebtoken.lang.Assert.hasText(Assert.java:135) ~[jjwt-0.9.0.jar:0.9.0]
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:479) ~[jjwt-0.9.0.jar:0.9.0]
at io.jsonwebtoken.impl.DefaultJwtParser.parseClaimsJws(DefaultJwtParser.java:541) ~[jjwt-0.9.0.jar:0.9.0]
at package.security.JwtTokenUtil.getAllClaimsFromToken(JwtTokenUtil.java:59) ~[classes/:na]
at package.security.JwtTokenUtil.getClaimFromToken(JwtTokenUtil.java:52) ~[classes/:na]
at package.security.JwtTokenUtil.getUsernameFromToken(JwtTokenUtil.java:34) ~[classes/:na]
at package.security.JwtAuthorizationTokenFilter.extractUsername(JwtAuthorizationTokenFilter.java:79) [classes/:na]
at package.security.JwtAuthorizationTokenFilter.doFilterInternal(JwtAuthorizationTokenFilter.java:44) [classes/:na]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
...
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346) [spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262) [spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.20.jar:8.5.20]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.20.jar:8.5.20]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) [spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
...
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799) [tomcat-embed-core-8.5.20.jar:8.5.20]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.20.jar:8.5.20]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) [tomcat-embed-core-8.5.20.jar:8.5.20]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1457) [tomcat-embed-core-8.5.20.jar:8.5.20]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.20.jar:8.5.20]
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) [na:1.8.0_201]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) [na:1.8.0_201]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.20.jar:8.5.20]
at java.lang.Thread.run(Unknown Source) [na:1.8.0_201]
Websocket 与 header 中的 HTTP 不遵循相同的模式。这就是为什么,即使您在 header 中发送令牌,它也找不到。我之前遇到过同样的问题,我更改了 websocket 安全结构。
我的示例代码是这样的:
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.setInterceptors(new ChannelInterceptorAdapter() {
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
MessageHeaders headers = message.getHeaders();
SimpMessageType type = (SimpMessageType) headers.get("simpMessageType");
List<String> tokenList = accessor.getNativeHeader("Authorization");
String token = null;
if(tokenList == null || tokenList.size() < 1) {
return message;
} else {
token = tokenList.get(0);
if(token == null) {
return message;
}
}
// validate and convert to a Principal based on your own requirements e.g.
// authenticationManager.authenticate(JwtAuthentication(token))
try{
JwtAuthenticationToken jwtAuthenticationToken = new JwtAuthenticationToken(new RawAccessJwtToken(tokenExtractor.extract(token)));
Authentication yourAuth = jwtAuthenticationProvider.authenticate(jwtAuthenticationToken);
accessor.setUser(yourAuth);
} catch (Exception e) {
throw new IllegalArgumentException(e.getMessage());
}
// not documented anywhere but necessary otherwise NPE in StompSubProtocolHandler!
accessor.setLeaveMutable(true);
return MessageBuilder.createMessage(message.getPayload(), accessor.getMessageHeaders());
}
});
}
server-side 配置以注册自定义身份验证拦截器。请注意,拦截器只需要在 CONNECT 消息上验证和设置用户 header。 Spring 注释并保存经过身份验证的用户,并将其与同一 session 上的后续 STOMP 消息相关联。以下示例显示如何注册自定义身份验证拦截器:
@Configuration
@EnableWebSocketMessageBroker
public class MyConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(new ChannelInterceptor() {
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor =
MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
Authentication user = ... ; // access authentication header(s)
accessor.setUser(user);
}
return message;
}
});
}
}
另外需要注意的是,当你使用SpringSecurity对消息的授权时,目前需要保证认证ChannelInterceptor配置排在SpringSecurity的前面。这最好通过在其自己的 WebSocketMessageBrokerConfigurer 实现中声明自定义拦截器来完成,该拦截器标有 @Order(Ordered.HIGHEST_PRECEDENCE + 99).
另一种方式:
同样,SockJS JavaScript 客户端不提供发送带有 SockJS 传输请求的 HTTP headers 的方法。如您所见,sockjs-client 问题 196。相反,它确实允许发送可用于发送令牌的查询参数,然后使用 Spring 您可以设置一些过滤器来识别 session使用提供的令牌。 ,但这有其自身的缺点(例如,令牌可能会无意中与服务器日志中的 URL 一起记录)。
我需要在与 SockJS 握手时发送令牌。我已经尝试了许多建议的实现,但相同的异常被称为
java.lang.IllegalArgumentException: JWT String argument cannot be null or empty.
在back-endWebSocketConfig
@Configuration
@EnableWebSocketMessageBroker
@CrossOrigin
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/socket");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/websocket").withSockJS();
}
}
尝试与套接字建立连接的函数。普通 javascript.
function connect() {
var socket = new SockJS('http://localhost:8889/websocket',
null,
{
transports: ['xhr-streaming'],
headers: {'Authorization': 'Bearer eyJhbGciOiJIUzUxMiJ9...' }
});
stompClient = Stomp.over(socket);
stompClient.connect({},function (frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/socket/event', function (greeting) {
showGreeting(JSON.parse(greeting.body).content);
});
});
}
问题出在握手上,那些 headers 似乎没有正确传递令牌。我已经尝试了多种握手方式,但在我的情况下找不到正确的方式。
我从这里得到了实施思路,在我尝试在握手后使用 headers 之前,但我发现它立即需要令牌。
https://github.com/sockjs/sockjs-client/issues/196#issuecomment-61469141
编辑:添加 WebSecurityConfig
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.cors()
.configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues())
.and()
.csrf()
.disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers("/login/**").permitAll()
.antMatchers("/websocket/**").permitAll()
.anyRequest().authenticated();
// Custom JWT based security filter
JwtAuthorizationTokenFilter authenticationTokenFilter = new JwtAuthorizationTokenFilter(userDetailsService(), jwtTokenUtil, tokenHeader);
httpSecurity
.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
}//end configure(HttpSecurity)
创建身份验证令牌
@ApiOperation(value = "Login with the user credentials",
response = JwtAuthenticationResponse.class)
@ApiResponses(value = {
@ApiResponse(code = 401, message = "Unauthorized"),
@ApiResponse(code = 404, message = "Not Found",response = ExceptionResponse.class),
@ApiResponse(code = 400, message = "Bad Request",response = ExceptionResponse.class),
@ApiResponse(code = 200 , message = "OK", response = JwtAuthenticationResponse.class)
})
@RequestMapping(value = "${jwt.route.authentication.path}", method = RequestMethod.POST)
public ResponseEntity<?> createAuthenticationToken(
@ApiParam(value = "User's email and password", required = true)
@RequestBody JwtAuthenticationRequest authenticationRequest)
throws AuthenticationException {
ResponseEntity<?> response;
//authenticate the user
final User user = userService.getByEmail(authenticationRequest.getEmail());
try {
authenticate(user.getUsername(), authenticationRequest.getPassword(),user.getId(),user.getAuthority().getName());
// Reload password post-security so we can generate the token
final UserDetails userDetails = userDetailsService.loadUserByUsername(user.getUsername());
final String token = jwtTokenUtil.generateToken(userDetails);
// Return the token
response = ResponseEntity.ok(new JwtAuthenticationResponse(token,user.getUsername(),user.getFirstName(),user.getLastName(),
user.getEmail(),user.getId(),user.getAuthority().getName(),jwtTokenUtil.getExpirationTime(token)));
}catch(NullPointerException e) {
response = new ResponseEntity<>(new ExceptionResponse(404,"User Not Found","Authentication Failure"),HttpStatus.NOT_FOUND);
}catch(AuthenticationException e) {
response = new ResponseEntity<>(new ExceptionResponse(400,"Invalid E-mail or Password","Authentication Failure"),HttpStatus.BAD_REQUEST);
}//end try
return response;
}//end createAuthenticationToken(JwtAuthenticationRequest)
堆栈跟踪(当使用 back-end 从 websocket 发生握手和连接时,相同的异常已被捕获四次)。我将它添加到 pastebin 上,因为它会破坏 post。
异常
2019-05-16 11:36:17.936 WARN 11584 --- [nio-8889-exec-9] a.d.s.JwtAuthorizationTokenFilter : couldn't find bearer string, will ignore the header
2019-05-16 11:36:17.937 ERROR 11584 --- [nio-8889-exec-9] a.d.s.JwtAuthorizationTokenFilter : an error occured during getting username from token
java.lang.IllegalArgumentException: JWT String argument cannot be null or empty.
at io.jsonwebtoken.lang.Assert.hasText(Assert.java:135) ~[jjwt-0.9.0.jar:0.9.0]
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:479) ~[jjwt-0.9.0.jar:0.9.0]
at io.jsonwebtoken.impl.DefaultJwtParser.parseClaimsJws(DefaultJwtParser.java:541) ~[jjwt-0.9.0.jar:0.9.0]
at package.security.JwtTokenUtil.getAllClaimsFromToken(JwtTokenUtil.java:59) ~[classes/:na]
at package.security.JwtTokenUtil.getClaimFromToken(JwtTokenUtil.java:52) ~[classes/:na]
at package.security.JwtTokenUtil.getUsernameFromToken(JwtTokenUtil.java:34) ~[classes/:na]
at package.security.JwtAuthorizationTokenFilter.extractUsername(JwtAuthorizationTokenFilter.java:79) [classes/:na]
at package.security.JwtAuthorizationTokenFilter.doFilterInternal(JwtAuthorizationTokenFilter.java:44) [classes/:na]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
...
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346) [spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262) [spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.20.jar:8.5.20]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.20.jar:8.5.20]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) [spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
...
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799) [tomcat-embed-core-8.5.20.jar:8.5.20]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.20.jar:8.5.20]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) [tomcat-embed-core-8.5.20.jar:8.5.20]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1457) [tomcat-embed-core-8.5.20.jar:8.5.20]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.20.jar:8.5.20]
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) [na:1.8.0_201]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) [na:1.8.0_201]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.20.jar:8.5.20]
at java.lang.Thread.run(Unknown Source) [na:1.8.0_201]
Websocket 与 header 中的 HTTP 不遵循相同的模式。这就是为什么,即使您在 header 中发送令牌,它也找不到。我之前遇到过同样的问题,我更改了 websocket 安全结构。
我的示例代码是这样的:
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.setInterceptors(new ChannelInterceptorAdapter() {
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
MessageHeaders headers = message.getHeaders();
SimpMessageType type = (SimpMessageType) headers.get("simpMessageType");
List<String> tokenList = accessor.getNativeHeader("Authorization");
String token = null;
if(tokenList == null || tokenList.size() < 1) {
return message;
} else {
token = tokenList.get(0);
if(token == null) {
return message;
}
}
// validate and convert to a Principal based on your own requirements e.g.
// authenticationManager.authenticate(JwtAuthentication(token))
try{
JwtAuthenticationToken jwtAuthenticationToken = new JwtAuthenticationToken(new RawAccessJwtToken(tokenExtractor.extract(token)));
Authentication yourAuth = jwtAuthenticationProvider.authenticate(jwtAuthenticationToken);
accessor.setUser(yourAuth);
} catch (Exception e) {
throw new IllegalArgumentException(e.getMessage());
}
// not documented anywhere but necessary otherwise NPE in StompSubProtocolHandler!
accessor.setLeaveMutable(true);
return MessageBuilder.createMessage(message.getPayload(), accessor.getMessageHeaders());
}
});
}
server-side 配置以注册自定义身份验证拦截器。请注意,拦截器只需要在 CONNECT 消息上验证和设置用户 header。 Spring 注释并保存经过身份验证的用户,并将其与同一 session 上的后续 STOMP 消息相关联。以下示例显示如何注册自定义身份验证拦截器:
@Configuration
@EnableWebSocketMessageBroker
public class MyConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(new ChannelInterceptor() {
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor =
MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
Authentication user = ... ; // access authentication header(s)
accessor.setUser(user);
}
return message;
}
});
}
}
另外需要注意的是,当你使用SpringSecurity对消息的授权时,目前需要保证认证ChannelInterceptor配置排在SpringSecurity的前面。这最好通过在其自己的 WebSocketMessageBrokerConfigurer 实现中声明自定义拦截器来完成,该拦截器标有 @Order(Ordered.HIGHEST_PRECEDENCE + 99).
另一种方式: 同样,SockJS JavaScript 客户端不提供发送带有 SockJS 传输请求的 HTTP headers 的方法。如您所见,sockjs-client 问题 196。相反,它确实允许发送可用于发送令牌的查询参数,然后使用 Spring 您可以设置一些过滤器来识别 session使用提供的令牌。 ,但这有其自身的缺点(例如,令牌可能会无意中与服务器日志中的 URL 一起记录)。