标题翻译
How to authenticate SockJS CONNECT with JWT in Spring
问题
我正在尝试使用SockJS连接到一个主题,但是我收到的响应是401未经授权。该项目使用带有JWT身份验证的Spring安全性。在互联网上搜索后,我发现我应该在拦截器中对用户进行身份验证。问题是我的拦截器未被触发。
在没有Spring安全性的POC(概念验证)应用程序中,一切都正常运行。
我希望我已经适当地描述了这个问题,提前感谢。
用于对用户进行身份验证的拦截器:
@Component
@RequiredArgsConstructor
public class SocketAuthenticationInterceptor implements ChannelInterceptor {
private final JwtTokenProvider tokenProvider;
private final UserDetailsServiceImpl userDetailsService;
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor =
MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
List<String> authorization = accessor.getNativeHeader("Authorization");
String jwt = authorization.get(0);
final Integer userId = tokenProvider.getUserIdFromJWT(jwt);
final UserDetails userDetails = userDetailsService.loadUserById(userId);
final UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
accessor.setUser(authentication);
}
return message;
}
}
注册拦截器:
@Configuration
@RequiredArgsConstructor
@EnableWebSocketMessageBroker
@Order(Ordered.HIGHEST_PRECEDENCE + 99)
public class WebSocketAuthenticationConfig implements WebSocketMessageBrokerConfigurer {
private final SocketAuthenticationInterceptor socketAuthenticationInterceptor;
@Override
public void configureClientInboundChannel(final ChannelRegistration registration) {
registration.interceptors(socketAuthenticationInterceptor);
}
}
英文翻译
I am trying to connect to a topic with SockJS, but the response I get is 401 Unauthorized. The project contains Spring security with JWT Authentication. Searching through internet I found that I should authenticate the user in a interceptor. The problem is that my interceptor is not reached.
Everything works fine in a POC (proof of concept) application without Spring security.
I hope I described the problem properly, thanks in advance.
The interceptor that authenticates the user:
@Component
@RequiredArgsConstructor
public class SocketAuthenticationInterceptor implements ChannelInterceptor {
private final JwtTokenProvider tokenProvider;
private final UserDetailsServiceImpl userDetailsService;
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor =
MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
List<String> authorization = accessor.getNativeHeader("Authorization");
String jwt = authorization.get(0);
final Integer userId = tokenProvider.getUserIdFromJWT(jwt);
final UserDetails userDetails = userDetailsService.loadUserById(userId);
final UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
accessor.setUser(authentication);
}
return message;
}
}
Registering the interceptor:
@Configuration
@RequiredArgsConstructor
@EnableWebSocketMessageBroker
@Order(Ordered.HIGHEST_PRECEDENCE + 99)
public class WebSocketAuthenticationConfig implements WebSocketMessageBrokerConfigurer {
private final SocketAuthenticationInterceptor socketAuthenticationInterceptor;
@Override
public void configureClientInboundChannel(final ChannelRegistration registration) {
registration.interceptors(socketAuthenticationInterceptor);
}
}
答案1
得分: 0
使用Springboot中的过滤器(filters)来对网页请求进行用户身份验证。默认情况下,有一些过滤器尝试对任何请求进行身份验证。需要注意的是,这种身份验证是在握手过程中和发送初始的CONNECT
消息之前完成的。CustomInterceptor
被用于处理我们接收到的消息,但不是用于身份认证(AuthN),因为过滤器本身正在对请求进行身份认证,所以消息甚至没有被接收到,因此执行不会达到拦截器。
因此,您可以像以下这样编写自己的自定义过滤器类:
class JwtRequestFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
String requestTokenHeader = request.getHeader("Authorization");
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
jwtToken = requestTokenHeader.substring(7);
}
final Integer userId = tokenProvider.getUserIdFromJWT(jwtToken);
final UserDetails userDetails = userDetailsService.loadUserById(userId);
final UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
//如果您想设置来源详细信息
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
然后,将此自定义过滤器添加到过滤器列表中,像这样在WebSecurityConfig.java
中:
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
// ...
// 在每个请求中添加一个过滤器来验证令牌
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); // 或者您选择的任何其他过滤器
}
使用以上方法,您的身份验证上下文将被设置。有用的链接:
英文翻译
Authenticating the user for a web request is done using filters in Springboot. By default there are a bunch of filters which try to authenticate any request. Note that this authentication is done during the handshake and before sending the initial CONNECT
message. A CustomInterceptor
is used for handling the messages we receive not for AuthN, since the filter itself is unauthn'ing the request the message is not even being received hence the execution is not reaching the interceptor.
So instead write your own custom filter class like so
class JwtRequestFilter extends OncePerRequestFilter {
...
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
String requestTokenHeader = request.getHeader("Authorization");
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
jwtToken = requestTokenHeader.substring(7);
}
final Integer userId = tokenProvider.getUserIdFromJWT(jwtToken);
final UserDetails userDetails = userDetailsService.loadUserById(userId);
final UsernamePasswordAuthenticationToken authentication =new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
//If you want to set source details
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
And then add this custom filter to the list of filters like so in WebSecurityConfig.java
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
...
// Add a filter to validate the tokens with every request
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); // Or any other filter of your choice
}
So with this your authentication context is set.
Useful Links:
专注分享java语言的经验与见解,让所有开发者获益!
评论