Session 、Cookie和Token三者的关系和区别

HTTP 是一个无状态协议,所以客户端每次发出请求时,下一次请求无法得知上一次请求所包含的状态数据,如何能把一个用户的状态数据关联起来呢?

比如在淘宝的某个页面中,你进行了登陆操作。当你跳转到商品页时,服务端如何知道你是已经登陆的状态?

首先产生了 cookie 这门技术来解决这个问题,cookie 是 http 协议的一部分,它的处理分为如下几步:

服务器向客户端发送 cookie。

通常使用 HTTP 协议规定的 set-cookie 头操作。

规范规定 cookie 的格式为 name = value 格式,且必须包含这部分。

浏览器将 cookie 保存。

每次请求浏览器都会将 cookie 发向服务器。

其他可选的 cookie 参数会影响将 cookie 发送给服务器端的过程,主要有以下几种:


path:表示 cookie 影响到的路径,匹配该路径才发送这个 cookie。

expires 和 maxAge:告诉浏览器这个 cookie 什么时候过期,expires 是 UTC 格式时间,maxAge 是 cookie 多久后过期的相对时间。
当不设置这两个选项时,会产生 session cookie,session cookie 是 transient 的,当用户关闭浏览器时,就被清除。
一般用来保存 session 的 session_id。

secure:当 secure 值为 true 时,cookie 在 HTTP 中是无效,在 HTTPS 中才有效。

httpOnly:浏览器不允许脚本操作 document.cookie 去更改 cookie。
一般情况下都应该设置这个为 true,这样可以避免被 xss 攻击拿到 cookie。

cookie机制是采用在客户端保持状态的方案(cookie的作用就是为了解决HTTP协议无状态的缺陷所作的努力)。cookie是保存在本地终端的数据。cookie由服务器生成,发送给浏览器,浏览器把cookie以kv形式保存到某个目录下的文本文件内,下一次请求同一网站时, 按照一定的原则在后台自动把该cookie发送给服务器(浏览器检查所有存储的cookie,如果某个cookie所声明的作用范围大于等于将要请求的资源所在的位置,则把该cookie附在请求资源的HTTP请求头上发送给服务器)。由于cookie是存在客户端上的,所以浏览器加入了一些限制确保cookie不会被恶意使用,同时不会占据太多磁盘空间,所以每个域的cookie数量是有限的。

cookie的内容主要包括:名字、值、过期时间、路径和域。

路径与域一起构成cookie的作用范围。

若不设置过期时间,则表示这个cookie的生命期为浏览器会话期间,关闭浏览器窗口,cookie就消失。这种生命期为浏览器会话期的cookie被称为会话cookie.会话cookie一般不存储在硬盘上而是保存在内存里,当然这种行为 并不是规范规定的。

若设置了过期时间,浏览器就会把cookie保存在硬盘上,关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间。存储在硬盘上的cookie可以在不同的浏览器进程间共享,比如两个IE窗口。而对于保存在内存里cookie,不同的浏览器有不同的处理方式。

cookie的组成有:名称\(key\)、值\(value\)、有效域\(domain\)、路径\(域的路径,一般设置为全局:"\"\)、失效时间、安全标志\(指定后,cookie只有在使用SSL连接时才发送到服务器\(https\)\)。下面是一个简单的js使用cookie的例子:

用户登录时产生cookie:

document.cookie = "id="+result.data['id']+"; path=/";

document.cookie = "name="+result.data['name']+"; path=/";

document.cookie = "avatar="+result.data['avatar']+"; path=/";

使用到cookie时做如下解析:

var cookie = document.cookie;var cookieArr = cookie.split(";");var user_info = {};for(var i = 0; i < cookieArr.length; i++) {

user\_info\[cookieArr\[i\].split\("="\)\[0\]\] = cookieArr\[i\].split\("="\)\[1\];

}

$('#user_name').text(user_info[' name']);

$('#user_avatar').attr("src", user_info[' avatar']);

$('#user_id').val(user_info[' id']);

Session机制

session的中文翻译是“会话”,当用户打开某个web应用时,便与web服务器产生一次session。服务器使用session把用户的信息临时保存在了服务器上,用户离开网站后session会被销毁。这种用户信息存储方式相对cookie来说更安全,可是session有一个缺陷:如果web服务器做了负载均衡,那么下一个操作请求到了另一台服务器的时候session会丢失。

session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。

当程序需要为某个客户端的请求创建一个session时,服务器首先检查这个客户端的请求里是否已包含了一个session标识(称为session id),如果已包含则说明以前已经为此客户端创建过session,服务器就按照session id把这个session检索出来使用(检索不到,会新建一个),如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以伪造的字符串,这个session id将被 在本次响应中返回给客户端保存。保存这个session id的方式可以采用cookie,这样在交互过程中浏览器 可以自动的按照规则把这个标识发送给服务器。一般这个cookie的名字就是类似于SESSIONID。

由于cookie可以被人为的禁止,必须有其他机制以便在cookie被禁止时仍然能够把session id传递回服务器。

两种方式:

第一种:URL重写(常用),就是把session id直接附加在URL路径的后面。

第二种:表单隐藏字段(现已很少使用)。就是服务器会自动修改表单,添加一个隐藏字段,以便在表单提交时能够把session id传递回服务器。

Token

token的意思是“令牌”,是用户身份的验证方式,最简单的token组成:uid\(用户唯一的身份标识\)、time\(当前时间的时间戳\)、sign\(签名,由token的前几位+盐以哈希算法压缩成一定长的十六进制字符串,可以防止恶意第三方拼接token请求服务器\)。还可以把不变的参数也放进token,避免多次查。

cookie和session的区别

1、cookie数据存放在客户的浏览器上,session数据放在服务器上。

2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗
考虑到安全应当使用session。

3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能
考虑到减轻服务器性能方面,应当使用COOKIE。

4、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。

5、所以个人建议:
将登陆信息等重要信息存放为SESSION
其他信息如果需要保留,可以放在COOKIE中

token 和session的区别

session和oauth token并不矛盾,作为身份认证token安全性比session好,因为每个请求都有签名还能防止监听以及重放攻击,而session就必须靠链路层来保障通讯安全了。如上所说,如果你需要实现有状态的会话,仍然可以增加session来在服务器端保存一些状态。

App通常用restful api跟server打交道。Rest是stateless的,也就是app不需要像browser那样用cookie来保存session,因此用session token来标示自己就够了,session/state由api server的逻辑处理。如果你的后端不是stateless的rest api,那么你可能需要在app里保存session.可以在app里嵌入webkit,用一个隐藏的browser来管理cookie session.

Session是一种HTTP存储机制,目的是为无状态的HTTP提供的持久机制。所谓Session认证只是简单的把User信息存储到Session里,因为SID的不可预测性,暂且认为是安全的。这是一种认证手段。

而Token,如果指的是OAuth Token或类似的机制的话,提供的是认证和授权 ,认证是针对用户,授权是针对App。其目的是让某App有权利访问 某用户 的信息。这里的Token是唯一的。不可以转移到其它App上,也不可以转到其它用户上。

Session只提供一种简单的认证,即有此SID,即认为有此User的全部权利。是需要严格保密的,这个数据应该只保存在站方,不应该共享给其它网站或者第三方App。所以简单来说,如果你的用户数据可能需要和第三方共享,或者允许第三方调用API接口,用Token。如果永远只是自己的网站,自己的App,用什么就无所谓了。

token就是令牌,比如你授权(登录)一个程序时,他就是个依据,判断你是否已经授权该软件;cookie就是写在客户端的一个txt文件,里面包括你登录信息之类的,这样你下次在登录某个网站,就会自动调用cookie自动登录用户名;session和cookie差不多,只是session是写在服务器端的文件,也需要在客户端写入cookie文件,但是文件里是你的浏览器编号.Session的状态是存储在服务器端,客户端只有session id;而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里。

打破误解

“只要关闭浏览器 ,session就消失了”?

不对。对session来说,除非程序通知服务器删除一个session,否则服务器会一直保留,程序一般都是在用户做log off的时候发个指令去删除session。

然而浏览器从来不会主动在关闭之前通知服务器它将要关闭,因此服务器根本不会有机会知道浏览器已经关闭,之所以会有这种错觉,是大部分session机制都使用会话cookie来保存session id,而关闭浏览器后这个session id就消失了,再次连接服务器时也就无法找到原来的session。如果服务器设置的cookie被保存在硬盘上,或者使用某种手段改写浏览器发出的HTTP请求头,把原来的session id发送给服务器,则再次打开浏览器仍然能够打开原来的session.

恰恰是由于关闭浏览器不会导致session被删除,迫使服务器为session设置了一个失效时间,当距离客户端上一次使用session的时间超过这个失效时间时,服务器就可以以为客户端已经停止了活动,才会把session删除以节省存储空间。

results matching ""

    No results matching ""