JWT完全攻略版


想必大家对于JWT也是耳熟能详了,但是具体如何实现,就让我们一起来看一下吧。

单点登录

在介绍JWT之前,我们要知道,什么是单点登录,因为JWT基本上是用来解决单点登录相关问题的。

单点登录(Single Sign On),简称为 SSOSSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

通常用来处理单点登录的方式有两种:

  1. redis作为缓存储存session信息(cookie/session)。
  2. 使用JWT来解决单点登陆

基于cookie和session解决单点登录问题的方法:

redis存储所有session_id。每次做session验证不再从本机对比session,而是将session放在redis里统一管理。无论那台服务器上存储的session都放在redis中。那么在判断的时候都以redis里的数据为准,即可解决单点登陆问题

一句话介绍JWT!

通俗来讲,JWT 是一个含签名并携带用户相关信息的加密串,页面请求校验登录接口时,请求头中携带 JWT 串到后端服务,后端通过签名加密串匹配校验,保证信息未被篡改。校验通过则认为是可靠的请求,将正常返回数据。

啥时候用JWT啊

  • 授权!基本上遇到单点登录问题,JWT就完了!毕竟用起来轻便、开销小,而且服务端无需记录用户状态,用的也是十分的广泛。
  • 信息交换:因为JWT可以签名,例如使用公钥-私钥对儿,可以确定请求方是合法的,同时由于使用了负载计算签名,也可以验证内容是否被篡改。

JWT的组成部分

一个完整的JWT=头信息+有效载荷+签名这三部分组成,中间的加号换成(.)进行分割;

  1. header(头信息):令牌类型[jwt]+散列算法(HMAC/RSASSA/RSASSA-PSS)
{
  'typ': 'JWT',
  'alg': 'HS256'
}

接着进行进行base64加密(该加密是可以对称解密的),就构成了第一部分.

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
  1. payload(有效载荷)的三部分:
1 ==> 标准中注册的声明
2 ==> 公共的声明
3 ==> 私有的声明

标准中注册的声明 (建议但不强制使用) :

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

公共的声明 :
公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.

私有的声明 :
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息

定义一个payload:

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}
加密后就得到了  
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

也就是JWT的第二部分

  1. signature(签名信息) 的三部分:
1 ==> header (base64后的)
2 ==> payload (base64后的)
3 ==> secret

这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。

encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload)
signature = HMACSHA256(encodedString, 'secret');
#TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

于是我们获得了一个最终的字符串(JWT完全体)
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

实际项目中的应用分析

首先,我们大致的看一下流程图
jwt-flow-chart
从流程图中呢,我们可以很清晰的看出JWT的基于TOKEN的鉴权机制。
这个token必须要在每次请求时传递给服务端,它应该保存在请求头里, 另外,服务端要支持CORS(跨来源资源共享)策略,一般我们在服务端这么做就可以了Access-Control-Allow-Origin: *。

用户使用用户名密码来请求服务器==>
服务器进行验证用户的信息==>
服务器通过验证发送给用户一个token==>
客户端存储token,并在每次请求时附送上这个token值==>
服务端验证token值,并返回数据

传统的session认证是每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。
同时,在多台服务器共同工作时,为了减轻压力,可能会互相切换服务器,而只要用户信息不在这台服务器的时候,就需要再次进行验证,这样子用户便需要频繁的进行认证,体验极差。
还有不得不防范的一点是,在传统的认证中,是基于cookie来进行用户识别的,如果cookie被截获,用户就会很容易遭到跨站请求伪造的攻击。

JWT的token被盗

如果出现JWT的token被别人获取了怎么办?

  1. jwt token被盗没办法,只能等他过期。
  2. 建议把token的过期时间弄短些,十几分钟到半小时就行。
  3. 如果token放在头部,并且用的是https,这样很难被盗。
  4. 重启web服务

总结一下

优点
  • 因为json的通用性,所以JWT是可以进行跨语言支持的,而且实现方式简单,操作便捷,能够快速实现,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用。
  • 因为有了payload部分,所以JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。
  • 便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的。
  • 它不需要在服务端保存会话信息, 所以它易于应用的扩展
    安全相关。
  • 由于服务端不存储用户状态信息,因此大用户量,对后台服务也不会造成压力。
缺点
  • 跨域实现相对比较麻烦,安全性也有待探讨。
  • 因为 JWT 令牌返回到页面中,可以使用 js 获取到,如果遇到 XSS 攻击令牌可能会被盗取,在 JWT 还没超时的情况下,就会被获取到敏感数据信息。(这一点跟cookie/session是一样的,获取了sessionID人家就能盗用你的身份)
  • 关于注销和修改密码
    这一点上cookie/session是优势,因为人家只需要服务器清空session即可,但是因为JWT的无状态,虽然客户端删除了jwt,但由于jwt是由服务端计算而得来,还处在有效期内,就会造成一个假注销的情况,也就是说此时携带jwt还是可以访问你的系统。
  • 续签问题
    ,因为人家只需要服务器清空session即可,但是因为JWT的无状态,虽然客户端删除了jwt,但由于jwt是由服务端计算而得来,还处在有效期内,就会造成一个假注销的情况,也就是说此时携带jwt还是可以访问你的系统。
  • 续签问题
    在这一点上,cookie/session算是搬得一城。传统的 cookie 续签方案一般都是框架自带的,session 有效期 30 分钟,30 分钟内如果有访问,session 有效期被刷新至 30 分钟。而 jwt 本身的 payload 之中也有一个 exp 过期时间参数,来代表一个 jwt 的时效性,而 jwt 想延期这个 exp 就有点身不由己了,因为 payload 是参与签名的,一旦过期时间被修改,整个 jwt 串就变了,jwt的特性就是这样……
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值