架构安全
架构安全解决三个核心问题:「你是谁」(认证)、「你能做什么」(授权)、「传输过程安全」(加密)。微服务架构下,服务间调用同样需要处理认证和授权问题。
提示
认证、授权、加密、凭证管理的安全体系
适用场景
- 用户登录系统:JWT/Session 实现无状态或有状态认证
- 第三方登录集成:OAuth 2.0 授权码模式对接微信、GitHub 等
- 微服务间调用:服务网格 mTLS 双向认证
- API 网关鉴权:统一 Token 验证和权限校验
- 敏感数据保护:密码加盐 Hash、API 密钥管理
- 合规要求:等保、GDPR 等安全合规场景
认证 vs 授权
| 维度 | 认证(AuthN) | 授权(AuthZ) |
|---|---|---|
| 问题 | 你是谁? | 你能做什么? |
| 时机 | 登录时 | 访问资源时 |
| 凭证 | 用户名/密码、Token、证书 | 角色、权限列表 |
认证先于授权,确认身份后再判断权限。
JWT
JWT 由三部分组成:Header.Payload.Signature,通过 Base64 URL 编码。
| 部分 | 内容 | 说明 |
|---|---|---|
| Header | {"alg":"HS256","typ":"JWT"} | 签名算法和类型 |
| Payload | {"sub":"123","exp":1698000000} | 声明(用户信息、过期时间) |
| Signature | HMAC-SHA256 签名 | 防篡改 |
Payload 是 Base64 编码而非加密,不要存放敏感信息。TTL 建议短(如 15 分钟),配合 Refresh Token 机制。
JWT vs Session
| 维度 | Session | JWT |
|---|---|---|
| 存储 | 服务端 | 客户端 |
| 水平扩展 | 需要共享 Session(Redis) | 无状态,天然支持 |
| 注销 | 直接删除 | 需 Redis 黑名单或短过期 |
OAuth 2.0
OAuth 2.0 是开放授权标准,允许第三方应用获取用户授权而无须获取用户密码。
四种授权模式
| 模式 | 适用场景 | 安全性 |
|---|---|---|
| 授权码模式 | 有后端服务的 Web 应用 | 最高(推荐) |
| 简化模式(已废弃) | 纯前端 SPA | 低 |
| 密码模式 | 高度信任的客户端 | 中 |
| 客户端凭证 | 服务间调用(无用户参与) | 高 |
授权码流程
1. 用户点击"微信登录"
2. 跳转到授权页:GET /authorize?response_type=code&client_id=APP_ID&redirect_uri=CALLBACK_URL
3. 用户确认授权,返回授权码到 callback
4. 后端用授权码换 token(服务端交换,不暴露给前端)
5. 使用 access_token 调用资源服务器授权码中转防止 token 泄露给浏览器。
访问控制
访问控制决定谁能访问什么资源,是授权的具体执行机制。
RBAC
用户 → [分配] → 角色 → [拥有] → 权限RBAC 简单直观,适合组织结构明确、权限稳定的场景。缺点是角色爆炸和灵活性不足。
服务治理 中网关的认证鉴权可基于 RBAC/ABAC 实现。
ABAC
ABAC(基于属性的访问控制)根据用户属性、资源属性、环境属性(时间、位置)动态决定权限,灵活度高,但策略计算复杂。
@PreAuthorize("@permissionService.canEdit(authentication, #resourceId)")
public Document updateDocument(@PathVariable Long resourceId) {
return documentService.update(resourceId, dto);
}HTTPS / TLS
TLS 握手核心:非对称加密(RSA/ECC)交换密钥,对称加密(AES)传输数据。
1. ClientHello(TLS 版本、加密套件、随机数)
2. ServerHello + 证书(公钥)
3. 客户端验证证书有效性
4. 用公钥加密 Pre-Master Secret
5. 双方计算对称密钥
6. 后续使用对称加密通信TLS 1.3 简化了握手流程,减少了往返次数。
凭证安全
密码存储
// 错误:MD5 可被彩虹表破解
user.setPassword(MD5("123456"));
// 正确:BCrypt 加盐 Hash
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
user.setPassword(encoder.encode("123456"));
encoder.matches("123456", storedHash); // 验证BCrypt 内置随机盐,每次 Hash 结果不同,计算成本可调。
API 密钥原则
| 原则 | 说明 |
|---|---|
| 不硬编码 | 密钥不进代码仓库 |
| 环境变量 | 通过 ${API_KEY} 注入 |
| 密钥管理服务 | AWS Secrets Manager / 阿里云 KMS |
常见漏洞
| 漏洞 | 原因 | 防御 |
|---|---|---|
| SQL 注入 | 拼接 SQL | PreparedStatement / MyBatis 参数化 |
| XSS | 未过滤用户输入 | 输出转义、CSP |
| CSRF | 跨站请求伪造 | CSRF Token、SameSite Cookie |
| JWT 伪造 | alg 设为 none | 服务端强制验证算法 |
常见问题
Q: JWT 和 Session 怎么选?
A: 单体应用用 Session(简单、注销方便);微服务/跨域场景用 JWT(无状态、天然支持分布式)。JWT 必须配合短过期时间 + Refresh Token。
Q: OAuth 2.0 四种模式怎么选?
A: 有后端的 Web 应用用授权码模式(最安全);纯前端 SPA 用 PKCE 模式(授权码的改进版);服务间调用用客户端凭证模式;密码模式仅用于高度信任的第一方应用。
Q: 密码存储为什么不能用 MD5?
A: MD5 速度快、无盐,可被彩虹表秒破。BCrypt 内置随机盐、计算成本可调(默认 10 轮),暴力破解成本极高。
Q: HTTPS 能防止中间人攻击吗?
A: 能,但前提是正确配置:1) 使用 TLS 1.2+;2) 禁用弱加密套件;3) 验证证书链;4) 开启 HSTS。自签名证书无法防止中间人攻击。
Q: 微服务间如何保证调用安全?
A: 1) 服务网格 mTLS 双向认证;2) API 网关统一鉴权;3) JWT 透传用户身份;4) 最小权限原则配置服务账号。