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

Spring Boot集成Keycloak实现STOMP WebSocket认证遇403问题求助

我之前也碰到过类似的Spring Boot + Keycloak + WebSocket认证的问题,结合你的场景,给你梳理一套可行的解决方案:

核心思路

你已经成功从STOMP CONNECT帧中拿到了Bearer Token,接下来需要利用Keycloak的认证逻辑把Token转换成Spring Security的Authentication对象,然后绑定到STOMP会话中,这样后续的WebSocket请求就能被Spring Security识别为已认证用户。

步骤1:实现WebSocket认证拦截器

创建一个ChannelInterceptor,专门处理STOMP连接时的Token验证和Authentication对象生成:

@Component
public class WebSocketAuthInterceptor implements ChannelInterceptor {

    private final KeycloakAuthenticationProvider keycloakAuthenticationProvider;
    private final KeycloakDeployment keycloakDeployment;

    // 通过构造注入Keycloak相关依赖
    public WebSocketAuthInterceptor(KeycloakAuthenticationProvider keycloakAuthenticationProvider,
                                    KeycloakDeployment keycloakDeployment) {
        this.keycloakAuthenticationProvider = keycloakAuthenticationProvider;
        this.keycloakDeployment = keycloakDeployment;
    }

    @Override
    public Message<?> preSend(Message<?> message, MessageChannel channel) {
        StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);

        // 只处理CONNECT命令
        if (StompCommand.CONNECT.equals(accessor.getCommand())) {
            List<String> authHeaders = accessor.getNativeHeader("Authorization");
            if (authHeaders != null && !authHeaders.isEmpty()) {
                // 提取Bearer Token(去掉前缀)
                String token = authHeaders.get(0).replace("Bearer ", "");
                
                try {
                    // 用Keycloak工具验证Token有效性
                    Jwt jwt = JwtDecoder.withPublicKey(keycloakDeployment.getRealmKey()).decode(token);
                    OAuth2TokenValidator<Jwt> validator = JwtValidators.createDefaultWithIssuer(keycloakDeployment.getRealmInfoUrl());
                    validator.validate(jwt);

                    // 构建Keycloak认证主体和权限
                    KeycloakPrincipal<KeycloakSecurityContext> principal = new KeycloakPrincipal<>(
                        new KeycloakSecurityContext(keycloakDeployment, token, jwt, null)
                    );
                    Collection<GrantedAuthority> authorities = keycloakAuthenticationProvider.getGrantedAuthorities(principal, null);
                    
                    // 创建Authentication对象并绑定到STOMP会话
                    Authentication authentication = new KeycloakAuthenticationToken(principal, false, authorities);
                    accessor.setUser(authentication);
                } catch (Exception e) {
                    // Token验证失败时抛出异常,拒绝连接
                    throw new AuthenticationCredentialsNotFoundException("无效或过期的Token", e);
                }
            }
        }
        return message;
    }
}

步骤2:注册拦截器到WebSocket配置

需要把上面的拦截器添加到WebSocket的入站通道中,确保每个STOMP请求都经过认证处理:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    private final WebSocketAuthInterceptor webSocketAuthInterceptor;

    public WebSocketConfig(WebSocketAuthInterceptor webSocketAuthInterceptor) {
        this.webSocketAuthInterceptor = webSocketAuthInterceptor;
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic");
        config.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws").setAllowedOrigins("*").withSockJS();
    }

    // 注册认证拦截器
    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
        registration.interceptors(webSocketAuthInterceptor);
    }
}

步骤3:补充Keycloak Deployment Bean

为了能正确获取Keycloak的部署配置,需要添加一个Bean来注入KeycloakDeployment:

@Bean
public KeycloakDeployment keycloakDeployment(KeycloakDeploymentContext deploymentContext) {
    return deploymentContext.resolveDeployment(null);
}

关键注意事项

  • 你的WebSocketSecurityConfig配置是正确的,只要Authentication对象绑定成功,Spring Security就会识别已认证用户,不再返回403。
  • 客户端必须在STOMP CONNECT请求的Header中携带Authorization: Bearer <你的Token>,这部分你已经能正确获取,没问题。
  • 因为你的Web安全配置设置了SessionCreationPolicy.STATELESS,和WebSocket的无状态Token认证完全兼容,不需要额外调整。

内容的提问来源于stack exchange,提问作者adrianmoya

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:43:48