token、cookie和session区别
Cookie
cookie 是一个非常具体的东西,指的就是浏览器里面能永久存储的一种数据,仅仅是浏览器实现的一种数据存储功能。
cookie由服务器生成,发送给浏览器,浏览器把cookie以kv形式保存到某个目录下的文本文件内,下一次请求同一网站时会把该cookie发送给服务器。由于cookie是存在客户端上的,所以浏览器加入了一些限制确保cookie不会被恶意使用,同时不会占据太多磁盘空间,所以每个域的cookie数量是有限的。
1,临时Cookie(会话Cookie) 2,永久Cookie 不设置过期时间,则表示这个cookie生命周期为浏览器会话期间,只要关闭浏览器窗口,cookie就消失了。 这种生命期为浏览会话期的cookie被称为会话cookie。会话cookie一般不保存在硬盘上而是保存在内存里。 设置了过期时间,浏览器就会把cookie保存到硬盘上, 关闭后再次打开浏览器,这些cookie依然有效直到超过设定的过期时间。 存储在硬盘上的cookie可以在不同的浏览器进程间共享,比如两个IE窗口。 而对于保存在内存的cookie,不同的浏览器有不同的处理方式。 Ps: cookie过期时间为0,马上过期,即会话,并非真的马上失效,浏览器关闭才失效。 当然cookie的有效性还得服务端检查。
Session
ession 从字面上讲,就是会话。这个就类似于你和一个人交谈,你怎么知道当前和你交谈的是张三而不是李四呢?对方肯定有某种特征(长相等)表明他就是张三。
session 也是类似的道理,服务器要知道当前发请求给自己的是谁。为了做这种区分,服务器就要给每个客户端分配不同的“身份标识”,然后客户端每次向服务器发请求的时候,都带上这个“身份标识”,服务器就知道这个请求来自于谁了。至于客户端怎么保存这个“身份标识”,可以有很多种方式,对于浏览器客户端,大家都默认采用 cookie 的方式。
服务器使用session把用户的信息临时保存在了服务器上,用户离开网站后session会被销毁。这种用户信息存储方式相对cookie来说更安全,可是session有一个缺陷:如果web服务器做了负载均衡,那么下一个操作请求到了另一台服务器的时候session会丢失。
cookie和session对比:
以下是两者和适用场所的特征比较。
1.不同的访问方法
只有ASCII字符串可以存储在cookie中。如果需要访问Unicode字符或二进制数据,则需要先对其进行编码。无法在cookie中直接访问Java对象。要存储稍微复杂的信息,使用cookie会更难。
会话可以访问任何类型的数据,包括但不限于字符串、Integer、List、Map等。 Session也可以直接存储Java Beans甚至任何Java类,对象等,使用起来非常方便。将Session视为Java容器类。
2.不同的隐私政策
Cookie存储在客户端阅读器中,对客户端可见。客户端上的某些程序可能会窥探、副本以更正cookie的内容。会话存储在服务器上,对客户端是透明的。不存在敏感信息泄露的风险。
如果您选择cookie,最好不要写敏感信息,如帐户密码。最好加密像Google、Baidu这样的cookie信息,并将其提交给服务器进行解密,以确保我自己可以读取cookie中的信息。如果选择Session,则可以省去很多麻烦。无论如何,它被放置在服务器上,并且会话中的任何隐私都可以得到有效保护。
3.有效期的差异
使用Google的任何人都知道,如果您已登录Google,Google的登录信息将长期有效。用户每次访问时都不必再次登录,Google会永久记录用户的登录信息。要达到这个效果,使用cookies将是一个不错的选择。只需将cookie的到期时间属性设置为一个非常大的数字。由于Session依赖于名为JSESSIONID的cookie,并且Cookie JSESSIONID的到期时间默认为-1,因此只需关闭阅读器就会使Session无效,Session将无法永久完成信息。无法使用URL地址重写。此外,如果设置Session的超时时间太长,服务器将累积的Sessions越多,导致内存溢出的可能性就越大。
4.不同的服务器压力
会话保留在服务器端,每个用户都将生成一个会话。如果有很多并发用户,它会产生大量的Session并消耗大量内存。因此,具有高并发流量的站点(如Google、Baidu、Sina)不太可能使用Session来跟踪客户会话。
Cookie保留在客户端上,不消耗服务器资源。如果有很多用户同时阅读,那么cookie是一个不错的选择。关于Google、Baidu、Sina,cookies可能是唯一的选择。
5.不同的浏览器支持
客户端浏览器需要支持Cookie。如果客户端禁用cookie或不支持cookie,则会话跟踪将无效。关于WAP上的应用程序,常规cookie无用。
如果客户端浏览器不支持cookie,则需要使用会话和URL地址重写。应该注意的是,Session程序中使用的所有URL都必须重写URL地址,否则会话会话跟踪将无效。对于WAP应用程序,会话+ URL地址重写可能是唯一的选择。
如果客户端支持cookie,则可以在浏览器窗口和子窗口中将cookie设置为有效(将到期时间设置为-1),或者可以将cookie设置为在所有阅读器窗口中有效(设置到期时间)大于1)0的整数。但是,Session只能在此阅读器窗口及其子窗口中使用。如果两个浏览器窗口彼此不相关,则它们将使用两个不同的会话。 (与IE8下不同窗口相关的会话)
6.跨域支持的差异Cookie支持跨域访问。例如,如果domain属性设置为“.biaodianfu.com”,则所有以“.biaodianfu.com”为后缀的域名都可以访问cookie。跨域cookie现在通常在网络上使用,例如Google、Baidu、Sina。会话不支持跨域访问。会话仅在其所在的域名内有效。
仅使用cookie或仅使用Sessions可能无法达到预期的效果。此时,您应该尝试使用Cookie和会话。 Cookie和Sessions的组合将在练习项目中实现许多意想不到的结果。
Token
token的意思是“令牌”,是用户身份的验证方式,最简单的token组成:uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,由token的前几位+盐以哈希算法压缩成一定长的十六进制字符串,可以防止恶意第三方拼接token请求服务器)。还可以把不变的参数也放进token,避免多次查库
传统身份验证
HTTP 是一种没有状态的协议,也就是它并不知道是谁是访问应用。这里我们把用户看成是客户端,客户端使用用户名还有密码通过了身份验证,不过下回这个客户端再发送请求时候,还得再验证一下。
解决的方法就是,当用户请求登录的时候,如果没有问题,我们在服务端生成一条记录,这个记录里可以说明一下登录的用户是谁,然后把这条记录的 ID 号发送给客户端,客户端收到以后把这个 ID 号存储在 Cookie 里,下次这个用户再向服务端发送请求的时候,可以带着这个 Cookie ,这样服务端会验证一个这个 Cookie 里的信息,看看能不能在服务端这里找到对应的记录,如果可以,说明用户已经通过了身份验证,就把用户请求的数据返回给客户端。
上面说的就是 Session,我们需要在服务端存储为登录的用户生成的 Session ,这些 Session 可能会存储在内存,磁盘,或者数据库里。我们可能需要在服务端定期的去清理过期的 Session 。
基于 Token 的身份验证
使用基于 Token 的身份验证方法,在服务端不需要存储用户的登录记录。大概的流程是这样的:
客户端使用用户名跟密码请求登录
服务端收到请求,去验证用户名与密码
验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端
客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据
APP登录的时候发送加密的用户名和密码到服务器,服务器验证用户名和密码,如果成功,以某种方式比如随机生成32位的字符串作为token,存储到服务器中,并返回token到APP,以后APP请求时,凡是需要验证的地方都要带上该token,然后服务器端验证token,成功返回所需要的结果,失败返回错误信息,让他重新登录。其中服务器上token设置一个有效期,每次APP请求的时候都验证token和有效期。
那么我的问题来了: 1.服务器上的token存储到数据库中,每次查询会不会很费时。如果不存储到数据库,应该存储到哪里呢。 2.客户端得到的token肯定要加密存储的,发送token的时候再解密。存储到数据库还是配置文件呢?
token是个易失数据,丢了无非让用户重新登录一下,新浪微博动不动就让我重新登录,反正这事儿我是无所谓啦。
所以如果你觉得普通的数据库表撑不住了,可以放到 MSSQL/MySQL 的内存表里(不过据说mysql的内存表性能提升有限),可以放到 Memcache里(讲真,这个是挺常见的策略),可以放到redis里(我做过这样的实现),甚至可以放到 OpenResty 的变量字典里(只要你有信心不爆内存)。
token是个凭条,不过它比门票温柔多了,门票丢了重新花钱买,token丢了重新操作下认证一个就可以了,因此token丢失的代价是可以忍受的——前提是你别丢太频繁,要是让用户隔三差五就认证一次那就损失用户体验了。
基于这个出发点,如果你认为用数据库来保持token查询时间太长,会成为你系统的瓶颈或者隐患,可以放在内存当中。
比如memcached、redis,KV方式很适合你对token查询的需求。
这个不会太占内存,比如你的token是32位字符串,要是你的用户量在百万级或者千万级,那才多少内存。
要是数据量真的大到单机内存扛不住,或者觉得一宕机全丢风险大,只要这个token生成是足够均匀的,高低位切一下分到不同机器上就行,内存绝对不会是问题。
客户端方面这个除非你有一个非常安全的办法,比如操作系统提供的隐私数据存储,那token肯定会存在泄露的问题。比如我拿到你的手机,把你的token拷出来,在过期之前就都可以以你的身份在别的地方登录。
解决这个问题的一个简单办法
1、在存储的时候把token进行对称加密存储,用时解开。
2、将请求URL、时间戳、token三者进行合并加盐签名,服务端校验有效性。
这两种办法的出发点都是:窃取你存储的数据较为容易,而反汇编你的程序hack你的加密解密和签名算法是比较难的。然而其实说难也不难,所以终究是防君子不防小人的做法。话说加密存储一个你要是被人扒开客户端看也不会被喷明文存储……
方法1它拿到存储的密文解不开、方法2它不知道你的签名算法和盐,两者可以结合食用。
但是如果token被人拷走,他自然也能植入到自己的手机里面,那到时候他的手机也可以以你的身份来用着,这你就瞎了。
于是可以提供一个让用户可以主动expire一个过去的token类似的机制,在被盗的时候能远程止损。
在网络层面上token明文传输的话会非常的危险,所以建议一定要使用HTTPS,并且把token放在post body里。
————————————————
版权声明:本文为CSDN博主「CharliChen」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/tobetheender/article/details/52485948