Java 完全指南 / 28 - 安全:加密、认证、OWASP、SQL 注入防护
28 - 安全:加密、认证、OWASP、SQL 注入防护
OWASP Top 10(2021)
| 排名 | 漏洞类型 | Java 防护措施 |
|---|
| A01 | 访问控制失效 | Spring Security、RBAC |
| A02 | 加密机制失败 | 使用成熟库,不自创算法 |
| A03 | 注入 | PreparedStatement、参数校验 |
| A04 | 不安全设计 | 威胁建模、安全评审 |
| A05 | 安全配置错误 | 最小权限、关闭调试端口 |
| A06 | 过时组件 | 定期更新依赖(Dependabot) |
| A07 | 认证失败 | 强密码策略、MFA |
| A08 | 数据完整性失败 | 签名验证、CI/CD 安全 |
| A09 | 日志监控不足 | 结构化日志、告警 |
| A10 | SSRF | 白名单校验、禁止内网访问 |
加密基础
import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.*;
import java.util.Base64;
public class CryptoUtils {
// ---- 对称加密 AES ----
private static final String AES_KEY = "0123456789abcdef"; // 16字节 = AES-128
public static String aesEncrypt(String plainText) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(AES_KEY.getBytes(), "AES");
byte[] iv = new byte[16];
new SecureRandom().nextBytes(iv);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(iv));
byte[] encrypted = cipher.doFinal(plainText.getBytes());
// IV + 密文
byte[] combined = new byte[iv.length + encrypted.length];
System.arraycopy(iv, 0, combined, 0, iv.length);
System.arraycopy(encrypted, 0, combined, iv.length, encrypted.length);
return Base64.getEncoder().encodeToString(combined);
}
public static String aesDecrypt(String cipherText) throws Exception {
byte[] combined = Base64.getDecoder().decode(cipherText);
byte[] iv = Arrays.copyOfRange(combined, 0, 16);
byte[] encrypted = Arrays.copyOfRange(combined, 16, combined.length);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(AES_KEY.getBytes(), "AES");
cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(iv));
return new String(cipher.doFinal(encrypted));
}
// ---- 哈希 SHA-256 ----
public static String sha256(String input) throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(input.getBytes());
return Base64.getEncoder().encodeToString(hash);
}
// ---- BCrypt 密码哈希(推荐)----
// 依赖: org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
public static String bcryptHash(String password) {
var encoder = new org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder();
return encoder.encode(password);
}
public static boolean bcryptVerify(String password, String hash) {
var encoder = new org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder();
return encoder.matches(password, hash);
}
}
加密算法对比
| 算法 | 类型 | 用途 | 推荐 |
|---|
| AES-256 | 对称加密 | 数据加密 | ✅ |
| RSA-2048+ | 非对称加密 | 密钥交换、签名 | ✅ |
| SHA-256 | 哈希 | 完整性校验 | ✅ |
| BCrypt | 哈希 | 密码存储 | ✅ 强烈推荐 |
| MD5 | 哈希 | 仅校验 | ❌ 不安全 |
| SHA-1 | 哈希 | | ❌ 已被破解 |
JWT 认证
import io.jsonwebtoken.*;
import java.util.Date;
public class JwtUtils {
private static final String SECRET = "your-256-bit-secret-key-here-minimum-32-chars";
private static final long EXPIRATION = 3600000; // 1小时
public static String generateToken(Long userId, String username) {
return Jwts.builder()
.setSubject(String.valueOf(userId))
.claim("username", username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION))
.signWith(SignatureAlgorithm.HS256, SECRET)
.compact();
}
public static Claims parseToken(String token) {
return Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token)
.getBody();
}
public static boolean isValid(String token) {
try {
Claims claims = parseToken(token);
return !claims.getExpiration().before(new Date());
} catch (JwtException e) {
return false;
}
}
}
SQL 注入防护
// ❌ 危险 —— SQL 注入
String sql = "SELECT * FROM users WHERE name = '" + userInput + "'";
// 输入: ' OR '1'='1 → 返回所有用户!
// ✅ 安全 —— PreparedStatement
String sql = "SELECT * FROM users WHERE name = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, userInput); // 自动转义
// ✅ 安全 —— JPA/Hibernate
@Query("SELECT u FROM User u WHERE u.name = :name")
List<User> findByName(@Param("name") String name);
// ✅ 安全 —— MyBatis #{}
// <select>SELECT * FROM users WHERE name = #{name}</select>
// ❌ 危险 —— MyBatis ${}
// <select>SELECT * FROM users WHERE name = ${name}</select>
XSS 防护
// 输出时转义
import org.springframework.web.util.HtmlUtils;
public String sanitize(String input) {
return HtmlUtils.htmlEscape(input);
// <script>alert('xss')</script> → <script>alert('xss')</script>
}
// CSP 响应头
@GetMapping("/page")
public ResponseEntity<String> page() {
return ResponseEntity.ok()
.header("Content-Security-Policy", "default-src 'self'; script-src 'self'")
.body(htmlContent);
}
Spring Security 快速配置
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.ignoringRequestMatchers("/api/**")) // REST API 禁用 CSRF
.sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.addFilterBefore(jwtFilter(), UsernamePasswordAuthenticationFilter.class)
.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
⚠️ 注意事项
- 密码永远不要明文存储 — 使用 BCrypt。
- 密钥不要硬编码 — 使用环境变量或密钥管理服务。
- HTTPS 必须 — 生产环境禁止 HTTP。
- 依赖安全扫描 — 定期检查已知漏洞(CVE)。
💡 技巧
- Spring Boot Actuator 安全 — 生产环境只暴露必要端点。
- Rate Limiting — 使用 Bucket4j 或 Resilience4j 限流防暴力破解。
- CORS 配置:
@Bean
CorsConfigurationSource corsConfigurationSource() {
var config = new CorsConfiguration();
config.setAllowedOrigins(List.of("https://example.com"));
config.setAllowedMethods(List.of("GET", "POST"));
var source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
🏢 业务场景
- 用户认证: JWT + Spring Security 实现无状态认证。
- 数据加密: 敏感字段(身份证、银行卡)加密存储。
- API 安全: OAuth2 + API Key 保护接口。
📖 扩展阅读