最近连续有好几个做毕设和找后端实习的朋友问我同一个问题:SpringBoot项目里做完用户登录之后,登录状态怎么总是不稳定?明明接口返回了登录成功,紧接着请求一个需要登录的页面,又提示未登录。还有人把问题想得特别玄,一上来就搬出JWT、Token、Redis,最后折腾半天发现只是Cookie路径或者跨域配置没搞定。
这些问题的根子,基本都在于对Cookie和Session的协作机制理解得不透。Cookie负责让浏览器把会话凭证带回来,Session负责在服务端记住用户是谁,两者配合才是完整的“登录状态保持”。既然这是Java后端几乎绕不开的知识点,这篇就以SpringBoot项目为主线,把登录提交、状态存储、拦截校验、失败排查以及分布式部署下的会话共享讲明白,代码可以直接抄,思路也尽量讲到能举一反三。
1. 登录状态保持的底层逻辑:Cookie负责带卡,Session负责记档
1.1 无状态的HTTP,需要一张“会员卡”才能认出你
HTTP协议是无状态的,意思是服务器处理完一个请求之后,默认不记得这个请求来自谁。登录之后你访问首页、访问个人中心,服务器其实并不认识你,除非请求里带了某种能被识别的凭证。
这就好比健身房办卡。你第一次前台登记身份证并交钱,系统里建立了你的会员记录,这是Session在做的事;但你每次进门不能重新报一次身份证号,于是前台发给你一张卡,你刷卡进门,卡号关联的就是系统里的会员记录。浏览器里的Cookie,就是那张卡。Session是存在服务端的用户会话数据,Cookie里真正保存的只是一个Session ID,大多数Java容器默认叫JSESSIONID。
整个链路是这样的:
- 浏览器提交用户名密码到
/login。 - 服务端校验通过后,创建HttpSession,把用户ID存进Session。
- 容器生成一个Session ID,并把它放到HTTP响应头里,
Set-Cookie: JSESSIONID=一段随机字符串。 - 浏览器收到后把这段Cookie保存在本地,后续请求自动带着它。
- 服务端收到请求时,从Cookie里拿到JSESSIONID,去内存里查询对应的Session,发现里面有用户ID,就知道“这个请求已经登录过了”。
这也是为什么Session和Cookie往往放
