[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-spring-session-auth-design":3,"article-neighbors-spring-session-auth-design":18,"article-related-spring-session-auth-design":40},{"id":4,"slug":5,"title":6,"excerpt":7,"category":8,"tags":9,"content":14,"publishedAt":15,"readingMinutes":16,"published":17},25,"spring-session-auth-design","SpringBoot 前后端分离 JWT 认证完整落地与坑点","JWT 无状态鉴权落地不难，难的是把密钥、过期时间、敏感数据和全局拦截这些坑都填平。","工程实践",[10,11,12,13],"Spring Boot","安全","认证","PostgreSQL","前后端分离的项目做认证，第一个想到的往往是 JWT。我用它做了几个项目，今天把落地过程和一些踩过的坑一起写下来。\n\n先说说为什么不用传统的 Session。Session 会话数据存在服务端内存里，一旦上分布式集群，就得靠 Redis 做会话同步，架构一下就复杂了；跨域请求又不会自动带 SessionId，前后端分离下适配很麻烦；多端登录（网页、小程序、App）更是撑不住。这些都是 Session 的天然短板。\n\n### JWT 到底是怎么工作的\n\nJWT 的卖点是\"无状态\"：用户身份、权限、过期时间这些信息加密打包在令牌里，服务端不用存会话，只用密钥校验签名。校验通过就放行，不通过就拒绝，多端、分布式都好使。\n\n它的结构是三段式：Header（声明算法）、Payload（存放用户信息）、Signature（签名）。Header 和 Payload 只是 Base64 编码，签名才是关键——它是用密钥对前两段做 HMAC（HS256）或 RSA（RS256）签出来的。服务端拿到令牌，重新算一遍签名，对得上就说明令牌没被篡改过。需要说清楚的是，Payload 是明文，不是加密，任何人都能解出来看。\n\n这里顺带说下 HS256 和 RS256 的区别：HS256 是对称加密，签发和校验用同一个密钥，简单但密钥要保密，适合内部系统；RS256 是非对称，私钥签发、公钥校验，适合多服务或第三方要验签的场景。个人站用 HS256 就够，但别把密钥写进代码。\n\n落地流程其实不复杂：登录验证账号密码 → 生成令牌 → 前端每次请求放 `Authorization` 头里 → 后端过滤器校验签名 → 通过后放行。闭环就这五步。\n\n### 四个必须避开的坑\n\n但坑也基本都集中在\"不规范\"三个字上，我一个个说。\n\n第一，密钥千万别硬编码在代码里。密钥一泄露，谁都能伪造令牌，等于整个认证体系裸奔。必须配置化、托管在环境变量里，最好用环境管理工具统一管理。\n\n第二，Token 过期时间别贪长。JWT 有个硬伤——签发之后没法主动作废。过期时间设太长，账号被盗了你也吊销不了，只能干瞪眼。所以业界通用做法是双令牌：短时效的 AccessToken（比如 15 分钟）负责日常请求，长时效的 RefreshToken（比如 7 天）负责续期，RefreshToken 走服务端保存，这样至少能吊销。这套\"双令牌 + 刷新\"机制，是生产环境的基本功。还有个细节：AccessToken 过期后，前端要能自动用 RefreshToken 换新的，不能一过期就让用户重新登录。\n\n第三，Payload 里别塞敏感数据。JWT 的 Payload 只是 Base64 编码，不是加密，任何人拿到都能解出来。身份证号、手机号这些千万别往里面放，只放用户 ID 这种非敏感标识。\n\n第四，接口一定要全局拦截。我见过有项目只拦了一部分接口，管理后台的接口裸奔在公网上，随便调。所有需要鉴权的接口必须统一走过滤器，一个都不能漏。可以做个注解 + AOP 的方式，给需要鉴权的接口打标记，统一进拦截逻辑。还要注意\"放行名单\"要小——只有登录、注册、验证码这类接口能放行，其他全部进拦截。\n\n### 别忘了配合的安全措施\n\n除了上面四条，还有几个配合措施值得做：一是 CSRF 防护，如果前端用 Cookie 存令牌，要防跨站请求伪造，用 `SameSite` 属性 + CSRF Token；二是加登录失败限流，防止暴力破解，比如同 IP 一分钟内失败超过 5 次就锁一会；三是给接口配 HTTPS，别让令牌在网络上裸奔；四是记录登录审计日志，出问题能溯源。\n\n最后补一句：JWT 的\"无状态\"也别过度美化。真正无状态带来的就是吊销困难，所以生产项目往往还是要服务端存一份 RefreshToken 或会话状态。选 Session 还是 JWT，取决于你有多端需求、是否分布式部署、是否必须主动吊销——没有绝对谁更好，只有适不适合你的场景。对个人站这种单服务、少并发的场景，用 JWT + 双令牌已经是很稳的组合了。\n","2025-08-20T02:00:00Z",6,true,{"prev":19,"next":30},{"id":20,"slug":21,"title":22,"excerpt":23,"category":24,"tags":25,"publishedAt":29,"readingMinutes":16},37,"data-circulation-platform-retrospective","数据流转确权平台｜数据模型、权限粒度、迭代重构问题","数据授权一次就拷贝一份副本，权限又粗到没法用——这个 B 端项目的前期债，后期还得很痛。","项目复盘",[26,27,28,24],"Vue","前端开发","业务系统","2025-07-10T02:00:00Z",{"id":31,"slug":32,"title":33,"excerpt":34,"category":8,"tags":35,"publishedAt":39,"readingMinutes":16},30,"frontend-security-is-not-ui-permission","前端权限安全：为什么仅隐藏 DOM 是无效安全","前端隐藏按钮只是体验优化，安全必须全部下沉到服务端，否则就是给攻击者留后门。",[36,37,38],"前端安全","权限","接口设计","2025-10-15T02:00:00Z",[41,50,60],{"id":42,"slug":43,"title":44,"excerpt":45,"category":8,"tags":46,"publishedAt":49,"readingMinutes":16},1,"build-a-reliable-personal-site","个人网站工程架构与长期可维护设计","这个站从零搭起来，分享我用 Nuxt 加 Spring Boot 把个人项目做成敢一直改下去的经过。",[47,10,48],"Nuxt","部署","2026-07-20T02:00:00Z",{"id":51,"slug":52,"title":53,"excerpt":54,"category":8,"tags":55,"publishedAt":59,"readingMinutes":16},32,"personal-site-feature-bloat","个人项目如何规避「功能废墟」工程问题","功能越加越多，项目却越来越难改，最后重构比重写还贵——这是我踩过的\"功能废墟\"坑。",[56,57,58],"个人网站","工程管理","维护","2026-05-15T02:00:00Z",{"id":61,"slug":62,"title":63,"excerpt":64,"category":8,"tags":65,"publishedAt":69,"readingMinutes":16},24,"vue-request-cache-implementation","Vue 全局请求缓存工程级实现、过期策略与脏数据规避","给 Vue 项目加全局缓存时踩过的坑：过期策略、脏数据、并发去重，一篇讲清楚。",[26,66,67,68],"TypeScript","缓存","性能","2026-01-25T02:00:00Z"]