JavaWeb项目为什么我们要放弃原生Tomcat的Session/Cookie机制?

技术文章 专栏收录该内容
8 篇文章 0 订阅

前言

 
先说一下原生Tomcat的Session/Cookie的定义与机制。
 
Cookie和Session都为了用来保存状态信息,都是保存状态的机制,它们都是为了解决HTTP无状态的问题而所做的努力。
 
Cookie和Session有以下明显的不同点:
1)Cookie将状态保存在客户端(默认在本地的硬盘里),
Session将状态保存在服务器端(默认在tomcat的内存中,当然tomcat自己也有机制会往硬盘里放,session.ser。也有童鞋使用msm组件放到memcached里的);
 
2)网络服务器用HTTP头向客户端set-cookie,在客户终端,浏览器解析这些cookie并将它们保存为一个本地文件,保存在客户端本机的硬盘里,
它会自动将同一服务器的任何请求缚上这些cookie。
 
3)Session是针对每一个用户的,变量的值保存在服务器上,用一个sessionID来区分是哪个用户session变量。
 
4)session比cookie更安全些。
 

Tomcat如何实现Session

 
 
1.使用Cookie来实现
1.1浏览器第一次请求是不带sessionId的。
 
1.2第一次请求成功后,tomcat首先检查这个请求里是否已包含了JSESSIONID,
如果已包含一个JSESSIONID则说明以前已经为此浏览器创建过session,tomcat就按照JSESSIONID把这个session检索出来使用(如果检索不到,可能会新建一个),
如果客户端请求不包含JSESSIONID,则tomcat自动为此客户端创建一个session并且生成一个与此session相关联的JSESSIONID,tomcat会把session信息存储在内存中。
tomcat自动在响应消息中用Set-Cookie头将cookie的内容回送给浏览器,浏览器把JSESSIONID保存cookie中(本地磁盘)。
(JSESSIONID的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串)
 
1.3浏览器自动在后面新的请求中将刚才的 JSESSIONID携带在Cookie头中发送给tomcat,tomcat通过 JSESSIONID在内存中 找到这个客户端对应的Session内容,从而实现会话的保持。

 

 
 
2.使用URL回显来实现
URL回写是指tomcat在发送给浏览器页面的所有链接中都携带JSESSIONID的参数,这样客户端点击任何一个链接都会把JSESSIONID带回tomcat。如果直接在浏览器输入服务端资源的url来请求该资源,那么Session是匹配不到的。
 
tomcat对Session的实现,是一开始同时使用Cookie和URL回写机制,如果发现客户端支持Cookie,就继续使用Cookie,停止使用URL回写。如果发现Cookie被禁用,就一直使用URL回写。
 

为什么要放弃?

 
上面的机制是基于浏览器的,浏览器来帮我们自动维护这套session/cookie机制,浏览器自动发送JSESSIONID以及获取JSESSIONID。
那么好,我们的客户端除了浏览器以外还有什么?
ios/安卓,车机,javascript,程序等等。
 
那么问题来了,其他客户端会帮你维护这套机制吗?其他客户端会有cookie吗?
答案是不会,或者不全会,这样就会导致你的每次请求的JSESSIONID都会发生变化,
每次请求都会在服务端生成不同的JSESSIONID。
 
如果我们的一套服务,想被多种客户端所使用,就需要换一种玩法。
我们要自己实现这套机制,不再依赖浏览器,不再使用cookie/session机制。
 
此外,有过压测经历的同学应该会遇到过full GC问题,在高并发压测的时候,通过分析工具分析dump文件,
发现在JVM的堆内存里充斥着大量的Session对象,Session对象没有超时不会被GC回收内存,因此会触发频繁的full GC直到内存溢出。
 

如何放弃?

 
相关流程:
注意:本流程是使用浏览器作为客户端的情况下,其他客户端大同小异。
 
 
1.登录
前端把account和password,提交到服务端的api,服务端验证正确后,随机生成一个token(32位uuid),
并把token和user对象(user对象的属性包括userId,userName,userPhoto等等,key是token,value是user对象)存在缓存里(redis/memcached),
因为你的tomcat是分布式的集群,在负载均衡的时候你需要一个分布式的缓存,别忘了设置缓存的失效时间,然后把token与user对象返回给前端。 
 
前端需要把user对象和token保存在session storage/local storage中,注意session storage/ local storage 是html5的特性,
因此需要有浏览器兼容性问题,我们现在的解决方案是如果发现用户使用ie6,ie7这种老爷浏览器,
前端会跳转到让用户升级浏览器的页面,我们的网站不支持老的浏览器。
 
在随后的每次请求中,将token放到请求头里,自定义一个请求头,
前后端开发人员要制定好这个头的名字。
 
例如:
$.ajax({
    url:"xxx",
    // ...
    headers:{
        "XXX-Token":"1d496737b00b4c63a430ee291da564d2"
    }
});
 
 
2.前端查询当前登录人信息
在页面中需要查询当前登录人信息的地方,就可以直接从session storage/local storage中获取,
而不需要每次都去后台请求接口。
 
为什么不把user对象放在cookie中?
cookie不安全,不要存储用户敏感信息,且每次请求都会自动带上cookie,
导致每次请求的体积增大,不利于性能。
为什么不每次都去后端获取当前用户信息?
因为不利于性能,增加请求数量。
 
 
3.服务端加认证token的有效性 :
创建一个过滤器/拦截器/aop切面,拦截住需要状态的请求,把请求头中的token获取到。
例如:
String xToken = request.getHeader(" XXX-Token");
 
别忘了需要在web.xml添加新的请求头信息:
<init-param>
    <param-name>allowHeaders</param-name>
    <param-value>Content-Type, XXX-Token</param-value>
</init-param>
 
去缓存中查询这个token,如果能查询到则代表状态合法,继续往下放。
否则表示状态非法,返回错误码, 前端将页面跳转至登录页,并在前端清除老的token和user对象。
(黄勇:这是我们实践下来,最简单的解决 rest 安全的方案 。)
 
 
4.一个月内免登录
一般在登录时,会有复选框让用户选中是否支持一个月内免登录,如果用户选中,标识需要一个月内免登录,
后端在设置缓存的时候,设置超时时间为30天。
 
 
5.退出登录
清理掉session storage/local storage中的token和user对象。
清理掉缓存中的token。 
 
 
6.修改个人信息
修改个人信息的时候,需要先读取个人信息。
注意:此处要考虑是否有其他平台会更改用户信息,例如管理平台的用户管理可以修改个人信息,
如果有这种场景就要调用后端接口去查数据库来获取用户信息。
表单提交后,数据发生改变,接口返回后,需要把新的用户信息返回给前端,
前端同步到session storage/local storage中。
 
 
7.Java写法的变更
以前熟悉的session.getAttribute()/session.setAttribute()/session.invalidate(),
则全部变成了从缓存中保存/读取/销毁。
 
 
8.扩展阅读
8.1spring session
 
8.2无状态+防篡改的token参考:
jwt,json web token。

 
  • 9
    点赞
  • 14
    评论
  • 11
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 精致技术 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值