写下流程:
用户打开浏览器,访问服务器
Tomcat创建一个sessionID,把他放进cookie里面,设置过期时间为浏览器关闭,再把他放进SessionManager实现类ManagerBase里的ConcurrentHashMap里面作为key,此时的value是null。浏览器带着cookie访问系统,session作为会话依据,开发者可以在session存储用户基本以及权限信息等,对会话做授权验证。
服务器程序调用request.getSession(true)(注意:request.getSession()=request.getSession(true)),request从cookie里面和URI里面找sessionID,根据sessionID从map里拿entity,自动创建一个session实例,此时value就不是null了。当然如果找不到sessionID,则返回null
所以sessionid和session是分开的,关闭浏览器,sessionid没了,但session还在内存里。
关闭服务器,session没了,但sessionid还在浏览器里。正常情况下,拿session是request的事情,程序员不用关心,但在那种分布式中,处理两次请求的服务器可能不是一台服务器,session就在另一台服务器内存里面,就拿不到session了,在那种对安全性稳定性要求特别高的系统里,服务器突然停了,session就没了,客户的某些重要信息就没了!为了解决这些问题,redis缓存session出现了!利用redis的hash散列表存储key:sessionID和value:session。拿session就不可以靠request.getSession()了。我们需要自己去cookie里面拿到sessionID,然后从redis里面拿session。不可以自己实现HttpServletRequest哦。Tomcat用的是自己实现的类,根据不用的请求报文,用不同的request实现类
那么什么时候存session呢,登录请求或主页请求的程序里,就可以从cookie里拿到sessionID,然后自己创建一个StandardSession实例(凡是带Standard的类大概率是Tomcat自己实现的),存到redis里面。可以自己实现一个session但不推荐。当然还是可以通过request.getSession(true)来创建session,这个最为稳妥了!但不可以通过这个方法来取session
这里面我们需要做的一个是存,一个是取,存很简单,但取需要用到ThreadLocal,ThreadLocal有什么用,成员变量在并发中是共享的,为了不让他共享,可以把他写在局部变量里,也可以用synchronized来局限,但局部变量会频繁初始化,synchronized太慢,所以ThreadLocal就是用来把成员变量变成非共享的数据。redis是数据库,数据库就需要缓存,缓存最简单的就是用ThreadLocal,这里用他来缓存session!减少对redis的查询频率。
sessionid作为一个会话的唯一凭据,服务器通过它来判断session是否生效,如果上来就访问一份restful接口,由于没有cookie里没有sessionid,tomcat会判断session失效,跳转到登录页面时会重写url,往后面加;jsessionid=xxx等,这个不安全,所以需要禁止重写url,很简单只需要@bean注入sessionmanager,自定义设置下即可,如
mySessionManager.setSessionIdUrlRewritingEnabled(false);
用session做权限有个问题由于sessionid是存储在cookie里,xss攻击会被泄露。
下面说下JWT(json web token)
相对于session,这个是目前非常常见的服务授权方式,分为有状态和无状态两种,有状态token和上面的session一样的东西,cookie存key,内存或自定义一个持久化工具(redis)存储key-session/token的散列表,所以就失去了token的意义了,无状态的token的传输则是写入header或者post body内给服务器,解决了跨域的问题,不会存储在服务器内,所以天然就是多服务器共享的,不需要持久化,全程有前端负责存储在LocalStorage或indexDB中,单点登录也会非常简单,不同的业务服务器只需要验证token即可。同时也是有一定缺点的,由于服务器未存储信息,所以当用户被管理员注销或用户主动注销时,按理来说这个用户就不可以再访问系统了,即token应该立即失效,但前端还持有token。一个方案是服务端在用户注销时,把注销的token保存在服务器内,这样再一次接收token就能知道这个token有没有效了。但是服务器还是得存储token,不理想,所以还有一个可行的方案是,注销时前端舍弃token,这样就行了。另外无状态token不保存在服务器里,所以在token到期前无法对token的验证信息做更改,以及权限更改,token有别于sessionid,它本身就包含了用户信息,可能会被泄露,结合以上两点token的过期时间不宜过长,减少盗用。另外使用token应该用https协议传输,减少盗用。下面是几个特性:
后端将JWT字符串作为登录成功的返回结果返回给前端。前端可以将返回的结果保存在localStorage或
sessionStorage上,退出登录时前端删除保存的JWT即可。
简洁(Compact):可以通过URL, POST 参数或者在 HTTP header 发送,因为数据量小,传输速度快
自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库
前端在每次请求时将JWT放入HTTP Header中的Authorization位。(解决XSS和XSRF问题)
后端拿到请求从header中获取相应的token,从token中获取过期时间判断token是否过期,没有过期就通过,过期就直接返回固定的值给前端,前端通过返回值知道token已经过期,重新请求token,再请求。
如有错误,请指正!