大话javascript 7期:Cookie、Session和Token的那些事儿

javascript/jquery

浏览数:513

2019-4-19

一、登录认证机制

随着互联网的不断发展,无论是网站还是app,一般都会要求用户注册/登录。主要的登录方式有账户密码登录、第三方登录(微信登录、QQ登录、微博登录等)

登录可分为三个阶段(登录验证、登录持续、退出登录);
登录验证指客户端提供账号/密码(或第三方平台(微信、qq)获取openid/unionid)向服务器提出登录请求,服务器应答请求判断能否登录并返回相应数据;
登录持续指客户端登录后, 服务器能够分辨出已登录的客户端,并为其持续提供登录权限的服务器。
退出登录指客户端退出登录状态。

二、保持登录持续状态的实现方式

为什么要保持登录状态的持续?

由于HTTP是一种无状态的协议,服务器单从网络连接上无从知道客户身份。怎么办呢?就给客户端们颁发一个通行证吧,每人一个,无论谁访问都必须携带自己通行证

方案:客户端登录成功后, 服务器为其分配一个唯一的凭证, 客户端每次请求资源时都带上这个凭证;

实现方案

  1. cookie 会话机制
  2. session 会话机制
  3. token 会话机制

三、Cookie、Session和Token

Cookie(浏览器缓存)

1.什么是Cookie

Cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。

cookie其实是补充http协议的无状态性的缺点,底层是通过服务器端在http响应消息中增加set-cookie字段来将cookie信息发送给浏览器端,因为它只能存4k,一般用来存浏览器的身份信息,浏览器在访问服务器的某些资源的时候,会在http请求头中将cookie数据传给服务器,这样服务器就知道是谁请求的了,但是如果用户清除了cookie,那就啥都没有了

2.Cookie的属性

1、Expires:该Cookie失效的时间,单位秒。

  • 如果为正数,则该Cookie在maxAge秒之后失效(持久级别Cookie)。
  • 如果为负数,该Cookie为临时Cookie,关闭浏览器即失效(会话级别Cookie),浏览器也不会以任何形式保存该Cookie。
  • 如果为0,表示删除该Cookie。默认为–1;

2、Domain:
我们现在有二个域名。域名A:b.f.com,域名B:d.f.com;显然域名A和域名B都是f.com的子域名

  • 如果我们在域名A中的Cookie的domain设置为.f.com,那么.f.com及其子域名都可以获取这个Cookie,即域名A和域名B都可以获取这个Cookie
  • 如果域名A没有显式设置Cookie的domain方法,那么domain就为.b.f.com,不一样的是,这时,域名A的子域名将无法获取这个Cookie
  • HttpOnly: 这个属性是面试的时候常考的,如果这个属性设置为true,就不能通过js脚本来获取cookie的值,能有效的防止xss攻击

3.Cookie的操作

封装cookie的常用操作方法

  • 设置cookie
  • 读取cookie
  • 删除cookie
var cookieUtil = {
    getItem: function (name) {
        var cookieName = encodeURIComponent(name) + "=",
            cookieStart = document.cookie.indexOf(cookieName),
            cookieValue = null;
        if (cookieStart > -1) {
            var cookieEnd = document.cookie.indexOf(';', cookieStart);
            if (cookieEnd == 1) {
                cookieEnd = document.cookie.length;
            }
            cookieValue = decodeURIComponent(document.cookie.substring(cookieStart + cookieName.length, cookieEnd))
        }
        return cookieValue;
    },
    setItem: function (name, value, expires, path, domain, secure) {
        var cookieText = encodeURIComponent(name) + "=" + encodeURIComponent(value);
        if (expires) {
            cookieText += ";expires=" + expires.toGMTString();
        }
        if (path) {
            cookieText += ";path=" + path;
        }
        if (domain) {
            cookieText += ";domain=" + domain;
        }
        if (secure) {
            cookieText += ";secure";
        }
        document.cookie = cookieText;
    },
    unset: function (name, path, domain, secure) {
        this.setItem(name, "", new Date(0), path, domain, secure)
    }
}
CookieUtil.setItem("name", 'tom'); // 设置cookie
console.log(CookieUtil.getItem('name'));//读取cookie
CookieUtil.unset("name")//删除cookie

4.Cookie防篡改机制

因为Cookie是存储在客户端,用户可以随意修改。所以,存在一定的安全隐患。

防篡改签名:服务器为每个Cookie项生成签名。如果用户篡改Cookie,则与签名无法对应上。以此,来判断数据是否被篡改。

原理如下:

  • 服务端提供一个签名生成算法secret
  • 根据方法生成签名secret(wall)=34Yult8i
  • 将生成的签名放入对应的Cookie项username=wall|34Yult8i。其中,内容和签名用|隔开。
  • 服务端根据接收到的内容和签名,校验内容是否被篡改。

举个栗子:
比如服务器接收到请求中的Cookie项username=pony|34Yult8i,然后使用签名生成算法secret(pony)=666。 算法得到的签名666和请求中数据的签名不一致,则证明数据被篡改。

Session(会话)

1.什么是session

session是一种服务器机制,是存储在服务器上的信息,主要配合cookie完成浏览器的身份认证和状态存储方式多种多样,可以是服务器的内存中,或者是mongo数据库,redis内存数据库中。为了获得更高的存取速度,服务器一般把Session放在内存里。每个用户都会有一个独立的Session。如果Session内容过于复杂,当大量客户访问服务器时可能会导致内存溢出。因此,Session里的信息应该尽量精简。

Session相对于cookie较安全点,当用户请求服务器的时候,服务器会把数据临时存下来,如果退出网站后,session会被销毁。

Session是基于cookie实现的,浏览器第一次访问服务器时,服务器创建一个Session,同时生成一个唯一的会话key,即sessionID。接着sessionIDsession分别作为keyvalue保存到缓存中,也可以保存到数据库中,然后服务器把sessionID通过set-cookie的方式写入浏览器,浏览器下次访问服务器时直接携带上cookie中的sessionID,服务器再根据sessionID找到对应的session进行匹配,来判断用户是否登录

2.session鉴权过程

【1】 客户端发起登录请求,服务器端创建session,并通过set-cookie将生成的sessionID写入的客户端的cookie中。
【2】 在发起其他需要权限的接口的时候,客户端的请求体的Header部分会携带sessionID发送给服务端。然后根据这个sessionId去找服务器端保存的该客户端的session,然后判断该请求是否合法。

3.cookie和session的区别

Token(身份令牌)

1.什么是token

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

浏览器第一次访问服务器,根据传过来的唯一标识userId,服务端会通过一些算法,如常用的HMAC-SHA256算法,然后加一个密钥,生成一个token,然后通过BASE64编码一下之后将这个token发送给客户端;客户端将token保存起来,下次请求时,带着token,服务器收到请求后,然后会用相同的算法和密钥去验证token,如果通过,执行业务操作,不通过,返回不通过信息;

2.token生成方式

浏览器第一次访问服务器时,服务器根据传过来的唯一标识userId,通过一些算法,加一个密钥,生成一个token,接着通过base64编码将token返回给客户端。客户端将token保存起来,下次请求时需要带着token,服务器收到请求后,用相同的算法和密钥去验证token

3.token和session的区别

token和session其实都是为了身份验证,session一般翻译为会话,而token更多的时候是翻译为令牌;
session服务器会保存一份,可能保存到缓存,文件,数据库;同样,session和token都是有过期时间一说,都需要去管理过期时间;
其实token与session的问题是一种时间与空间的博弈问题,session是空间换时间,而token是时间换空间。两者的选择要看具体情况而定。
虽然确实都是“客户端记录,每次访问携带”,但 token 很容易设计为自包含的,也就是说,后端不需要记录什么东西,每次一个无状态请求,每次解密验证,每次当场得出合法 /非法的结论。这一切判断依据,除了固化在 CS 两端的一些逻辑之外,整个信息是自包含的。这才是真正的无状态。
而 sessionid ,一般都是一段随机字符串,需要到后端去检索 id 的有效性。万一服务器重启导致内存里的 session 没了呢?万一 redis 服务器挂了呢?

sessionID是基于cookie实现的,而token不需要基于cookie。这就导致了sessionID只能用在浏览器上,对于原生的应用无法实现。原生的应用是不具备cookie的特性的。另外sessionID可以实现服务端注销会话,而token不能(当然你可以把用户登陆的token存入到redis中,但是不推荐token入库)

4.token的优点

Token作为用户认证的处理方式,有几个优点:

  • 无状态,可扩展:不会在服务端存储用户的登录状态,可以很容易的实现服务器的增减
  • 支持移动设备,对多类型客户端的支持良好
  • 支持跨程序调用,各个接口之间的调用更方便
  • 安全可靠

5.什么是JSON Web Token

JSON web Token,简称JWT,本质是一个token,是一种紧凑的URL安全方法,用于在网络通信的双方之间传递。一般放在HTTP的headers参数里面的authorization里面,值的前面加Bearer关键字和空格。除此之外,也可以在url和request body中传递。

Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

如果你觉得这篇文章对你有所帮助,那就顺便点个赞吧,点点关注不迷路~

黑芝麻哇,白芝麻发,黑芝麻白芝麻哇发哈!

前端哇发哈