Spring Boot 3纯WebSocket的OAuth安全验证实现方案问询
纯WebSocket(非STOMP)的OAuth2安全实现方案
核心思路:在握手阶段完成OAuth2令牌验证
WebSocket连接建立依赖HTTP握手升级流程,所以OAuth2验证必须在握手发起时完成——只有令牌合法的请求,才能升级为WebSocket连接。@EnableWebSocketSecurity确实只适配STOMP+Broker的消息场景,纯WebSocket需要自定义握手拦截逻辑来实现安全校验。
具体实现步骤
1. 自定义WebSocket握手拦截器
实现HandshakeInterceptor接口,在beforeHandshake方法中提取请求里的OAuth2令牌,调用授权服务器的令牌 introspect 接口验证有效性,验证通过才允许握手继续。
示例代码:
@Component public class OAuth2WebSocketHandshakeInterceptor implements HandshakeInterceptor { private final OAuth2TokenIntrospector tokenIntrospector; public OAuth2WebSocketHandshakeInterceptor(OAuth2TokenIntrospector tokenIntrospector) { this.tokenIntrospector = tokenIntrospector; } @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { // 从请求头提取Bearer令牌 String token = extractBearerToken(request.getHeaders()); if (token == null) { response.setStatusCode(HttpStatus.UNAUTHORIZED); return false; } try { // 调用授权服务器验证令牌 OAuth2AuthenticatedPrincipal principal = tokenIntrospector.introspect(token); // 将用户信息存入会话属性,后续处理器可直接使用 attributes.put("authenticatedUser", principal); return true; } catch (OAuth2AuthenticationException e) { response.setStatusCode(HttpStatus.UNAUTHORIZED); return false; } } @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) { // 握手完成后的收尾操作(可选) } private String extractBearerToken(HttpHeaders headers) { String authHeader = headers.getFirst(HttpHeaders.AUTHORIZATION); if (authHeader != null && authHeader.startsWith("Bearer ")) { return authHeader.substring(7); } return null; } }
2. 绑定拦截器到WebSocket配置
在@EnableWebSocket的配置类中,注册WebSocket处理器时添加上述拦截器:
@Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { private final OAuth2WebSocketHandshakeInterceptor handshakeInterceptor; private final CustomWebSocketHandler webSocketHandler; public WebSocketConfig(OAuth2WebSocketHandshakeInterceptor handshakeInterceptor, CustomWebSocketHandler webSocketHandler) { this.handshakeInterceptor = handshakeInterceptor; this.webSocketHandler = webSocketHandler; } @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(webSocketHandler, "/ws/connect") .addInterceptors(handshakeInterceptor) // 按需配置跨域规则 .setAllowedOrigins("https://your-frontend-domain.com"); } }
3. 配置OAuth2令牌校验器
使用Spring提供的NimbusOAuth2TokenIntrospector对接授权服务器的令牌 introspect 端点:
@Configuration public class OAuth2ResourceServerConfig { @Value("${spring.security.oauth2.resourceserver.introspection-uri}") private String introspectUri; @Value("${spring.security.oauth2.resourceserver.client-id}") private String clientId; @Value("${spring.security.oauth2.resourceserver.client-secret}") private String clientSecret; @Bean public OAuth2TokenIntrospector tokenIntrospector() { NimbusOAuth2TokenIntrospector introspector = new NimbusOAuth2TokenIntrospector(introspectUri); introspector.setClientCredentials(clientId, clientSecret); return introspector; } }
4. 在WebSocket处理器中获取用户信息
握手通过后,用户认证信息会存在WebSocket会话的属性中,处理器可直接取用:
@Component public class CustomWebSocketHandler extends TextWebSocketHandler { @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { OAuth2AuthenticatedPrincipal user = (OAuth2AuthenticatedPrincipal) session.getAttributes().get("authenticatedUser"); String username = user.getName(); // 业务逻辑处理 session.sendMessage(new TextMessage("已收到消息,当前用户:" + username)); } }
关键问题解答
- 是否需要实现拦截器? 是的,这是纯WebSocket场景下OAuth2验证的唯一可行方式——通过拦截握手请求完成令牌校验,阻止非法连接建立。
- 是否要在握手阶段执行验证? 必须在握手阶段做。因为WebSocket连接建立后,后续消息传输脱离HTTP上下文,无法再通过Spring Security过滤器链拦截,所以身份验证只能在握手升级前完成。
- @EnableWebSocketSecurity为什么不适用? 该注解是专为STOMP+Broker模式设计的,它依赖消息代理的消息拦截机制,纯WebSocket没有Broker支撑,因此无法生效。
内容的提问来源于stack exchange,提问作者HereAndBeyond
相关产品推荐
相关产品推荐

