Spring Security 6.x 功能概览与代码示例
Spring Security 6.x 功能概览与代码示例
Spring Security 6.x 是一个功能强大的安全框架,提供了全面的安全解决方案。以下是其主要功能及相应的代码示例:
1. 认证功能
1.1 基于表单的认证
@Configuration
@EnableWebSecurity
public class SecurityConfig {@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(authz -> authz.requestMatchers("/public/**").permitAll().anyRequest().authenticated()).formLogin(form -> form.loginPage("/login").defaultSuccessUrl("/home").permitAll()).logout(logout -> logout.logoutSuccessUrl("/login?logout").permitAll());return http.build();}@Beanpublic UserDetailsService userDetailsService() {UserDetails user = User.withUsername("user").password("{noop}password").roles("USER").build();return new InMemoryUserDetailsManager(user);}
}
1.2 HTTP Basic 认证
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(authz -> authz.anyRequest().authenticated()).httpBasic(Customizer.withDefaults());return http.build();
}
1.3 JWT 认证
@Component
public class JwtUtil {private final String secret = "your-secret-key";public String generateToken(UserDetails userDetails) {Map<String, Object> claims = new HashMap<>();return Jwts.builder().setClaims(claims).setSubject(userDetails.getUsername()).setIssuedAt(new Date(System.currentTimeMillis())).setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)).signWith(SignatureAlgorithm.HS256, secret).compact();}public Boolean validateToken(String token, UserDetails userDetails) {final String username = extractUsername(token);return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));}// 其他JWT相关方法...
}@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {@Autowiredprivate JwtUtil jwtUtil;@Autowiredprivate UserDetailsService userDetailsService;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {final String authorizationHeader = request.getHeader("Authorization");String username = null;String jwt = null;if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {jwt = authorizationHeader.substring(7);username = jwtUtil.extractUsername(jwt);}if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);if (jwtUtil.validateToken(jwt, userDetails)) {UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));SecurityContextHolder.getContext().setAuthentication(authToken);}}filterChain.doFilter(request, response);}
}
2. 授权功能
2.1 基于角色的访问控制
@Configuration
@EnableMethodSecurity
public class SecurityConfig {@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(authz -> authz.requestMatchers("/admin/**").hasRole("ADMIN").requestMatchers("/user/**").hasAnyRole("ADMIN", "USER").requestMatchers("/public/**").permitAll().anyRequest().authenticated());return http.build();}
}@RestController
@RequestMapping("/api/admin")
public class AdminController {@GetMapping("/dashboard")@PreAuthorize("hasRole('ADMIN')")public String adminDashboard() {return "Admin Dashboard";}
}
2.2 基于权限的访问控制
@RestController
@RequestMapping("/api/products")
public class ProductController {@GetMapping@PreAuthorize("hasAuthority('PRODUCT_READ')")public List<Product> getAllProducts() {// 获取所有产品}@PostMapping@PreAuthorize("hasAuthority('PRODUCT_WRITE')")public Product createProduct(@RequestBody Product product) {// 创建新产品}@DeleteMapping("/{id}")@PreAuthorize("hasAuthority('PRODUCT_DELETE')")public void deleteProduct(@PathVariable Long id) {// 删除产品}
}
3. OAuth2 集成
3.1 OAuth2 客户端配置
@Configuration
public class OAuth2Config {@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(authz -> authz.anyRequest().authenticated()).oauth2Login(oauth2 -> oauth2.loginPage("/oauth2/authorization/my-client").defaultSuccessUrl("/home")).oauth2Client(Customizer.withDefaults());return http.build();}@Beanpublic ClientRegistrationRepository clientRegistrationRepository() {return new InMemoryClientRegistrationRepository(this.googleClientRegistration());}private ClientRegistration googleClientRegistration() {return ClientRegistration.withRegistrationId("google").clientId("google-client-id").clientSecret("google-client-secret").clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC).authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE).redirectUri("{baseUrl}/login/oauth2/code/{registrationId}").scope("openid", "profile", "email").authorizationUri("https://accounts.google.com/o/oauth2/v2/auth").tokenUri("https://www.googleapis.com/oauth2/v4/token").userInfoUri("https://www.googleapis.com/oauth2/v3/userinfo").userNameAttributeName(IdTokenClaimNames.SUB).clientName("Google").build();}
}
3.2 OAuth2 资源服务器配置
@Configuration
@EnableWebSecurity
public class ResourceServerConfig {@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(authz -> authz.anyRequest().authenticated()).oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));return http.build();}@Beanpublic JwtDecoder jwtDecoder() {return JwtDecoders.fromIssuerLocation("https://your-oauth-server.com");}
}
4. 方法级安全
4.1 使用 @PreAuthorize 和 @PostAuthorize
@Service
public class ProductService {@PreAuthorize("hasRole('ADMIN') or @securityService.isProductOwner(#productId, authentication.name)")public Product updateProduct(Long productId, Product product) {// 更新产品逻辑}@PostAuthorize("returnObject.owner == authentication.name or hasRole('ADMIN')")public Product getProduct(Long productId) {// 获取产品逻辑}
}@Component("securityService")
public class SecurityService {public boolean isProductOwner(Long productId, String username) {// 检查用户是否是产品所有者return productRepository.findById(productId).map(product -> product.getOwner().equals(username)).orElse(false);}
}
5. CSRF 保护
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(authz -> authz.anyRequest().authenticated()).formLogin(Customizer.withDefaults()).csrf(csrf -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()));return http.build();
}
6. CORS 配置
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(authz -> authz.anyRequest().authenticated()).cors(cors -> cors.configurationSource(corsConfigurationSource()));return http.build();
}@Bean
CorsConfigurationSource corsConfigurationSource() {CorsConfiguration configuration = new CorsConfiguration();configuration.setAllowedOrigins(Arrays.asList("https://example.com"));configuration.setAllowedMethods(Arrays.asList("GET", "POST"));configuration.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type"));UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**", configuration);return source;
}
7. 密码编码
@Bean
public PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();
}@Service
public class UserService {@Autowiredprivate PasswordEncoder passwordEncoder;public User createUser(String username, String rawPassword) {String encodedPassword = passwordEncoder.encode(rawPassword);return new User(username, encodedPassword);}
}
8. 安全事件发布与监听
@Component
public class AuthenticationEventListener {@EventListenerpublic void handleAuthenticationSuccess(AuthenticationSuccessEvent event) {// 处理认证成功事件String username = event.getAuthentication().getName();log.info("User {} successfully authenticated", username);}@EventListenerpublic void handleAuthenticationFailure(AbstractAuthenticationFailureEvent event) {// 处理认证失败事件String username = event.getAuthentication().getName();log.warn("User {} failed to authenticate", username);}
}
9. 自定义访问决策
@Component
public class TimeBasedAccessDecisionVoter implements AccessDecisionVoter<Object> {@Overridepublic boolean supports(ConfigAttribute attribute) {return "TIME_ACCESS".equals(attribute.getAttribute());}@Overridepublic boolean supports(Class<?> clazz) {return true;}@Overridepublic int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {for (ConfigAttribute attribute : attributes) {if (this.supports(attribute)) {// 只允许在9:00-17:00之间访问int hour = LocalTime.now().getHour();if (hour >= 9 && hour < 17) {return ACCESS_GRANTED;} else {return ACCESS_DENIED;}}}return ACCESS_ABSTAIN;}
}@Configuration
@EnableMethodSecurity
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {@Autowiredprivate TimeBasedAccessDecisionVoter timeBasedVoter;@Overrideprotected AccessDecisionManager accessDecisionManager() {List<AccessDecisionVoter<?>> decisionVoters = new ArrayList<>();decisionVoters.add(new RoleVoter());decisionVoters.add(new AuthenticatedVoter());decisionVoters.add(timeBasedVoter);return new UnanimousBased(decisionVoters);}
}// 使用自定义投票器
@PreAuthorize("hasRole('USER') and @timeBasedAccessDecisionVoter.supports('TIME_ACCESS')")
public String timeSensitiveMethod() {return "This method can only be accessed between 9:00 and 17:00";
}
10. 响应式安全支持
@EnableWebFluxSecurity
public class ReactiveSecurityConfig {@Beanpublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {return http.authorizeExchange(exchanges -> exchanges.pathMatchers("/public/**").permitAll().anyExchange().authenticated()).httpBasic(Customizer.withDefaults()).formLogin(Customizer.withDefaults()).build();}@Beanpublic ReactiveUserDetailsService userDetailsService() {UserDetails user = User.withUsername("user").password("{noop}password").roles("USER").build();return new MapReactiveUserDetailsService(user);}
}
总结
Spring Security 6.x 提供了全面的安全功能,包括:
- 多种认证机制:表单登录、HTTP Basic、JWT、OAuth2等
- 灵活的授权控制:基于角色、权限、方法参数等
- 防护机制:CSRF、CORS、会话管理等
- 密码安全:多种密码编码器支持
- 事件机制:认证成功/失败事件监听
- 扩展性:自定义投票器、访问决策等
- 响应式支持:WebFlux应用的安全保护
这些功能使得Spring Security 6.x能够满足从简单应用到复杂企业级应用的各种安全需求。以上代码示例展示了如何使用这些功能,你可以根据实际需求进行调整和扩展。