这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 安全不是登录页面的一个过滤器,而是输入校验、认证授权、密钥管理、依赖治理、日志脱敏、传输加密和审计的完整体系。
24.1 认证与授权
| 概念 | 问题 |
|---|---|
| Authentication | 你是谁 |
| Authorization | 你能做什么 |
| Auditing | 你做过什么 |
常见认证:
- Session Cookie;
- JWT;
- OAuth2 / OIDC;
- API Key;
- mTLS;
- 云厂商签名。
授权模型:
| 模型 | 适合 |
|---|---|
| RBAC | 角色权限清晰 |
| ABAC | 属性条件复杂 |
| ACL | 资源级别控制 |
| Policy | 平台统一策略 |
24.2 Spring Security
依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
配置:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.csrf(CsrfConfigurer::disable)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/health").permitAll()
.requestMatchers("/api/**").authenticated()
.anyRequest().denyAll())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.build();
}
}
前后端分离 API 常用无状态 JWT,但 CSRF 禁用必须基于“不使用 Cookie 认证”等前提。
24.3 JWT
结构:
header.payload.signature
校验点:
- 签名算法是否允许;
- 签名是否有效;
- exp 是否过期;
- nbf 是否生效;
- iss 是否匹配;
- aud 是否匹配;
- scope 是否足够;
- key 是否轮转。
JWT 无法主动撤销。登出、封禁、改密需要短过期时间、黑名单、刷新机制或版本号校验。
24.4 OAuth2 与 OIDC
常见模式:
| 模式 | 场景 |
|---|---|
| Authorization Code + PKCE | Web / 移动应用 |
| Client Credentials | 服务对服务 |
| Device Code | 设备登录 |
| Refresh Token | 换取新 access token |
服务间调用:
Order Service
-> token endpoint
-> access token
-> Inventory Service
注意:
- client secret 不进前端;
- token 不进日志;
- scope 最小化;
- 使用 HTTPS;
- 校验 aud;
- 处理 token 过期;
- 缓存时间小于有效期。
24.5 常见漏洞防护
SQL 注入使用参数绑定:
jdbcClient.sql("select * from users where id = :id").param("id", id);
其他防护:
| 风险 | 防护 |
|---|---|
| XSS | 输出转义、CSP、富文本白名单 |
| CSRF | Cookie 认证启用 token |
| SSRF | URL 白名单、DNS 校验、禁内网访问 |
| 越权 | 服务端校验资源归属 |
| 反序列化 | 禁止任意类型 |
| 文件上传 | 校验魔数、大小、扩展名 |
水平越权示例:
Order order = repository.findByIdAndUserId(id, currentUser.id())
.orElseThrow(OrderNotFoundException::new);
仅依赖前端隐藏按钮不是授权。
24.6 输入输出安全
- 服务端重复校验;
- 限制长度和范围;
- 白名单优先;
- 限制分页 size;
- 返回错误不泄露堆栈;
- 批量接口限制数量;
- 外部响应同样按不可信输入处理。
批量限制:
public record ImportRequest(
@NotEmpty @Size(max = 1000) List<@Valid Item> items) {
}
24.7 密钥管理
禁止:
app:
db-password: admin123
推荐:
环境变量
Vault
KMS
云 Secret Manager
Kubernetes Secret + 加密
治理:
- 定期轮转;
- 最小权限;
- 密钥泄露立即吊销;
- 不同环境不同密钥;
- 日志脱敏;
- 代码仓库历史扫描。
24.8 传输安全
- 外部强制 HTTPS;
- 内部按安全等级选择 mTLS 或服务网格;
- 证书有效期管理;
- TLS 版本和密码套件符合公司规范;
- HSTS;
- Cookie 设置 Secure、HttpOnly、SameSite。
server:
ssl:
enabled: true
key-store: /certs/server.p12
key-store-password: ${TLS_PASSWORD}
24.9 依赖安全
命令:
mvn dependency:tree
扫描工具:
| 工具 | 用途 |
|---|---|
| OWASP Dependency-Check | CVE 扫描 |
| Trivy | 镜像和依赖扫描 |
| Snyk / Dependabot / Renovate | 依赖升级 |
治理:
- 统一 BOM;
- 定期升级;
- 记录 CVE 决策;
- 无法升级时评估缓解措施;
- 镜像最小化;
- SBOM 归档。
24.10 安全审计
必须记录:
登录成功/失败
权限变更
敏感数据导出
配置修改
管理员操作
支付相关操作
安全策略变更
审计日志字段:
time
actor
action
resource
result
sourceIp
traceId
reason
审计日志不可被普通用户修改,且要有独立存储和保留策略。
本章小结
Spring Security 提供认证授权框架,但安全边界要靠业务和平台共同保障。生产系统必须做服务端授权、输入校验、密钥管理、传输加密、依赖扫描和审计。安全设计要在需求阶段进入,而不是上线后补过滤器。
思考题
- JWT 如何处理登出和账号封禁?
- 哪些场景可以禁用 CSRF?
- 如何防止水平越权?
- 密钥应该放在哪里?
- 审计日志至少包含哪些字段?