You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用ChannelInterceptor做JWT认证时,如何在SessionConnectedEvent中获取已认证用户的Principal

使用ChannelInterceptor做JWT认证时,如何在SessionConnectedEvent中获取已认证用户的Principal

我来帮你解决这个问题,核心原因是你之前的ChannelInterceptor里虽然完成了JWT认证,但没有把认证后的用户信息绑定到WebSocket会话中,导致SessionConnectedEvent监听器里拿不到Principal。下面是具体的解决步骤:

第一步:修正ChannelInterceptor的认证逻辑,绑定用户信息到WebSocket会话

在你的handleConnect方法中,认证成功后需要把Authentication对象绑定到StompHeaderAccessor,并且重新构建消息(因为原消息的Header是不可变的)。修改后的代码如下:

private Message<?> handleConnect(Message<?> message, StompHeaderAccessor accessor) {
    String authorizationHeader = accessor.getFirstNativeHeader(HttpHeaders.AUTHORIZATION);
    if (authorizationHeader == null || !authorizationHeader.startsWith(JwtService.BEARER_PREFIX)) {
        throw new MessageHandlingException(message, "Missing or invalid authorization header");
    }
    String token = authorizationHeader.substring(JwtService.BEARER_PREFIX.length()).trim();
    try {
        var jwtAuthToken = new JwtAuthenticationToken(token);
        // 执行认证,获取已认证的Authentication对象
        Authentication authenticatedAuth = authManager.authenticate(jwtAuthToken);
        
        // 关键:将认证后的用户对象绑定到StompHeaderAccessor
        accessor.setUser(authenticatedAuth);
        // 必须设置为可变,否则Header的修改不会生效
        accessor.setLeaveMutable(true);
        
        // 重新构建Message,使用修改后的Header信息
        return MessageBuilder.createMessage(message.getPayload(), accessor.getMessageHeaders());
    } catch (BadCredentialsException e) {
        throw new MessageHandlingException(message, e.getMessage());
    } catch (RuntimeException e) {
        throw new MessageHandlingException(message, "Internal server error");
    }
}

关键说明:

  • accessor.setUser(authenticatedAuth):把认证成功后的Authentication对象绑定到Header Accessor,这样Spring会自动将其关联到对应的WebSocket会话。
  • accessor.setLeaveMutable(true):默认情况下StompHeaderAccessor是不可变的,必须开启可变才能修改Header信息。
  • 重新构建Message:原消息的Header是不可变的,所以需要用修改后的Accessor重新创建Message,确保修改的Header能被Spring WebSocket识别。

第二步:在SessionConnectedEvent监听器中获取Principal

完成上面的修改后,你就可以在SessionConnectedEvent的监听器中通过WebSocket会话直接获取Principal了:

@Slf4j
@Component
@RequiredArgsConstructor
public class WebSocketEventListener {

    @EventListener
    public void sessionConnectedEvent(SessionConnectedEvent event) {
        // 获取WebSocket会话对象
        WebSocketSession webSocketSession = event.getWebSocketSession();
        // 从会话中获取已认证的Principal
        Principal principal = webSocketSession.getPrincipal();
        
        if (principal != null) {
            log.info("STOMP会话已建立,当前用户:{}", principal.getName());
            
            // 如果需要获取JWT的详细信息,可以将Principal强转为你的JwtAuthenticationToken
            if (principal instanceof JwtAuthenticationToken jwtAuthToken) {
                log.info("当前用户权限:{}", jwtAuthToken.getAuthorities());
                // 还可以获取JWT的Claims等信息
                // var claims = jwtAuthToken.getToken().getClaims();
            }
        } else {
            log.warn("SessionConnectedEvent中未获取到已认证用户");
        }
    }
}

为什么这样能生效?

SessionConnectedEvent是在STOMP会话完全建立后触发的,此时WebSocket会话已经和之前认证绑定的Authentication对象关联起来了,所以通过webSocketSession.getPrincipal()就能直接拿到已认证的用户信息。

补充说明

你之前尝试的方法没用,核心就是缺失了将认证对象绑定到StompHeaderAccessor并重新构建消息这一步——Spring不会自动把authManager.authenticate()的结果关联到WebSocket会话,必须手动完成绑定。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 11:53:00