如何为Spring 5 WebSocket添加安全防护
Great to hear your WebSocket app is running smoothly! Let's walk through how to add robust security to your setup, tailored to your existing GreetingController.
1. Add Spring Security Dependencies
First, make sure you've got the necessary dependencies in your build file to enable Spring Security for WebSockets:
For Maven (pom.xml):
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-messaging</artifactId> </dependency>
For Gradle (build.gradle):
implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.security:spring-security-messaging'
2. Configure WebSocket-Specific Security Rules
Create a configuration class to control access to your STOMP endpoints and message destinations:
import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.messaging.MessageSecurityMetadataSourceRegistry; import org.springframework.security.config.annotation.web.socket.AbstractSecurityWebSocketMessageBrokerConfigurer; @Configuration public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer { @Override protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) { messages // Require authentication to send messages to /hello .simpDestMatchers("/hello").authenticated() // Restrict subscription to /topic/greetings to users with USER role .simpSubscribeDestMatchers("/topic/greetings").hasRole("USER") // All other STOMP traffic requires authentication .anyMessage().authenticated(); } @Override protected boolean sameOriginDisabled() { // Disable same-origin check if you need to handle cross-origin requests return true; } }
3. Set Up Core Authentication
Add a basic Spring Security configuration to handle user authentication (this example uses an in-memory user store—replace it with your actual user service later):
import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .passwordEncoder(new BCryptPasswordEncoder()) .withUser("user") .password(new BCryptPasswordEncoder().encode("password")) .roles("USER"); } @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .and() .formLogin() // Enable form-based login for testing .and() .csrf().ignoringAntMatchers("/ws/**"); // Skip CSRF checks for WebSocket endpoints if needed } }
4. Add Method-Level Security to Your Controller
Enforce security directly on your controller methods using Spring Security annotations. First, enable method-level security:
import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration; @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true) public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration { }
Then update your GreetingController to restrict access to the greeting method:
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.SendTo; import org.springframework.web.util.HtmlUtils; @Controller public class GreetingController { @MessageMapping("/hello") @SendTo("/topic/greetings") @PreAuthorize("hasRole('USER')") // Only authenticated users with USER role can call this public Greeting greeting(HelloMessage message) throws Exception { Thread.sleep(1000); // simulated delay return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!"); } }
5. Access Authenticated User Details (Optional)
You can retrieve the authenticated user's details in your controller if needed:
import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; // Inside your greeting method Authentication auth = SecurityContextHolder.getContext().getAuthentication(); String currentUsername = auth.getName(); // Get the logged-in user's username
Key Notes
- CSRF Protection: For browser clients, ensure you include the CSRF token in the WebSocket handshake request. Spring Security can auto-inject this into frontend templates.
- Cross-Origin Requests: If your app handles cross-origin traffic, configure CORS in your WebSocket config and update security rules to allow trusted origins.
- Production-Grade Authentication: Replace the in-memory user store with your actual user service (e.g., JPA, LDAP) or use OAuth2/JWT for token-based authentication.
Test your setup by logging in with the credentials you defined—unauthenticated users should be blocked from sending messages to /hello or subscribing to /topic/greetings.
内容的提问来源于stack exchange,提问作者gstackoverflow

