JWT(JSON Web Token)

问:token为何要存入redis中:

为什么token要存入redis中?
1、实现单点登录
假设:有设备AB、无单点登录限制的某APP、一个账号(用户)
设备A在APP第1次登录用户,服务器会给该用户创建1个Token并返回到设备A,设备A会将该Token保存到本地
设备B也是在APP第1次登录该用户,服务器也会给该用户创建1个Token并返回到设备B(此处的Token不同于上一个),设备B会将该Token保存到本地
那这样的话,2个设备可同时在该APP登录同一个账号,即多点登录,不合理
因此需要通过Redis进行限制
2、用户修改密码,实现重新登录
假设:有个账号在登录后的使用期间修改了账号密码、APP无自动退出账号的操作
毕竟是登录过的用户,则设备本地必然有个Token,若修改密码后,该Token仍未过期,则账号仍是正常使用,不会被退出
正常是在登录期间,修改了密码,那对应的Token会被删除,使得账号被迫退出,用户需再次登录来获取新的Token;此删除与添加操作可通过Redis完成

1.HTTP是一种无状态的协议

用户向我们的应用提供了用户名和密码来进行用户认证,认证通过后HTTP协议不会记录下认证后的状态,那么下一次请求时,用户还要再一次进行认证,因为根据HTTP协议,我们并不知道是哪个用户发出的请求

2.浅谈Session认证

image.png
为了让我们的应用能识别是哪个用户发出的请求,我们只能在用户首次登录成功后,在服务器存储一份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存为cookie,以便下次请求时发送给我们的应用,这样我们的应用就能识别请求来自哪个用户了,这是传统的基于session认证的过程

3.session认证存在的问题

1.每个用户的登录信息都会保存到服务器的session中,随着用户的增多,服务器开销会明显增大2.由于session是存在与服务器的物理内存中,所以在分布式系统中,这种方式将会失效。虽然可以将session统一保存到Redis中,但是这样做无疑增加了系统的复杂性,对于不需要redis的应用也会白白多引入一个缓存中间件3.对于非浏览器的客户端、手机移动端等不适用,因为session依赖于cookie,而移动端经常没有cookie4.因为session认证本质基于cookie,所以如果cookie被截获,用户很容易收到跨站请求伪造攻击。并且如果浏览器禁用了cookie,这种方式也会失效5.前后端分离系统中更加不适用,后端部署复杂,前端发送的请求往往经过多个中间件到达后端,6.cookie中关于session的信息会转发多次由于基于Cookie,而cookie无法跨域,所以session的认证也无法跨域,对单点登录不适用

4.CSRF

CSRF(Cross Site Request Forgery, 跨站域请求伪造)是一种网络的攻击方式,它在 2007 年曾被列为互联网 20 大安全隐患之一。

4.1攻击原理

在病毒网站的网页源代码插入js代码,利用存在浏览器的cookie,伪造一个请求发送给给其他服务器,比如伪造一个转账请求,这类情况就是csrf攻击

5.单点登录

web系统早已从久远的单系统发展成为如今由多系统组成的应用群,面对如此众多的系统,用户难道要一个一个登录、然后一个一个注销吗?
JWT - 图2
单点登录全称Single Sign On(以下简称SSO),是指在多系统应用群中登录一个系统,便可在其他所有系统中得到授权而无需再次登录,包括单点登录与单点注销两部分
JWT - 图3

6.JWT结构

image.png
image.png
JWT由3部分组成:标头(Header)、有效载荷(Payload)和签名(Signature)。在传输的时候,会将JWT的3部分分别进行Base64编码后用.进行连接形成最终传输的字符串

6.1 Header

JWT头是一个描述JWT元数据的JSON对象,alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性表示令牌的类型,JWT令牌统一写为JWT。最后,使用Base64 URL算法将上述JSON对象转换为字符串保存

  1. {
  2. "alg": "HS256",
  3. "typ": "JWT"
  4. }

6.2 Payload

有效载荷部分,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据。 JWT指定七个默认字段供选择

  1. iss: jwt签发者
  2. sub: jwt所面向的用户
  3. aud: 接收jwt的一方
  4. exp: jwt的过期时间,这个过期时间必须要大于签发时间
  5. nbf: 定义在什么时间之前,该jwt都是不可用的.
  6. iat: jwt的签发时间
  7. jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。

除以上默认字段外,我们还可以自定义私有字段,一般会把包含用户信息的数据放到payload中,如下例:

  1. {
  2. "sub": "1234567890",
  3. "name": "Helen",
  4. "admin": true
  5. }

6.3 Signature(签名哈希)

签名计算公式:
HMACSHA256(base64UrlEncode(header)+”.”+base64UrlEncode(payload),secret)
密钥(secret)仅仅为保存在服务器中,并且不能向用户公开。
在计算出签名哈希后,JWT头,有效载荷和签名哈希的三个部分组合成一个字符串,每个部分用.分隔,就构成整个JWT对象

6.4 原理

服务端接收到客户端发送过来的JWT token之后:
header和payload可以直接利用base64解码出原文,从header中获取哈希签名的算法,从payload中获取有效数据signature由于使用了不可逆的加密算法,无法解码出原文,它的作用是校验token有没有被篡改。服务端获取header中的加密算法之后,利用该算法加上secretKey对header、payload进行加密,比对加密后的数据和客户端发送过来的是否一致。注意secretKey只能保存在服务端,而且对于不同的加密算法其含义有所不同,一般对于MD5类型的摘要加密算法,secretKey实际上代表的是盐值

6.5 编码区别

base64url算法将json对象转化为字符串保存
base64编码结果中会有+、/、=三个特殊字符,它们在URL中属于特殊字符是直接无法传递的;
base64url其实就是把字符中的’+’和’/‘分别替换成’-‘和’_’,另外把末尾填充的‘=’去掉;其他都一样。

6.6 存放位置

你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息Authorization字段里面。

  1. Authorization: Bearer <token>

7. 盐值

SALT值属于随机值。用户注册时,系统用来和用户密码进行组合而生成的随机数值,称作salt值,通称为加盐值。
1、背景:系统通常把用户的密码如MD5加密后,以密文形式保存在数据库中,来防止黑客偷窥。
2、产生:随着对MD5密文查询工具的出现,而很多用户的密码又设置简单,单纯的对用户密码进行MD5加密后保存,用密文很容易就能反查询得到某用户的密码。
3、原理:为用户密码添加Salt值,使得加密的得到的密文更加冷僻,不宜查询。即使黑客有密文查询到的值,也是加了salt值的密码,而非用户设置的密码。salt值是随机生成的一组字符串,可以包括随机的大小写字母、数字、字符,位数可以根据要求而不一样。
4、用途:当用户首次提供密码时(通常是注册时),由系统自动添加随机生成的salt值,然后再散列。而当用户登录时,系统为用户提供的代码撒上同样的加盐值,然后散列,再比较散列值,已确定密码是否正确。
5、其它:经过添加salt值处理的密码,即使用户设置的原密码是相通的,数据库中的密文却是不同的。