CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
软件架构师入门/课时/安全架构
RawBlame
课时目录12 节

软件架构师入门

  1. 01软件架构是什么
  2. 02架构师软技能
  3. 03设计模式与原则
  4. 04架构风格
  5. 05数据架构
  6. 06API 与集成
  7. 07安全架构
  8. 08云与设计模式
  9. 09网络基础
  10. 10技术栈选型
  11. 11治理与方法论
  12. 12综合练习

安全架构

OWASP、PKI 与认证。

30 分钟

安全架构

这一课你将能做到

  1. 说出 OWASP Top 10 中至少 5 个风险,并指出本站每个风险对应的防护代码写在哪一层
  2. 为本站设计完整的认证方案:JWT 短期令牌 + Refresh Token 轮换 + Redis 黑名单
  3. 解释密码存储为什么要用 BCrypt 而不是 MD5,能写出正确的加密存储代码

为什么要学

安全是架构的「隐性需求」——没人会在需求文档里写「系统不能被脱库」,但出了事架构师第一个被问责。而且安全问题几乎不可逆:性能差了可以优化,数据泄露了就是永久事故。

人话:安全就像大楼的消防系统。平时感觉不到它的存在,验收时也不会因为它加分,但火灾发生时,有和没有的区别就是「损失一层楼」和「整栋楼烧光」。

先建立图像

本站的安全纵深防御体系:

                    攻击者
                      │
  ┌───────────────────▼───────────────────┐
  │ 第一层:边界安全                         │
  │  HTTPS(TLS) / WAF / 限流 / 防火墙        │
  ├────────────────────────────────────────┤
  │ 第二层:认证授权                         │
  │  JWT 校验 / RBAC 权限 / 接口防重放        │
  ├────────────────────────────────────────┤
  │ 第三层:应用安全                         │
  │  参数化查询 / XSS 转义 / CSRF Token      │
  ├────────────────────────────────────────┤
  │ 第四层:数据安全                         │
  │  密码 BCrypt / 敏感字段加密 / 脱敏返回    │
  └────────────────────────────────────────┘
  原则:任何一层被击穿,还有下一层兜底

跟着做

步骤 1:密码存储——从错误到正确

// ❌ 错误 1:明文存储(脱库即全军覆没)
user.setPassword("123456");

// ❌ 错误 2:MD5(彩虹表秒破,MD5 是摘要算法不是加密算法)
user.setPassword(DigestUtils.md5Hex("123456"));

// ❌ 错误 3:MD5 + 固定盐(盐泄露后依然可批量破解)
user.setPassword(md5("123456" + "static_salt"));

// ✅ 正确:BCrypt(慢哈希 + 每个密码随机盐 + 可调成本因子)
@Service
public class PasswordService {
    private final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(12);
    
    public String hash(String raw) { return encoder.encode(raw); }
    public boolean verify(String raw, String hash) { 
        return encoder.matches(raw, hash); 
    }
}

BCrypt 的关键设计:故意慢。MD5 算一次几微秒,BCrypt(cost=12) 算一次约 250ms——用户登录无感知,但攻击者暴力破解的成本提高百万倍。

步骤 2:JWT 认证完整方案

// 双令牌策略
public TokenPair login(LoginReq req) {
    User user = authenticate(req);
    // Access Token:15 分钟,无状态校验
    String accessToken = jwtUtil.create(user.getId(), Duration.ofMinutes(15));
    // Refresh Token:7 天,存 Redis,可吊销
    String refreshToken = UUID.randomUUID().toString();
    redis.set("refresh:" + refreshToken, user.getId(), 7, DAYS);
    return new TokenPair(accessToken, refreshToken);
}

// 刷新时轮换:旧 Refresh Token 立即作废(防重放)
public TokenPair refresh(String oldRefreshToken) {
    Long userId = redis.get("refresh:" + oldRefreshToken);
    if (userId == null) throw new UnauthorizedException();
    redis.delete("refresh:" + oldRefreshToken);  // 关键:一次性
    return issueTokens(userId);
}

// 登出/封号:Access Token 加入黑名单直到过期
public void logout(String accessToken) {
    long ttl = jwtUtil.getRemainingTtl(accessToken);
    redis.set("blacklist:" + accessToken, "1", ttl, SECONDS);
}

步骤 3:SQL 注入与 XSS 防护

// ❌ SQL 注入:字符串拼接
String sql = "SELECT * FROM course WHERE title LIKE '%" + keyword + "%'";
// 攻击者输入: ' OR '1'='1' --  直接全表泄露

// ✅ 参数化查询(MyBatis #{} 就是预编译)
@Select("SELECT * FROM course WHERE title LIKE CONCAT('%', #{keyword}, '%')")
List<Course> search(String keyword);

// XSS 防护:输出到页面时转义(Thymeleaf 默认转义,Vue 的 v-html 要小心)
// 富文本场景:用 Jsoup 白名单过滤
String safe = Jsoup.clean(userInput, Safelist.relaxed());

讲清楚要点

  1. 纵深防御(Defense in Depth):不要把安全寄托在单一措施上。WAF 防不住的注入,参数化查询兜住;JWT 被偷了,短期过期 + 黑名单兜住。
  2. 永远不要自己发明加密算法:用行业验证过的(BCrypt/Argon2 存密码、AES 加密、RSA 签名、TLS 传输)。自研加密几乎必然有漏洞。
  3. 认证(Authentication)≠ 授权(Authorization):认证是「你是谁」(登录),授权是「你能干什么」(RBAC 权限)。本站用户能看课程(认证后),但只有作者能编辑课程(授权判断)。
  4. 敏感数据最小化:能不收集就不收集,必须收集的就加密存储、脱敏展示。手机号返回 138****1234,日志里绝不打印密码、身份证、银行卡。
  5. 安全是流程不是产品:依赖组件定期扫描(OWASP Dependency-Check)、上线前渗透测试、密钥定期轮换、安全日志审计。一次性加固没有意义。

你可能会懵的地方

  • JWT 无状态不是挺好,为什么还要 Redis? 纯 JWT 无法主动吊销——token 签发后有效期内永远有效,用户被封号也拦不住。所以要么忍受短有效期(15 分钟)+ 刷新机制,要么牺牲无状态性加 Redis 黑名单。工程上通常两者结合。

  • HTTPS 都有了,密码还要加密吗? HTTPS 保护的是传输过程。服务器收到的是明文密码,如果日志、内存 dump、数据库里有明文或弱哈希,一样泄露。分层看:TLS 管传输,BCrypt 管存储,两者缺一不可。

  • CORS 是安全机制吗? 不是。CORS 是浏览器的「同源策略豁免」机制,保护的是用户(防止恶意网站盗用你的登录态调 API),不保护服务器。服务端自己的鉴权才是真正的防线,curl 发起请求根本不受 CORS 约束。

  • API 密钥(API Key)怎么设计? 生成:密码学随机数(SecureRandom)至少 32 字节;存储:服务端只存哈希,用户只展示一次;传输:放 Header(不放 URL,避免日志泄露);权限:每个 Key 绑定最小权限范围。

  • 什么是零信任架构? 传统安全是「内网可信、外网不可信」;零信任是「永不信任,始终验证」——即使服务间内网调用也要 mTLS 双向认证 + 细粒度授权。本站微服务化后,订单服务调课程服务也要带服务身份凭证。

错法对照

错法问题正解
MD5/SHA1 存密码彩虹表秒破BCrypt/Argon2,成本因子 ≥ 10
JWT 有效期设 30 天泄露后长期可用,无法吊销Access 15 分钟 + Refresh 轮换
前端校验就算安全防护curl 绕过一切前端校验前端校验为了体验,后端必须重校验
错误提示「密码错误」vs「用户不存在」泄露用户是否存在统一提示「用户名或密码错误」
密钥写死在代码里提交 Git历史记录永久泄露环境变量/配置中心 + Git 历史清洗

卡点急救

  1. 怀疑接口被刷:看 Nginx 日志统计 IP 频率 → 网关层按 IP/用户限流(令牌桶)→ 关键接口加图形验证码或滑块 → 恶意 IP 直接封禁。
  2. 发现密钥已提交到 GitHub:立即吊销该密钥(当作已泄露处理),用 BFG Repo-Cleaner 清洗历史,然后排查泄露期间的所有访问日志。
  3. JWT 被 XSS 偷走的风险:token 不存 localStorage(JS 可读,XSS 可偷),存 HttpOnly + Secure + SameSite 的 Cookie;同时全站严格 XSS 过滤。

面试常问

  • JWT 的优缺点?如何做吊销? 优点:无状态(服务端不存会话,易水平扩展)、跨域友好、自包含(减少 DB 查询)。缺点:无法主动吊销、payload 只编码不加密(不能放敏感信息)、token 比 sessionId 大。吊销方案:1) 短有效期 + Refresh Token;2) Redis 黑名单存未过期 token;3) 用户版本号(改密码后 version+1,旧 token 全部失效)。本站用 1+2 组合。

  • 如何防止 SQL 注入? 核心一条:绝不拼接 SQL,全部用参数化查询(预编译)。MyBatis 用 #{} 不用 ${}(${} 是直接拼接,只有动态表名/排序字段才用且必须白名单校验)。辅助:DB 账号最小权限(应用账号不给 DROP 权限)、WAF 兜底、输入长度限制。

  • 对称加密和非对称加密的区别和应用场景? 对称(AES):同一把钥匙加解密,快,适合大量数据;问题是怎么把钥匙安全给对方。非对称(RSA):公钥加密私钥解密,慢,适合小数据。实际用法是混合:HTTPS 里用 RSA 交换 AES 密钥,后续通信用 AES。本站场景:用户手机号用 AES 加密存库(密钥在 KMS),对外接口签名用 RSA。

自测三问

  1. 为什么 BCrypt 比 SHA-256 更适合存密码?核心设计差异是什么?
  2. 本站的 JWT Access Token 泄露后,在 15 分钟有效期内如何止损?
  3. 用户在评论区输入 <script>alert(document.cookie)</script>,从输入到渲染要经过哪几道防线?

小结一句话

安全架构的本质是纵深防御:假设每一层都会被突破,用传输加密、强哈希存储、短期令牌、最小权限构建多层兜底,并用流程保证防线持续有效。

延伸

  • OWASP Top 10 官方文档
  • 《白帽子讲 Web 安全》(吴翰清)
  • Spring Security 官方文档:OAuth2 与 JWT 章节
  • 下一课:云与设计模式——容器、CI/CD 与云原生设计模式。
加载本节练习…
← 上一课API 与集成下一课 →云与设计模式