Spring MVC会话属性安全性及Token存入会话的方案合理性分析
这是个很实际的问题,尤其是在传统Spring MVC和REST服务混合的架构里,咱们来好好捋一捋细节。
一、Spring MVC会话属性的安全性基础
Spring MVC里的会话属性(也就是存在HttpSession里的数据)本身的安全性,主要取决于会话管理的配置和服务器环境的安全性:
- 服务器端存储:会话属性默认存在服务器内存(或你配置的会话存储如Redis)里,只要服务器没有被入侵,这些数据是不会被外部直接获取的。
- 会话ID传输:如果你的应用用了HTTPS,会话ID(存在Cookie的
JSESSIONID里)会通过加密通道传输,能有效防止中间人劫持;如果是HTTP,会话ID很容易被窃取,进而被攻击者冒充用户。 - 会话防护:如果集成了Spring Security,它默认会做会话固定攻击防护(比如用户登录后自动更换会话ID),能避免攻击者提前获取会话ID后诱骗用户登录的风险。
- 超时配置:合理设置会话超时时间(比如30分钟无操作就过期),能缩小会话被滥用的时间窗口。
不过要注意:会话属性本身没有默认加密,如果服务器被攻破,攻击者能直接读取会话存储里的Token,这是底层风险。
二、你的方案是否可行?
从流程上来说,这个方案是完全可行的:
- 前端通过Ajax调用REST服务获取Token,后端把Token存入
HttpSession; - JSP通过EL表达式
${sessionScope.myToken}取出Token,嵌入到Ajax请求头里; - 后续Ajax请求带着这个Token调用REST服务,服务端验证Token合法性。
很多早期的Spring MVC项目在前后端未完全分离时,都会采用类似的方案来衔接传统页面和REST接口。
三、方案存在的核心缺陷
虽然可行,但这个方案有几个不容忽视的问题,尤其是安全性和扩展性方面:
- XSS攻击的高风险:这是最致命的问题。你把Token从会话里取出来渲染到JSP页面中(哪怕是藏在JS的请求头配置里),只要页面存在XSS漏洞(比如用户输入未做过滤就渲染),攻击者注入的恶意脚本就能直接获取到这个Token,进而用它冒充用户调用所有有权限的REST接口。毕竟Token是认证凭证,拿到它就等于拿到了用户的权限。
- 浪费REST服务的无状态优势:你的REST服务本来是支持Token认证的,Token(比如JWT)本身可以是无状态的——服务端不需要存储Token,只要验证签名和有效期即可。但你把Token存入会话后,就把无状态的服务变成了有状态,后续如果要做集群部署,必须配置会话共享(比如Redis),增加了架构复杂度。
- Token与会话生命周期不同步:Token本身通常有自己的过期时间(比如1小时),而会话的超时时间可能更长或更短。如果Token过期了但会话还没过期,前端会带着无效Token请求服务,导致失败;如果会话过期了但Token还有效,用户必须重新登录获取Token,影响体验。
- 前端暴露Token的风险:哪怕没有XSS漏洞,用户打开浏览器开发者工具,能直接看到请求头里的Token。如果是在共享设备上,其他用户可以通过这种方式窃取Token,冒充原用户操作。
四、一些改进建议
如果要优化这个方案,推荐几个方向:
- 不让Token出现在前端页面:后端在会话中存储Token,前端只需要通过Cookie携带
JSESSIONID(开启HttpOnly和Secure属性,防止JS读取和HTTP传输),后端收到请求后从会话中取出Token,再去调用REST服务。这样Token完全不会暴露到前端,从根源上避免XSS窃取Token的风险。 - 强化会话Cookie的安全性:在Spring配置里把
JSESSIONID的Cookie设置为HttpOnly=true和Secure=true,同时开启Spring Security的会话固定防护,最大程度降低会话ID被窃取的风险。 - REST服务结合CSRF防护:如果必须让前端携带Token,一定要给REST服务加上CSRF验证(比如Spring Security的
CsrfToken),防止攻击者通过CSRF攻击冒用用户会话发起请求。 - 改用JWT+HttpOnly Cookie:如果REST服务支持JWT,直接把JWT存入
HttpOnly、Secure的Cookie中,前端Ajax请求时浏览器会自动带上这个Cookie,后端验证JWT即可。这种方式既保持了REST服务的无状态,又避免了Token暴露到JS里,安全性和扩展性都更好。
内容的提问来源于stack exchange,提问作者Nikita Mikhailov
相关产品推荐
相关产品推荐

