关于 java-security
java-security 是面向 Java 应用开发者的安全速查技能,重点涉及 Spring Security。内容涵盖依赖审计、常见 Web 漏洞防护、认证、校验和安全运维。
主要功能
- 01Maven 与 Gradle 依赖审计
- 02Spring Security 配置示例
- 03SQL 注入与 XSS 防护
- 04认证、输入校验与文件上传
- 05安全事件记录和故障排查
适用场景
- 01审查 Maven 或 Gradle 依赖风险
- 02为 Spring Boot 配置安全过滤器
- 03避免 SQL 注入和跨站脚本漏洞
- 04检查 JWT、文件上传与访问规则
MCP 相关
README 指出可通过 mcp__documentation__fetch_docs 获取 Spring Boot 文档;未说明该技能提供 MCP 服务或客户端功能。
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ claude-dev-suite/ claude-dev-suite --skill java-security安装后在 Claude Code 中输入/java-security确认已加载。
详细介绍
Java 安全 - 快速参考
何时不应使用此技能
- 通用 OWASP 概念 - 使用
owasp或owasp-top-10技能 - Node.js/TypeScript 安全 - 使用基础安全技能
- Python 安全 - 使用
python-security技能 - 密钥管理 - 使用
secrets-management技能
深入了解:使用
mcp__documentation__fetch_docs,并将技术指定为spring-boot,以获取 Spring Security 文档。
依赖项审计
# Maven - OWASP Dependency Check
mvn dependency-check:check
# Maven - check for updates
mvn versions:display-dependency-updates
# Gradle - dependency check plugin
./gradlew dependencyCheckAnalyze
# Snyk for Java
snyk test --all-projects
Maven 插件配置
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>9.0.9</version>
<configuration>
<failBuildOnCVSS>7</failBuildOnCVSS>
<suppressionFile>dependency-check-suppression.xml</suppressionFile>
</configuration>
</plugin>
Spring Security 配置
基本安全配置(Spring Boot 3.x)
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
)
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
.headers(headers -> headers
.contentSecurityPolicy(csp ->
csp.policyDirectives("default-src 'self'; script-src 'self'"))
.frameOptions(frame -> frame.deny())
.xssProtection(xss -> xss.disable()) // Use CSP instead
.contentTypeOptions(Customizer.withDefaults())
)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.build();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of("https://myapp.com"));
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/**", config);
return source;
}
}
密码编码
@Bean
public PasswordEncoder passwordEncoder() {
// BCrypt with strength 12 (recommended)
return new BCryptPasswordEncoder(12);
}
// Usage
String encoded = passwordEncoder.encode(rawPassword);
boolean matches = passwordEncoder.matches(rawPassword, encoded);
方法级安全
@Configuration
@EnableMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig {}
// Usage in service
@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(Long id) { ... }
@PreAuthorize("#userId == authentication.principal.id or hasRole('ADMIN')")
public User getUser(Long userId) { ... }
@PostAuthorize("returnObject.owner == authentication.principal.username")
public Document getDocument(Long id) { ... }
防止 SQL 注入
JPA/Hibernate - 安全
// SAFE - Named parameters
@Query("SELECT u FROM User u WHERE u.email = :email")
Optional<User> findByEmail(@Param("email") String email);
// SAFE - Criteria API
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> root = query.from(User.class);
query.where(cb.equal(root.get("email"), email));
// SAFE - Spring Data JPA method names
Optional<User> findByEmailAndStatus(String email, Status status);
JPA/Hibernate - 不安全
// UNSAFE - String concatenation
@Query("SELECT u FROM User u WHERE u.email = '" + email + "'") // NEVER!
// UNSAFE - Native query without parameters
@Query(value = "SELECT * FROM users WHERE email = " + email, nativeQuery = true) // NEVER!
JDBC Template - 安全
// SAFE - Parameterized query
jdbcTemplate.query(
"SELECT * FROM users WHERE email = ? AND status = ?",
new Object[]{email, status},
userRowMapper
);
// SAFE - Named parameters
namedParameterJdbcTemplate.query(
"SELECT * FROM users WHERE email = :email",
Map.of("email", email),
userRowMapper
);
防止 XSS
Thymeleaf(自动转义)
<p th:text="${userInput}"></p>
<p th:utext="${userInput}"></p>
API 响应净化
// Use OWASP Java HTML Sanitizer
import org.owasp.html.PolicyFactory;
import org.owasp.html.Sanitizers;
PolicyFactory policy = Sanitizers.FORMATTING.and(Sanitizers.LINKS);
String safeHtml = policy.sanitize(userInput);
身份验证最佳实践
JWT 配置
@Component
public class JwtTokenProvider {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration:3600000}") // 1 hour
private long expiration;
public String generateToken(Authentication auth) {
Date now = new Date();
Date expiryDate = new Date(now.getTime() + expiration);
return Jwts.builder()
.setSubject(auth.getName())
.setIssuedAt(now)
.setExpiration(expiryDate)
.signWith(Keys.hmacShaKeyFor(secret.getBytes()), SignatureAlgorithm.HS512)
.compact();
}
public boolean validateToken(String token) {
try {
Jwts.parserBuilder()
.setSigningKey(Keys.hmacShaKeyFor(secret.getBytes()))
.build()
.parseClaimsJws(token);
return true;
} catch (JwtException | IllegalArgumentException e) {
return false;
}
}
}
使用 Resilience4j 进行速率限制
@RateLimiter(name = "loginRateLimiter", fallbackMethod = "loginFallback")
public AuthResponse login(LoginRequest request) {
// login logic
}
public AuthResponse loginFallback(LoginRequest request, RequestNotPermitted ex) {
throw new TooManyRequestsException("Too many login attempts. Try again later.");
}
# application.yml
resilience4j:
ratelimiter:
instances:
loginRateLimiter:
limitForPeriod: 5
limitRefreshPeriod: 15m
timeoutDuration: 0
输入验证
public record CreateUserRequest(
@NotBlank
@Email
@Size(max = 255)
String email,
@NotBlank
@Size(min = 12, max = 128)
@Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&]).*$",
message = "Password must contain uppercase, lowercase, number and special char")
String password,
@NotBlank
@Size(min = 2, max = 100)
@Pattern(regexp = "^[a-zA-Z\\s-']+$")
String name
) {}
@PostMapping("/users")
public ResponseEntity<User> createUser(@Valid @RequestBody CreateUserRequest request) {
// request is already validated
}
安全文件上传
@PostMapping("/upload")
public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) {
// Validate file type
String contentType = file.getContentType();
if (!ALLOWED_TYPES.contains(contentType)) {
throw new InvalidFileTypeException("File type not allowed");
}
// Validate file size (also configure in application.yml)
if (file.getSize() > MAX_FILE_SIZE) {
throw new FileTooLargeException("File exceeds maximum size");
}
// Generate safe filename
String originalName = file.getOriginalFilename();
String safeName = UUID.randomUUID() + getExtension(originalName);
// Store outside web root
Path destination = uploadPath.resolve(safeName);
Files.copy(file.getInputStream(), destination);
return ResponseEntity.ok(safeName);
}
记录安全事件
@Slf4j
@Component
public class SecurityEventLogger {
public void logLoginAttempt(String username, boolean success, HttpServletRequest request) {
log.info("Login attempt: user={}, success={}, ip={}, userAgent={}",
username,
success,
request.getRemoteAddr(),
request.getHeader("User-Agent")
);
}
public void logAccessDenied(String username, String resource, HttpServletRequest request) {
log.warn("Access denied: user={}, resource={}, ip={}",
username,
resource,
request.getRemoteAddr()
);
}
// NEVER log sensitive data
// log.info("Password: {}", password); // NEVER!
// log.info("Token: {}", jwt); // NEVER!
}
反模式
| 反模式 | 问题所在 | 正确做法 |
|---|---|---|
使用字符串拼接的 @Query | SQL 注入 | 使用命名参数 :param |
对用户内容使用 th:utext | XSS 漏洞 | 使用 th:text(自动转义) |
| 使用 MD5/SHA1 存储密码 | 容易被破解 | 使用强度为 12+ 的 BCrypt |
| 在代码中存储 JWT 密钥 | 密钥暴露 | 使用环境变量 |
对敏感端点使用 permitAll() | 未授权访问 | 定义明确的身份验证规则 |
| 对有状态应用禁用 CSRF | CSRF 攻击 | 为会话启用 CSRF |
静默捕获 Exception | 掩盖安全问题 | 记录日志并针对性处理 |
快速故障排查
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 有效请求返回 403 | 缺少 CSRF 令牌 | 在请求中包含 CSRF 令牌 |
| 有效 JWT 返回 401 | 令牌已过期或密钥错误 | 检查过期时间和密钥 |
| 浏览器出现 CORS 错误 | 缺少 CORS 配置 | 将来源添加到 allowedOrigins |
| 密码验证失败 | BCrypt 版本不匹配 | 使用相同版本的编码器 |
| 方法安全未生效 | 缺少 @EnableMethodSecurity | 在配置类中添加注解 |
| 依赖项检查导致构建失败 | CVSS 阈值过低 | 调整 failBuildOnCVSS 或添加抑制规则 |
安全扫描命令
# OWASP Dependency Check
mvn dependency-check:check
./gradlew dependencyCheckAnalyze
# SpotBugs with Security Plugin
mvn spotbugs:check -Dspotbugs.plugins=com.h3xstream.findsecbugs:findsecbugs-plugin:1.12.0
# Snyk
snyk test --all-projects
# SonarQube (if configured)
mvn sonar:sonar -Dsonar.host.url=http://localhost:9000
相关技能
评论
支持 Markdown