阶段:框架篇 | 用法:todo-list 项目做到「能登录、能鉴权」时学这一篇;面试安全题(JWT/CSRF/密码存储)基本都在这里。先读完再动项目,安全方案别自己发明。
学习目标
- 能画出一个「账号密码登录 → 发 Token → 后续请求带 Token → 网关/拦截器校验」的完整时序。
- 说清 Cookie/Session/JWT 三者的取舍,以及分布式下 Session 共享的三种方案。
- 记住密码存储铁律:加盐慢哈希(BCrypt),以及为什么「忘记密码只能重置不能找回」。
- 能解释 XSS / CSRF / SQL 注入 三种攻击的原理与防御各一条,且不混淆。
- 理解 RBAC 权限模型,能设计「用户-角色-权限-资源」四张表。
核心知识点
1. 认证 vs 鉴权(先分清这两个词)
- 认证 Authentication:你是谁——校验凭据(密码/短信/扫码/OAuth)。
- 鉴权 Authorization:你能干什么——校验权限(这个角色能不能删这条订单)。
- 一句话:认证是门卫查证件,鉴权是前台刷卡开闸。Spring Security 的两大组件一一对应(
AuthenticationManager / AccessDecisionManager 或新版 AuthorizationManager)。
2. 会话方案演进:Cookie+Session → Token → JWT ⭐
| 方案 | 状态存哪 | 扩容 | 跨域 | 注销 |
|---|
| Session(Cookie 传 JSESSIONID) | 服务端内存 | 要共享(见下) | 同域才行 | 服务端删,立即生效 |
| 集中式 Session(Redis) | Redis | 天然共享 | 同上 | 立即生效 |
JWT Token(放 Authorization: Bearer) | 不存(自包含) | 无状态最省事 | 无限制 | 难,需黑名单/短过期 |
- JWT 结构:
header.payload.signature,Base64URL 编码——是签名不是加密,payload 里别放敏感信息,任何人都能 base64 解开看。
- HS256(对称,单服务自用)vs RS256(非对称,签发方私钥签、验证方公钥验,微服务/开放平台用)。
- 过期与刷新:Access Token 短期(15min
2h)+ Refresh Token 长期(730 天,存库可吊销)。口径:JWT 的无状态是用「吊销难」换来的。
- 常见坑:
alg: none 绕过验签(库要关支持)、密钥泄露等于系统沦陷、把 JWT 当加密信封。
- 分布式 Session 三方案:粘滞会话(同一 IP 固定路由到同一台,扩容即乱)、Session 复制(集群广播,小集群才用)、集中存储(Spring Session + Redis,事实标准)。
3. 密码与敏感数据 ⭐
- 铁律:服务端永远不存明文密码,也不存可逆密文。忘记密码只能重置不能告诉你原密码——因为存的是哈希,本来就无法还原(这正是安全的证明)。
- 正确姿势:BCrypt(或 Argon2/PBKDF2),自带随机盐、慢哈希抗暴力。
encoder.encode(raw) 一次一盐,比对用 encoder.matches(raw, encoded)。
- 三个概念别混:
- 哈希 Hash:单向,不可逆 → 存密码用它(MD5/SHA1 太快来不及加盐,禁用)。
- 加密 Encryption:双向有密钥 → AES 存要还原的数据(手机号、API key),RSA 公加密私解密。
- 编码 Encoding:无损格式转换(Base64/URL-encode)→ 不是安全手段,Base64 不是加密是常识题。
- 加盐(Salt)作用:同密码不同哈希,破彩虹表攻击;慢哈希作用:把 GPU 爆破成本拉高 1000 倍。
- 脱敏:日志和返回里的手机号
138****1234、身份证、银行卡——序列化时处理(Jackson 注解或 MyBatis TypeHandler),别在业务代码里手搓 substring。
4. 三种经典攻击与防御 ⭐
| 攻击 | 原理一句话 | 发生地 | 防御 |
|---|
| SQL 注入 | 用户输入拼进 SQL 改变了语义(' or 1=1 --) | 数据库层 | 预编译 #{}(MyBatis)、最小权限 DB 账号、WAF 兜底 |
| XSS 跨站脚本 | 恶意 JS 注入页面,偷 Cookie/冒充操作 | 浏览器 | 输出转义、HttpOnly Cookie、CSP、前端框架默认转义 |
| CSRF 跨站请求伪造 | 借用户在目标站的已登录 Cookie,诱导浏览器发伪造请求 | 浏览器 | SameSite Cookie、CSRF Token、校验 Origin/Referer |
- 记忆锚点:SQL 注入是「数据被当代码执行」,XSS 是「数据在浏览器被当代码执行」,CSRF 是「请求冒充了合法用户」。
- CSRF 只对「自动携带凭据(Cookie)」的方案有效,Header+JWT 方案天然免疫大半(浏览器不会自动带 Authorization 头)——这是面试加分点。
- SQL 注入防御呼应《MyBatis》的
#{} vs ${}:${} 就是字符串拼接,order by 场景不得不用 ${} 时要做白名单校验。
5. RBAC 权限模型
- 模型:用户 → 角色 → 权限 → 资源,用户不直接挂权限,一律通过角色中转(换岗位只改角色不改人)。
- 四张核心表:
user、role、permission、user_role、role_permission(多对多两张中间表)。
- 落地分层:前端按钮显隐(体验层,不是安全)+ 后端接口鉴权(安全层,缺一不可)。接口上用注解(Spring Security
@PreAuthorize("hasRole('ADMIN')") 或自研拦截器)。
- 面试一句话:「前端控制是体验,后端控制才是安全」。
6. 第三方登录(OAuth2 速览)
- 场景:微信/GitHub 授权登录。角色:资源所有者(用户)、客户端(你的应用)、授权服务器(微信)、资源服务器(微信 API)。
- 授权码模式(最常用)时序:跳授权页 → 用户同意 → 回调带
code → 你的后端用 code+client_secret 换 access_token → 拿用户信息。
- 关键点:code 走前端、token 换在后端(client_secret 绝不能放前端);state 参数防 CSRF。
- 了解即可,别展开实现——面试问到说出时序就够。
动手实践
任务 1:给 todo-list 加登录发 JWT
- 要求:
POST /login(用户名+密码,BCrypt matches 校验)→ 签发 JWT(含 userId、role,过期 2h);写一个拦截器:Authorization: Bearer 验签+验过期,失败统一返回 401;除 /login 外全部接口拦截。
- 验收标准:不带 token 访问受保护接口返回 401;带篡改过的 token 返回 401;把过期时间调成 30 秒,等待后访问能复现 401;能说出为什么 payload 里不放手机号。
任务 2:亲手玩一次 SQL 注入与防御
- 要求:先用
${} 拼接写一个查询接口,用 ' OR '1'='1 复现全表泄露;再改 #{} 验证免疫;对「按列名动态排序」参数做白名单(只允许 id|create_time|priority)。
- 验收标准:能演示注入前后差异;白名单外的列名传入直接 400;说出预编译防御的原理(SQL 结构与参数分离,参数永远是数据)。
任务 3:RBAC 四表 + 一个接口级鉴权
- 要求:建五张表(user/role/permission+两张中间表);登录时把角色写进 JWT payload;
DELETE /todos/{id} 只允许 ADMIN 角色;普通用户删除自己的记录放行(数据级权限,与功能级权限的区别)。
- 验收标准:ADMIN 删除任意记录 200,USER 删别人的 403、删自己的 200;能说出「功能级权限」与「数据级权限」分别拦在哪一层。
高频面试题
推荐资料