java-security

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Java Security - Quick Reference

Java安全 - 快速参考

When NOT to Use This Skill

不适用本技能的场景

  • General OWASP concepts - Use
    owasp
    or
    owasp-top-10
    skill
  • Node.js/TypeScript security - Use base security skills
  • Python security - Use
    python-security
    skill
  • Secrets management - Use
    secrets-management
    skill
Deep Knowledge: Use
mcp__documentation__fetch_docs
with technology:
spring-boot
for Spring Security documentation.
  • 通用OWASP概念 - 使用
    owasp
    owasp-top-10
    技能
  • Node.js/TypeScript安全 - 使用基础安全技能
  • Python安全 - 使用
    python-security
    技能
  • 密钥管理 - 使用
    secrets-management
    技能
深度知识获取:使用
mcp__documentation__fetch_docs
工具,指定technology为
spring-boot
以获取Spring Security相关文档。

Dependency Auditing

依赖项审计

bash
undefined
bash
undefined

Maven - 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
undefined
snyk test --all-projects
undefined

Maven 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
undefined
java
@RateLimiter(name = "loginRateLimiter", fallbackMethod = "loginFallback")
public AuthResponse login(LoginRequest request) {
    // 登录逻辑
}

public AuthResponse loginFallback(LoginRequest request, RequestNotPermitted ex) {
    throw new TooManyRequestsException("登录尝试次数过多,请稍后再试。");
}
yaml
undefined

application.yml

application.yml

resilience4j: ratelimiter: instances: loginRateLimiter: limitForPeriod: 5 limitRefreshPeriod: 15m timeoutDuration: 0
undefined
resilience4j: ratelimiter: instances: loginRateLimiter: limitForPeriod: 5 limitRefreshPeriod: 15m timeoutDuration: 0
undefined

Input 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-PatternWhy It's BadCorrect Approach
@Query
with string concat
SQL injectionUse named parameters
:param
th:utext
for user content
XSS vulnerabilityUse
th:text
(auto-escaped)
MD5/SHA1 for passwordsEasily crackedUse BCrypt with strength 12+
Storing JWT secret in codeSecret exposureUse environment variables
permitAll()
for sensitive endpoints
Unauthorized accessDefine explicit auth rules
Disabling CSRF for stateful appsCSRF attacksKeep CSRF enabled for sessions
Catching
Exception
silently
Hides security issuesLog and handle specifically
反模式危害正确做法
@Query
使用字符串拼接
SQL注入风险使用命名参数
:param
对用户内容使用
th:utext
XSS漏洞使用
th:text
(自动转义)
对密码使用MD5/SHA1加密易被破解使用强度12+的BCrypt加密
在代码中硬编码JWT密钥密钥泄露使用环境变量存储
对敏感端点使用
permitAll()
未授权访问风险定义明确的认证规则
对有状态应用禁用CSRFCSRF攻击风险针对会话保持CSRF启用状态
静默捕获
Exception
隐藏安全问题针对性记录和处理异常

Quick Troubleshooting

快速故障排查

IssueLikely CauseSolution
403 on valid requestCSRF token missingInclude CSRF token in requests
401 with valid JWTToken expired or wrong keyCheck expiration and secret key
CORS error in browserMissing CORS configAdd origin to
allowedOrigins
Password validation failsBCrypt version mismatchUse same encoder version
Method security not working
@EnableMethodSecurity
missing
Add annotation to config class
Dependency check fails buildCVSS threshold too lowAdjust
failBuildOnCVSS
or suppress
问题可能原因解决方案
合法请求返回403缺少CSRF令牌在请求中包含CSRF令牌
有效JWT返回401令牌过期或密钥错误检查过期时间和密钥
浏览器出现CORS错误缺少CORS配置将来源添加到
allowedOrigins
密码验证失败BCrypt版本不匹配使用相同版本的加密器
方法级安全不生效缺少
@EnableMethodSecurity
注解
在配置类上添加该注解
依赖项检查导致构建失败CVSS阈值过低调整
failBuildOnCVSS
或添加抑制规则

Security Scanning Commands

安全扫描命令

bash
undefined
bash
undefined

OWASP 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
undefined
mvn sonar:sonar -Dsonar.host.url=http://localhost:9000
undefined

Related Skills

相关技能

  • OWASP Top 10:2025
  • OWASP General
  • Secrets Management
  • Supply Chain Security
  • OWASP Top 10:2025
  • 通用OWASP
  • 密钥管理
  • 供应链安全