java-security
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseJava Security - Quick Reference
Java安全 - 快速参考
When NOT to Use This Skill
不适用本技能的场景
- General OWASP concepts - Use or
owaspskillowasp-top-10 - Node.js/TypeScript security - Use base security skills
- Python security - Use skill
python-security - Secrets management - Use skill
secrets-management
Deep Knowledge: Usewith technology:mcp__documentation__fetch_docsfor Spring Security documentation.spring-boot
- 通用OWASP概念 - 使用或
owasp技能owasp-top-10 - Node.js/TypeScript安全 - 使用基础安全技能
- Python安全 - 使用技能
python-security - 密钥管理 - 使用技能
secrets-management
深度知识获取:使用工具,指定technology为mcp__documentation__fetch_docs以获取Spring Security相关文档。spring-boot
Dependency Auditing
依赖项审计
bash
undefinedbash
undefinedMaven - OWASP Dependency Check
Maven - OWASP依赖项检查
mvn dependency-check:check
mvn dependency-check:check
Maven - check for updates
Maven - 检查更新
mvn versions:display-dependency-updates
mvn versions:display-dependency-updates
Gradle - dependency check plugin
Gradle - 依赖项检查插件
./gradlew dependencyCheckAnalyze
./gradlew dependencyCheckAnalyze
Snyk for Java
Snyk Java安全扫描
snyk test --all-projects
undefinedsnyk test --all-projects
undefinedMaven Plugin Configuration
Maven插件配置
xml
<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>xml
<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 Configuration
Spring Security配置
Basic Security Config (Spring Boot 3.x)
基础安全配置(Spring Boot 3.x)
java
@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;
}
}java
@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()) // 推荐使用CSP替代
.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;
}
}Password Encoding
密码加密
java
@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);java
@Bean
public PasswordEncoder passwordEncoder() {
// BCrypt加密,强度12(推荐值)
return new BCryptPasswordEncoder(12);
}
// 使用示例
String encoded = passwordEncoder.encode(rawPassword);
boolean matches = passwordEncoder.matches(rawPassword, encoded);Method-Level Security
方法级安全
java
@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) { ... }java
@Configuration
@EnableMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig {}
// 在服务层使用
@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 Injection Prevention
SQL注入防护
JPA/Hibernate - Safe
JPA/Hibernate - 安全写法
java
// 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);java
// 安全 - 使用命名参数
@Query("SELECT u FROM User u WHERE u.email = :email")
Optional<User> findByEmail(@Param("email") String email);
// 安全 - 使用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));
// 安全 - Spring Data JPA方法命名
Optional<User> findByEmailAndStatus(String email, Status status);JPA/Hibernate - UNSAFE
JPA/Hibernate - 不安全写法
java
// 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!java
// 不安全 - 字符串拼接
@Query("SELECT u FROM User u WHERE u.email = '" + email + "'") // 绝对禁止!
// 不安全 - 未使用参数的原生查询
@Query(value = "SELECT * FROM users WHERE email = " + email, nativeQuery = true) // 绝对禁止!JDBC Template - Safe
JDBC Template - 安全写法
java
// 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
);java
// 安全 - 参数化查询
jdbcTemplate.query(
"SELECT * FROM users WHERE email = ? AND status = ?",
new Object[]{email, status},
userRowMapper
);
// 安全 - 命名参数
namedParameterJdbcTemplate.query(
"SELECT * FROM users WHERE email = :email",
Map.of("email", email),
userRowMapper
);XSS Prevention
XSS防护
Thymeleaf (Auto-escaping)
Thymeleaf(自动转义)
html
<!-- SAFE - Auto-escaped -->
<p th:text="${userInput}"></p>
<!-- UNSAFE - Unescaped HTML -->
<p th:utext="${userInput}"></p> <!-- Avoid if possible -->html
<!-- 安全 - 自动转义 -->
<p th:text="${userInput}"></p>
<!-- 不安全 - 未转义HTML -->
<p th:utext="${userInput}"></p> <!-- 尽可能避免使用 -->API Response Sanitization
API响应内容清理
java
// 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);java
// 使用OWASP Java HTML清理器
import org.owasp.html.PolicyFactory;
import org.owasp.html.Sanitizers;
PolicyFactory policy = Sanitizers.FORMATTING.and(Sanitizers.LINKS);
String safeHtml = policy.sanitize(userInput);Authentication Best Practices
身份认证最佳实践
JWT Configuration
JWT配置
java
@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;
}
}
}java
@Component
public class JwtTokenProvider {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration:3600000}") // 1小时
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;
}
}
}Rate Limiting with Resilience4j
使用Resilience4j实现限流
java
@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.");
}yaml
undefinedjava
@RateLimiter(name = "loginRateLimiter", fallbackMethod = "loginFallback")
public AuthResponse login(LoginRequest request) {
// 登录逻辑
}
public AuthResponse loginFallback(LoginRequest request, RequestNotPermitted ex) {
throw new TooManyRequestsException("登录尝试次数过多,请稍后再试。");
}yaml
undefinedapplication.yml
application.yml
resilience4j:
ratelimiter:
instances:
loginRateLimiter:
limitForPeriod: 5
limitRefreshPeriod: 15m
timeoutDuration: 0
undefinedresilience4j:
ratelimiter:
instances:
loginRateLimiter:
limitForPeriod: 5
limitRefreshPeriod: 15m
timeoutDuration: 0
undefinedInput Validation
输入验证
java
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
}java
public record CreateUserRequest(
@NotBlank
@Email
@Size(max = 255)
String email,
@NotBlank
@Size(min = 12, max = 128)
@Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&]).*$",
message = "密码必须包含大写字母、小写字母、数字和特殊字符")
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) {
// 请求已完成验证
}Secure File Upload
安全文件上传
java
@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);
}java
@PostMapping("/upload")
public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) {
// 验证文件类型
String contentType = file.getContentType();
if (!ALLOWED_TYPES.contains(contentType)) {
throw new InvalidFileTypeException("不允许的文件类型");
}
// 验证文件大小(也可在application.yml中配置)
if (file.getSize() > MAX_FILE_SIZE) {
throw new FileTooLargeException("文件超出最大限制");
}
// 生成安全文件名
String originalName = file.getOriginalFilename();
String safeName = UUID.randomUUID() + getExtension(originalName);
// 存储在Web根目录之外
Path destination = uploadPath.resolve(safeName);
Files.copy(file.getInputStream(), destination);
return ResponseEntity.ok(safeName);
}Logging Security Events
安全事件日志
java
@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!
}java
@Slf4j
@Component
public class SecurityEventLogger {
public void logLoginAttempt(String username, boolean success, HttpServletRequest request) {
log.info("登录尝试: user={}, success={}, ip={}, userAgent={}",
username,
success,
request.getRemoteAddr(),
request.getHeader("User-Agent")
);
}
public void logAccessDenied(String username, String resource, HttpServletRequest request) {
log.warn("访问被拒绝: user={}, resource={}, ip={}",
username,
resource,
request.getRemoteAddr()
);
}
// 绝对禁止记录敏感数据
// log.info("Password: {}", password); // 绝对禁止!
// log.info("Token: {}", jwt); // 绝对禁止!
}Anti-Patterns
反模式
| Anti-Pattern | Why It's Bad | Correct Approach |
|---|---|---|
| SQL injection | Use named parameters |
| XSS vulnerability | Use |
| MD5/SHA1 for passwords | Easily cracked | Use BCrypt with strength 12+ |
| Storing JWT secret in code | Secret exposure | Use environment variables |
| Unauthorized access | Define explicit auth rules |
| Disabling CSRF for stateful apps | CSRF attacks | Keep CSRF enabled for sessions |
Catching | Hides security issues | Log and handle specifically |
| 反模式 | 危害 | 正确做法 |
|---|---|---|
| SQL注入风险 | 使用命名参数 |
对用户内容使用 | XSS漏洞 | 使用 |
| 对密码使用MD5/SHA1加密 | 易被破解 | 使用强度12+的BCrypt加密 |
| 在代码中硬编码JWT密钥 | 密钥泄露 | 使用环境变量存储 |
对敏感端点使用 | 未授权访问风险 | 定义明确的认证规则 |
| 对有状态应用禁用CSRF | CSRF攻击风险 | 针对会话保持CSRF启用状态 |
静默捕获 | 隐藏安全问题 | 针对性记录和处理异常 |
Quick Troubleshooting
快速故障排查
| Issue | Likely Cause | Solution |
|---|---|---|
| 403 on valid request | CSRF token missing | Include CSRF token in requests |
| 401 with valid JWT | Token expired or wrong key | Check expiration and secret key |
| CORS error in browser | Missing CORS config | Add origin to |
| Password validation fails | BCrypt version mismatch | Use same encoder version |
| Method security not working | | Add annotation to config class |
| Dependency check fails build | CVSS threshold too low | Adjust |
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 合法请求返回403 | 缺少CSRF令牌 | 在请求中包含CSRF令牌 |
| 有效JWT返回401 | 令牌过期或密钥错误 | 检查过期时间和密钥 |
| 浏览器出现CORS错误 | 缺少CORS配置 | 将来源添加到 |
| 密码验证失败 | BCrypt版本不匹配 | 使用相同版本的加密器 |
| 方法级安全不生效 | 缺少 | 在配置类上添加该注解 |
| 依赖项检查导致构建失败 | CVSS阈值过低 | 调整 |
Security Scanning Commands
安全扫描命令
bash
undefinedbash
undefinedOWASP Dependency Check
OWASP依赖项检查
mvn dependency-check:check
./gradlew dependencyCheckAnalyze
mvn dependency-check:check
./gradlew dependencyCheckAnalyze
SpotBugs with Security Plugin
带安全插件的SpotBugs
mvn spotbugs:check -Dspotbugs.plugins=com.h3xstream.findsecbugs:findsecbugs-plugin:1.12.0
mvn spotbugs:check -Dspotbugs.plugins=com.h3xstream.findsecbugs:findsecbugs-plugin:1.12.0
Snyk
Snyk扫描
snyk test --all-projects
snyk test --all-projects
SonarQube (if configured)
SonarQube(已配置的情况下)
mvn sonar:sonar -Dsonar.host.url=http://localhost:9000
undefinedmvn sonar:sonar -Dsonar.host.url=http://localhost:9000
undefinedRelated Skills
相关技能
- OWASP Top 10:2025
- OWASP General
- Secrets Management
- Supply Chain Security
- OWASP Top 10:2025
- 通用OWASP
- 密钥管理
- 供应链安全