Spring WebSocket AccessDeniedException问题排查与配置优化
Hey there! Let's work through your WebSocket security issue step by step. First, let's recap your requirements to make sure we're aligned:
- Anonymous access allowed: Anyone should be able to subscribe to
/voting-socket/topic/proc/**without authentication - Restricted send access: Only logged-in users with the
ADMINrole can send messages to/voting-socket/app/proc/**
Why Removing Those First Two Lines Worked (And If It's Correct)
Chances are, those first two lines in your WebSocketSecurityConfig were related to HTTP security (like @EnableWebSecurity or extending WebSecurityConfigurerAdapter), which don't belong in a WebSocket-specific security config. Mixing HTTP and WebSocket security configurations causes Spring Security to apply conflicting rules—leading to that AccessDeniedException.
Removing those lines is absolutely the right first move! But we need to make sure your WebSocketSecurityConfig is properly set up for WebSocket-specific authorization to avoid future issues.
Correct WebSocket Security Configuration
Here's how to structure your WebSocketSecurityConfig to meet your exact requirements:
@Configuration @EnableWebSocketSecurity public class WebSocketSecurityConfig implements SecurityWebSocketMessageBrokerConfigurer { @Override public void configureInbound(MessageSecurityMetadataSourceRegistry messages) { messages // Allow anonymous users to subscribe to the topic endpoints .simpSubscribeDestMatchers("/voting-socket/topic/proc/**").permitAll() // Restrict send operations to authenticated ADMIN users only .simpDestMatchers("/voting-socket/app/proc/**").hasRole("ADMIN") // Require authentication for all other WebSocket messages .anyMessage().authenticated(); } // Optional: Disable CSRF if your frontend doesn't handle it (adjust for production!) @Override public void configureWebSocketTransport(WebSocketTransportSecurityRegistry registry) { registry.disableCsrf(); // Note: In production, it's better to have your frontend pass the CSRF token // via the `X-XSRF-TOKEN` header when establishing the WebSocket connection } }
Critical Complementary Configurations
Don't forget these two key pieces to make everything work smoothly:
WebSocket Message Broker Setup
Ensure yourWebSocketConfigcorrectly maps your destination prefixes:@Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/voting-socket/topic"); config.setApplicationDestinationPrefixes("/voting-socket/app"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/voting-socket") .withSockJS(); // Remove if you're not using SockJS } }HTTP Security for WebSocket Handshake
WebSocket connections start with an HTTP handshake—so yourWebSecurityConfigneeds to allow access to the WebSocket endpoint:@Configuration @EnableWebSecurity public class WebSecurityConfig implements WebSecurityConfigurer { @Override public void configure(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .requestMatchers("/voting-socket/**").permitAll() // Allow handshake requests .anyRequest().authenticated() ) // Add your preferred authentication method (form login, OAuth2, etc.) .formLogin(form -> form.permitAll()); } }
Final Checks
- Verify that user roles are correctly assigned (make sure ADMIN users have the
ROLE_ADMINauthority—Spring Security automatically prefixes roles withROLE_when usinghasRole()). - If you're still seeing issues, enable debug logging for Spring Security to trace exactly where the authorization is failing:
logging.level.org.springframework.security=DEBUG
内容的提问来源于stack exchange,提问作者peter Schiza

