Authentication:用户认证,指的是验证用户的身份,例如你但愿以小A的身份登陆,那么应用程序须要经过用户名和密码确认你真的是小A。redis
Authorization:受权,指的是确认你的身份以后提供给你权限,例如用户小A能够修改数据,而用户小B只能阅读数据。算法
因为http协议是无状态的,每一次请求都无状态。当一个用户经过用户名和密码登陆了以后,他的下一个请求不会携带任何状态,应用程序没法知道他的身份,那就必须从新认证。所以咱们但愿用户登陆成功以后的每一次http请求,都可以保存他的登陆状态。数据库
目前主流的用户认证方法有基于token和基于session两种方式。json
基于session的认证流程以下: 后端
最经常使用的是JSON Web Token(jwt): 浏览器
一个jwt实际上就是一个字符串,它由三部分组成,头部、载荷与签名,这三个部分都是json格式。安全
头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。服务器
{ "typ": "JWT", "alg": "HS256" } 复制代码
在这里,咱们说明了这是一个JWT,而且咱们所用的签名算法是HS256算法。cookie
载荷能够用来放一些不敏感的信息。session
{ "iss": "John Wu JWT", "iat": 1441593502, "exp": 1441594722, "aud": "www.example.com", "sub": "jrocket@example.com", "from_user": "B", "target_user": "A" } 复制代码
这里面的前五个字段都是由JWT的标准所定义的。
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcm9tX3VzZXIiOiJCIiwidGFyZ2V0X3VzZXIiOiJBIn0 复制代码
最后,咱们将上面拼接完的字符串用HS256算法进行加密。在加密的时候,咱们还须要提供一个密钥(secret)。加密后的内容也是一个字符串,最后这个字符串就是签名,把这个签名拼接在刚才的字符串后面就能获得完整的jwt。header部分和payload部分若是被篡改,因为篡改者不知道密钥是什么,也没法生成新的signature部分,服务端也就没法经过,在jwt中,消息体是透明的,使用签名能够保证消息不被篡改。
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcm9tX3VzZXIiOiJCIiwidGFyZ2V0X3VzZXIiOiJBIn0.rSWamyAYwuHCo7IFAgd1oRpSP7nzL7BF5t7ItqpKViM 复制代码
基于session和基于jwt的方式的主要区别就是用户的状态保存的位置,session是保存在服务端的,而jwt是保存在客户端的。
因为jwt的payload是使用base64编码的,并无加密,所以jwt中不能存储敏感数据。而session的信息是存在服务端的,相对来讲更安全。
jwt太长。因为是无状态使用JWT,全部的数据都被放到JWT里,若是还要进行一些数据交换,那载荷会更大,通过编码以后致使jwt很是长,cookie的限制大小通常是4k,cookie极可能放不下,因此jwt通常放在local storage里面。而且用户在系统中的每一次http请求都会把jwt携带在Header里面,http请求的Header可能比Body还要大。而sessionId只是很短的一个字符串,所以使用jwt的http请求比使用session的开销大得多。
无状态是jwt的特色,但也致使了这个问题,jwt是一次性的。想修改里面的内容,就必须签发一个新的jwt。
(1)没法废弃 经过上面jwt的验证机制能够看出来,一旦签发一个jwt,在到期以前就会始终有效,没法中途废弃。例如你在payload中存储了一些信息,当信息须要更新时,则从新签发一个jwt,可是因为旧的jwt还没过时,拿着这个旧的jwt依旧能够登陆,那登陆后服务端从jwt中拿到的信息就是过期的。为了解决这个问题,咱们就须要在服务端部署额外的逻辑,例如设置一个黑名单,一旦签发了新的jwt,那么旧的就加入黑名单(好比存到redis里面),避免被再次使用。
(2)续签 若是你使用jwt作会话管理,传统的cookie续签方案通常都是框架自带的,session有效期30分钟,30分钟内若是有访问,有效期被刷新至30分钟。同样的道理,要改变jwt的有效时间,就要签发新的jwt。最简单的一种方式是每次请求刷新jwt,即每一个http请求都返回一个新的jwt。这个方法不只暴力不优雅,并且每次请求都要作jwt的加密解密,会带来性能问题。另外一种方法是在redis中单独为每一个jwt设置过时时间,每次访问时刷新jwt的过时时间。
能够看出想要破解jwt一次性的特性,就须要在服务端存储jwt的状态。可是引入 redis 以后,就把无状态的jwt硬生生变成了有状态了,违背了jwt的初衷。并且这个方案和session都差很少了。
适合使用jwt的场景:
好比,用户注册后发一封邮件让其激活帐户,一般邮件中须要有一个连接,这个连接须要具有如下的特性:可以标识用户,该连接具备时效性(一般只容许几小时以内激活),不能被篡改以激活其余可能的帐户,一次性的。这种场景就适合使用jwt。
而因为jwt具备一次性的特性。单点登陆和会话管理很是不适合用jwt,若是在服务端部署额外的逻辑存储jwt的状态,那还不如使用session。基于session有不少成熟的框架能够开箱即用,可是用jwt还要本身实现逻辑。