您的位置:首页 > 运维架构 > Tomcat

JavaWeb项目为什么我们要放弃原生tomcat的session/cookie机制?

2016-12-09 14:51 786 查看
前戏:先说一下原生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更安全些。

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机制

上面的机制是基于浏览器的,浏览器来帮我们自动维护这套session/cookie机制,浏览器自动发送JSESSIONID以及获取JSESSIONID。
那么好,我们的客户端除了浏览器以外还有什么?
ios/安卓,车机,javascript,程序等等。

那么问题来了,其他客户端会帮你维护这套机制吗?其他客户端会有cookie吗?
答案是不会,或者不全会,这样就会导致你的每次请求的JSESSIONID都会发生变化,
每次请求都会在服务端生成不同的JSESSIONID。

如果我们的一套服务,想被多种客户端所使用,就需要换一种玩法。
我们要自己实现这套机制,不再依赖浏览器,不再使用cookie/session机制。

相关流程:
注意:本流程是使用浏览器作为客户端的情况下,其他客户端大同小异。

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。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐