前言
在web中,我们经常说session,token,cookie。这三个内容,究竟啥区别,为什么会有这三个内容呢?这就是我们今天想要讨论的。
故事,美好的开始
洋洋: 琪琪,你们原先的开发 权限是怎么认证的?
琪琪: 权限?不就是拦截器嘛?通过拦截不同的url,进行拦截,然后进行校验。
洋洋: 那你们用过shiro嘛?进行权限校验的。
琪琪: 那个倒是没用过,我原先了解是用token做的,然后再造轮子写权限。
洋洋: 哦哦那你讲下session与token 是啥?我还是有点不明白呢,你们当初是怎么设计的?
故事开始了
既然要说 session 与token,顺便了解下cookie?
cookie
co哥: 该我出场提问了。第一个问题: 为什么要有cookie?
琪琪: 为什么要有呢?(小样,竟然问我这么简单的问题)。有它不就是为了存储web中的状态信息嘛,方便服务端使用嘛。
co哥:那cookie 一般是怎么拥有的?客户端可以创建嘛?
琪琪:我们都说cookie存在于客户端,如果不是篡改的问题,cookie第一次是服务器向客户端发送的。
co哥:你说的对,顺便再加一个就是我们的浏览器也会将cookie保存,并且每次请求后也把这些信息发送给服务端哦。
琪琪: 恩?那发送的是什么格式呢?(自问自答怎么样?)有什么限制吗?
琪琪: cookie吗? 简单说就是遵循name=value 格式的字符串。还有就是4kB的大小限制,cookie中其他内容就是可选项了。
琪琪: 还有一点就是set-cookie是在header中哦.
顺便提供下主要构成吗,大家可以看下。
name:一个唯一确定的cookie名称。通常来讲cookie的名称是不区分大小写的。
value:存储在cookie中的字符串值。最好为cookie的name和value进行url编码
domain:cookie对于哪个域是有效的。所有向该域发送的请求中都会包含这个cookie信息。这个值可以包含子域(如:yq.aliyun.com),也可以不包含它(如:.aliyun.com,则对于aliyun.com的所有子域都有效).
path: 表示这个cookie影响到的路径,浏览器跟会根据这项配置,像指定域中匹配的路径发送cookie。
expires:失效时间,表示cookie何时应该被删除的时间戳(也就是,何时应该停止向服务器发送这个cookie)。如果不设置这个时间戳,浏览器会在页面关闭时即将删除所有cookie;不过也可以自己设置删除时间。这个值是GMT时间格式,如果客户端和服务器端时间不一致,使用expires就会存在偏差。
max-age: 与expires作用相同,用来告诉浏览器此cookie多久过期(单位是秒),而不是一个固定的时间点。正常情况下,max-age的优先级高于expires。
HttpOnly: 告知浏览器不允许通过脚本document.cookie去更改这个值,同样这个值在document.cookie中也不可见。但在http请求张仍然会携带这个cookie。注意这个值虽然在脚本中不可获取,但仍然在浏览器安装目录中以文件形式存在。这项设置通常在服务器端设置。
secure: 安全标志,指定后,只有在使用SSL链接时候才能发送到服务器,如果是http链接则不会传递该信息。就算设置了secure 属性也并不代表他人不能看到你机器本地保存的 cookie 信息,所以不要把重要信息放cookie就对了
session 你来了?
wa: 既然有了cookie,那为什么还出来session了?这是做什么的?
session 生成
浏览器第一次访问服务器的时候,服务器会创建一个session,同时生成唯一的key,则是sessionID。
琪琪: 小黑板来了,我来提问了,第一次访问,那多个浏览器使用同一个账户信息,服务器不做其他限制,那是不是形成多个session呢?
wa: 当然了,每次访问都会形成一个新的sessionID,那就是session不一样了.这样相当于session 都保持这次会话访问的连接。
琪琪:会话访问的连接?那就代表有状态了哦。 那在后端是怎么把session返回给客户端的?
wa: 通过设置cookie的方式返回给客户端哦。当然在后端的时候我们也可以将session保存到数据库或者redis这些nosql中。
琪琪:我们通过cookie发送了。但有时候浏览器禁止cookie怎么办?
wa: 这个嘛?我想想…(你说你问这么多干嘛,还得费脑子)。可以通过URL重写的方式发送给服务器哦。
琪琪: 这也对。但还有个问题,如果我们后台有个服务器部署,属于分布式的,那我再其中一台登录了,称为A,session也保存到A中,万一下次我访问到另外一台服务器B怎么办?B上没有A的session呢。
wa: enenen ? 你问我?我也不知道。
le: 来来,我说下吧。可以这样嘛,既然每个服务器都有session,那我们就保存到库里面算了。这样我们就能互相通过访问过来的cookie 里拿到session信息与库中的进行对比。session 就共享了。
wa: 是哦,通过数据库或者nosql 进行处理了,但这样不就出现另外一个问题,我们需要保存这些数据,如果数量小点还好说数据量大了这些内容不也就带来性能问题?
le: 嗯呢,所以集群,出来了,所以每次保存这些session信息就是一个负担了。这就是我们需要在系统设计的时候考虑,状态信息需要保存嘛?
session的终结者,token你选择嘛?
session 是保存了会话的链接信息与我们需要传输的内容,容量大,还需要保持会话信息,每次同一用户不同客户端访问我都需要重新建立session,造成冗余信息大。
ff: 这该怎么办呢,真不想保存。
琪琪:这还不简单,那就不保存了,采用token吧?
ff: token? 那是个啥?
琪琪: ennnn,那不是啥。那是一个新技术,被称为令牌的东西。
ff: 那token能验证用户的合法性嘛?
琪琪: …当然,比如用户登录了,我们会把令牌信息,发送给他,包含了一个他的userId,代表他的信息,再访问的时候通过header或者bady带过来就好。
ff: 感觉与sessionID没有区别呢? 咦,貌似不用存储session信息到库里面了,还有每次不同的会话创建新的token,也不影响(单点登录貌似可以通过token做哦,做过的小伙伴可以尝试下)。好神奇。
琪琪: 是的,是不是感觉token比session好多了?
ff: ennnn,但这不是给黑客创造了伪造的 token信息嘛?怎么防止这些信息呢?
琪琪: 这还不简单,对咱们返回的token里面数据做一个签名,秘钥别人不知道,这样伪造的token不就通不过我们的限制了嘛。加密算法可以采用HMAC-SHA256这些算法了etc.
ff :神奇的token啊。这就解决session 这些问题了。
session与token 故事完了吗?
嘿嘿,怎么能结束呢,只说了理论,但还有很多问题没解决呢。就让琪琪一个个提问吧。
wa: 先不提问,我再说几个结论,在提问。。
- token 是无状态的,后端不需要记录信息,每次请求每次解密就行。
- session 是有状态的,需要后端每次去检索id的有效性。不同的session都需要进行保存哦。但让也可以设置单点登录,减少保存的数据。
- session与token的问题是空间与时间博弈,为什么这么说呢,是因为token不需要保存,直接获取,每次访问都需要进行解密。
好了就先说这几条吧,以后有了在补充。
琪琪:总结了这么多了,那我们开始提问了。首先,为啥客户端ios与Andriod基本上没见过用session的?
ff: 这个我来回答吧。因为在这些客户端上啊,原生接口都是每一次建立一个会话,这就出问题了,这样会导致登录功能失效了,登录每次把信息放到session中,session都不一样了,每次登录都成新的一个人了,这就不ok了。
琪琪:哦哦原来客户端是每次用原生接口都是新建立一个会话,好尴尬的设计。那我们采用什么方式解决这个问题呢?
ff: 在用户登录后,重点哦我们可以通过cookie嘛,我们在app端也可以是存储cookie的,我们知道cookie将sessionID保存好,返回给客户端,服务器最后也是通过SessionId来标识的。但是利用token的话,内容我们可以自定义,并且不用再服务端进行保存。方便我们处理。
琪琪:那么就是token可以保存到cookie中,如果禁止的话我们也可以保存到body中每次都请求上或者header中。
总结
恩,token,cookie,session的内容就到这里的,不同的方案都有各个的区别,当然从这里看我门了解的还是不不够多,需要继续努力
欢迎大家关注公众号LuckQI.