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
相关产品推荐
相关产品推荐

