SpringNotes

第 24 章:安全

zjc 于 2026-01-24 发布

这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 安全不是登录页面的一个过滤器,而是输入校验、认证授权、密钥管理、依赖治理、日志脱敏、传输加密和审计的完整体系。

24.1 认证与授权

概念 问题
Authentication 你是谁
Authorization 你能做什么
Auditing 你做过什么

常见认证:

  1. Session Cookie;
  2. JWT;
  3. OAuth2 / OIDC;
  4. API Key;
  5. mTLS;
  6. 云厂商签名。

授权模型:

模型 适合
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

校验点:

  1. 签名算法是否允许;
  2. 签名是否有效;
  3. exp 是否过期;
  4. nbf 是否生效;
  5. iss 是否匹配;
  6. aud 是否匹配;
  7. scope 是否足够;
  8. 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

注意:

  1. client secret 不进前端;
  2. token 不进日志;
  3. scope 最小化;
  4. 使用 HTTPS;
  5. 校验 aud;
  6. 处理 token 过期;
  7. 缓存时间小于有效期。

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 输入输出安全

  1. 服务端重复校验;
  2. 限制长度和范围;
  3. 白名单优先;
  4. 限制分页 size;
  5. 返回错误不泄露堆栈;
  6. 批量接口限制数量;
  7. 外部响应同样按不可信输入处理。

批量限制:

public record ImportRequest(
        @NotEmpty @Size(max = 1000) List<@Valid Item> items) {
}

24.7 密钥管理

禁止:

app:
  db-password: admin123

推荐:

环境变量
Vault
KMS
云 Secret Manager
Kubernetes Secret + 加密

治理:

  1. 定期轮转;
  2. 最小权限;
  3. 密钥泄露立即吊销;
  4. 不同环境不同密钥;
  5. 日志脱敏;
  6. 代码仓库历史扫描。

24.8 传输安全

  1. 外部强制 HTTPS;
  2. 内部按安全等级选择 mTLS 或服务网格;
  3. 证书有效期管理;
  4. TLS 版本和密码套件符合公司规范;
  5. HSTS;
  6. 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 依赖升级

治理:

  1. 统一 BOM;
  2. 定期升级;
  3. 记录 CVE 决策;
  4. 无法升级时评估缓解措施;
  5. 镜像最小化;
  6. SBOM 归档。

24.10 安全审计

必须记录:

登录成功/失败
权限变更
敏感数据导出
配置修改
管理员操作
支付相关操作
安全策略变更

审计日志字段:

time
actor
action
resource
result
sourceIp
traceId
reason

审计日志不可被普通用户修改,且要有独立存储和保留策略。

本章小结

Spring Security 提供认证授权框架,但安全边界要靠业务和平台共同保障。生产系统必须做服务端授权、输入校验、密钥管理、传输加密、依赖扫描和审计。安全设计要在需求阶段进入,而不是上线后补过滤器。

思考题

  1. JWT 如何处理登出和账号封禁?
  2. 哪些场景可以禁用 CSRF?
  3. 如何防止水平越权?
  4. 密钥应该放在哪里?
  5. 审计日志至少包含哪些字段?