HTTP协议是无状态的服务,不会记录客户端的身份等基本信息,比如打开浏览器,访问一个网页50次和访问1次对于服务器来说没有区别。
一、Session、Cookie的作用
Session是客户端与服务器通讯会话跟踪的一门技术,可以使服务器与客户端保证整个通讯的会话基本信息。
客户端在第一次访问服务器的时候,服务端会响应一个sessionId,并且将它存入到客户端本地的cookie中,在之后的访问会将cookie中的sessionId放入到请求头中去访问服务器,如果服务器通过这个sessionId没有找到对应的数据,那么服务器会创建一个新的sessionId并且响应给客户端。
二、分布式Session存在的问题?
假设第一次访问服务A生成一个sessionId并且存入到cookie中,第二次却访问服务B(可能需要调用到服务器B上的某个模块),客户端会在cookie读取sessionId加入到请求头中,如果在服务器B通过sessionId没有找到对应的数据那么慢它创建一个新的sessionId并且将它返回给客户端,这样并并不能共享我们的sessionId。
解决方案:
(1)基于Cookie的Session共享
在大型网站中比较普遍使用。原理是将全站用户的Session信息加密、序列化后以Cookie的方式,统一存储到根域名下(比如:.host.com),利用浏览器访问该根域名下的所有二级域名站点时,会传递与之域名对应的所有Cookie内容的特性,从而实现客户端Cookie的Session在多服务器间的共享访问。
优点:
- 无需额外的服务器资源;
缺点:
- 由于受HTTP协议头信息长度的限制,仅能够存储小部分的用户信息,同时Cookie化的Session内容需要进行安全加解密(比如:采用DES、RSA等进行明文加解密;再由MD5、SHA-1等算法进行防伪认证), 此外它也会占用一定的带宽资源,因为浏览器会在请求当前域名下任何资源时将本地Cookie附加在HTTP头部中传递给服务器。
(2) 基于Redis的Session共享
该方案使用Redis来存储用户 的登录状态,Redis服务器也存在多做存储数据的淘汰策略,与Session的过期机制非常类似。目前是互联网公司最为广泛使用的方案之一。实现原理其实就是把每次用户的请求时候生成的sessionId存储在Redis上,然后在基于Redis的特性进行设置一个失效时间的机制,这样就能保证客户端在Redis中的session失效时间内,都不需要进行再次登录。
优点:
- 能适应于负载均衡策略;
- 服务器重启或宕机不会造成session丢失;
- 安全性较高;
- 扩展能力强;
- 适合集群数量大的使用;
缺点:
- 对应用有侵入,应加强相关配置;
- 增加一次网络开销,用户体验降低;
- 序列化和反序列化消耗CPU性能;
(3) 基于Nginx的ip_hash负载均衡
其实就是对请求过来的IP地址对N台服务器进行取模,然后就会把客户端的请求通过Nginx的反向代理给分发到对应的服务器上。(这里会把可用的服务器放到一个数组中,如果取模得到的结果是idx,就把请求分到服务器数组重点的下标为idx的服务器上)
优点:
- 配置简单,对应用无侵入性,不需要修改代码;
- 只要hash属性是均匀的,对台web-server的负载是均衡的;
- 便于服务器水平扩展;
- 便于服务器水平扩展;
- 安全性较高;
缺点:
- 服务器重启会造成部分session丢失;
- 水平扩展过程中也会造成部分session丢失;
- 存在单点负载过高的风险;
(4) 基于Tomcat的Session共享
当客户端请求的时候,把所产生的sessionId复制到系统中所有的服务器中,这样就能保证当请求的时候从服务器A可能调用到服务器B上的模块时,也能保证服务B也有该用户的sessionId,这样就不会再次让客户端进行再次登录操作。
三、Token的引入
- Token的引入:Token是在客户端频繁向服务端请求数据,服务端频繁的去数据库查询用户名和密码并进行对比,判断用户名和密码正确与否,并作出相应提示,在这样的背景下,Token便应运而生。
- Token的定义:Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。
- 使用Token的目的:Token的目的是为了减轻服务器的压力,减少频繁的查询数据库,使服务器更加健壮。
了解了Token的意义后,我们就更明确的知道为什么要用它了。
(1)如何使用Token?
1、用设备号/设备mac地址作为Token(推荐)
客户端:客户端在登录的时候获取设备的设备号/mac地址,并将其作为参数传递到服务端。
服务端:服务端接收到该参数后,便用一个变量来接收同时将其作为Token保存在数据库,并将该Token设置到session中,客户端每次请求的时候都要统一拦截,并将客户端传递的token和服务器端session中的token进行对比,如果相同则放行,不同则拒绝。
分析:此刻客户端和服务器端就统一了一个唯一的标识Token,而且保证了每一个设备拥有了一个唯一的会话。该方法的缺点是客户端需要带设备号/mac地址作为参数传递,而且服务器端还需要保存;优点是客户端不需重新登录,只要登录一次以后一直可以使用,至于超时的问题是有服务器这边来处理,如何处理?若服务器的Token超时后,服务器只需将客户端传递的Token向数据库中查询,同时并赋值给变量Token,如此,Token的超时又重新计时。
2、用session值作为Token
客户端:客户端只需携带用户名和密码登陆即可。
服务端端:服务端接收到用户名和密码后并判断,如果正确了就将本地获取sessionID作为Token返回给客户端,客户端以后只需带上请求数据即可。
分析:这种方式使用的好处是方便,不用存储数据,但是缺点就是当session过期后,客户端必须重新登录才能进行访问数据。
(2)、使用过程中出现的问题以及解决方案?
刚才我们轻松介绍了Token的两种使用方式,但是在使用过程中我们还出现各种问题,Token第一种方法中我们隐藏了一个在网络不好或者并发请求时会导致多次重复提交数据的问题。
该问题的解决方案:将session和Token套用,如此便可解决,如何套用呢?请看这段解释:
这就是解决重复提交的方案!