Reviews Spring Boot security best practices for authentication (JWT/OAuth2/Session), authorization (@PreAuthorize), input validation, CSRF/CORS/headers, secrets management, rate limiting, and dependency vulnerabilities.
npx claudepluginhub xu-xiang/everything-claude-code-zhThis skill uses the workspace's default tool permissions.
在添加身份验证(Auth)、处理输入、创建端点或处理机密信息时使用。
Provides Spring Security best practices for Spring Boot services: authn/authz, input validation, CSRF protection, secrets management, security headers, rate limiting, dependency checks. Use when adding auth, handling inputs, or securing endpoints.
Applies Spring Security best practices for authn/authz, input validation, CSRF, secrets, headers, rate limiting, and dependency security in Java Spring Boot services.
Guides Spring Security implementation for authentication, authorization, OAuth2, JWT, CORS, CSRF in Spring Boot applications.
Share bugs, ideas, or general feedback.
在添加身份验证(Auth)、处理输入、创建端点或处理机密信息时使用。
@PreAuthorize、基于角色的访问控制)httpOnly、Secure、SameSite=Strict 属性的 CookieOncePerRequestFilter 或资源服务器校验令牌@Component
public class JwtAuthFilter extends OncePerRequestFilter {
private final JwtService jwtService;
public JwtAuthFilter(JwtService jwtService) {
this.jwtService = jwtService;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
String header = request.getHeader(HttpHeaders.AUTHORIZATION);
if (header != null && header.startsWith("Bearer ")) {
String token = header.substring(7);
Authentication auth = jwtService.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(auth);
}
chain.doFilter(request, response);
}
}
@EnableMethodSecurity@PreAuthorize("hasRole('ADMIN')") 或 @PreAuthorize("@authz.canEdit(#id)")@RestController
@RequestMapping("/api/admin")
public class AdminController {
@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/users")
public List<UserDto> listUsers() {
return userService.findAll();
}
@PreAuthorize("@authz.isOwner(#id, authentication)")
@DeleteMapping("/users/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.delete(id);
return ResponseEntity.noContent().build();
}
}
@Valid 使用 Bean Validation@NotBlank、@Email、@Size、自定义校验器// 差(BAD):没有校验
@PostMapping("/users")
public User createUser(@RequestBody UserDto dto) {
return userService.create(dto);
}
// 好(GOOD):校验过的 DTO
public record CreateUserDto(
@NotBlank @Size(max = 100) String name,
@NotBlank @Email String email,
@NotNull @Min(0) @Max(150) Integer age
) {}
@PostMapping("/users")
public ResponseEntity<UserDto> createUser(@Valid @RequestBody CreateUserDto dto) {
return ResponseEntity.status(HttpStatus.CREATED)
.body(userService.create(dto));
}
:param 绑定;切勿拼接字符串// 差(BAD):在原生查询中直接拼接字符串
@Query(value = "SELECT * FROM users WHERE name = '" + name + "'", nativeQuery = true)
// 好(GOOD):参数化原生查询
@Query(value = "SELECT * FROM users WHERE name = :name", nativeQuery = true)
List<User> findByName(@Param("name") String name);
// 好(GOOD):Spring Data 衍生查询(自动参数化)
List<User> findByEmailAndActiveTrue(String email);
PasswordEncoder Bean,而不是手动执行哈希@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12); // 成本因子(cost factor)为 12
}
// 在 Service 中
public User register(CreateUserDto dto) {
String hashedPassword = passwordEncoder.encode(dto.password());
return userRepository.save(new User(dto.email(), hashedPassword));
}
http
.csrf(csrf -> csrf.disable())
.sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
application.yml 不含凭据信息;使用占位符# 差(BAD):在 application.yml 中硬编码
spring:
datasource:
password: mySecretPassword123
# 好(GOOD):使用环境变量占位符
spring:
datasource:
password: ${DB_PASSWORD}
# 好(GOOD):Spring Cloud Vault 集成
spring:
cloud:
vault:
uri: https://vault.example.com
token: ${VAULT_TOKEN}
http
.headers(headers -> headers
.contentSecurityPolicy(csp -> csp
.policyDirectives("default-src 'self'"))
.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin)
.xssProtection(Customizer.withDefaults())
.referrerPolicy(rp -> rp.policy(ReferrerPolicyHeaderWriter.ReferrerPolicy.NO_REFERRER)));
*@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of("https://app.example.com"));
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
config.setAllowedHeaders(List.of("Authorization", "Content-Type"));
config.setAllowCredentials(true);
config.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/**", config);
return source;
}
// 在 SecurityFilterChain 中:
http.cors(cors -> cors.configurationSource(corsConfigurationSource()));
// 使用 Bucket4j 进行单端点速率限制
@Component
public class RateLimitFilter extends OncePerRequestFilter {
private final Map<String, Bucket> buckets = new ConcurrentHashMap<>();
private Bucket createBucket() {
return Bucket.builder()
.addLimit(Bandwidth.classic(100, Refill.intervally(100, Duration.ofMinutes(1))))
.build();
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
String clientIp = request.getRemoteAddr();
Bucket bucket = buckets.computeIfAbsent(clientIp, k -> createBucket());
if (bucket.tryConsume(1)) {
chain.doFilter(request, response);
} else {
response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
response.getWriter().write("{\"error\": \"Rate limit exceeded\"}");
}
}
}
记住:遵循默认拒绝、校验输入、最小权限原则,并优先通过配置实现安全。