JWT

Posted tian-cai-1996

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JWT相关的知识,希望对你有一定的参考价值。

一、JWT学习

1.web认证

什么是web认证?通俗的说,web认证就是在你访问web服务器时,如何让服务器认识你是谁?

 

web认证最常见的三种:

  1. 基础的用户名和密码认证,每次访问web服务器时,都携带用户名和密码。

  2. 基于Session/Cookie的认证,是指登录成功后,在服务器端存储一个session,客户端存储一个Session Id。认证时,请求通过cookie携带Session Id,并由服务器从Session存储中找到对应的Session。

  3. 基于Token的认证,是指登录成功后,服务器不保存任何认证信息,而是将所有认证相关的信息在服务器端编码成一个Token,并由服务器签名,以确保不被篡改,并将该token发送给客户端,之后的客户端发送请求时,只要携带上这个token,服务器就可以通过token中的签名直接验证用户的身份是否合法,有哪些权限和信息等。

    JWT(Json Web Token)就是最常见的Token认证。

 

2.JWT 简介

JWT(Json Web Token) 是一个开放标准(RFC 7519),它定义了一种简洁,自包含的用于通信双方之间以 JSON 对象的形式,安全传递信息的方法

通俗的说,JWT就是一种基于json的web token认证方式,具体为服务器生成一个内部为json对象的token串(一般都是在客户端认证通过后),并且发放给客户端,客户端之后的请求只要携带上这个token串,服务器便能够通过解析这个token串识别客户端的身份,同时还能从token串中直接获知相关信息

 

3.JWT 组成

JWT实际上就是一个字符串,它由三部分组成,头部、负载与签名。

技术图片

  • header、Payload和Signature之间用 . 号连接,例如:

    eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNTY3MTU2MTA5LCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwib3JpZ19pYXQiOjE1NjY1NTEzMDl9.VwVwdkQalKip4Cp1_8QjcqR0n_S9w3jgUJEf3oO4PoI
  • Header 头部

头部包含了两部分,token 类型和采用的加密算法

{
"alg": "HS256",
"typ": "JWT"
}
  • typ: (Type)类型,指明类型是JWT

  • alg: (Algorithm)算法,必须是JWS支持的算法,主要是HS256和RS256

    HS256: HMAC SHA256算法,是一种对称加密算法

    RS256: RSA SHA256算法,是非对称加密

它会使用 base64url编码组成 JWT 结构的第一部分,

注意:JWT中会丢弃base64编码后,尾部的 = 号


base64编码
64个可见字符:A-Z a-z 0-9 + /  
编码集:序号从前到后,   0就是A , 63就是 /
?
把每3个字节,拆分为4个字节
原始字节:   a     b     c
16进制:   61     62   63
二进制: 01100001     01100010 01100011
按6位分割: 011000   010110   001001   100011
6位的前面补00: 00011000   00010110   00001001   00100011
十六进制:           18                 16             09             23
十进制:             24                   22             9             35
base64:             Y                     W             J             j
?
?
?
有几个问题:
1、 位数留1个字节 :   a
二进制:   01100001
拆分后: 011000     01
补充,先把位数补齐6个,从后补0:   00011000   00010000
为了位置4个倍数的字节数,在后面补=号: Y   P     =     =
?
?
2、在http中, + 和 / 是不安全字符
改造了一个url安全的base64: 使用   - 替换 + , _ 替换 /

 

  • Payload 负载

这部分就是我们存放信息的地方了,你可以把用户 ID 等信息放在这里,JWT 规范里面对这部分有进行了比较详细的介绍,JWT 规定了7个官方字段,供选用

iss (issuer):签发人
exp (expiration time):过期时间,时间戳
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间,时间戳
iat (Issued At):签发时间,时间戳
jti (JWT ID):编号

常用的有iss、iat、exp、aud和sub,一般最少要设置一个 exp 失效时间

另外还可以自己添加一些自定义信息,记住必须是非私密的,譬如:用户id,是否管理员等,同时包含的内容不宜过多

同样的,它会使用 base64url 编码组成 JWT 结构的第二部分

 

  • Signature 签名

签名的作用是保证 JWT 没有被篡改过

前面两部分都是使用 base64url 进行编码的,前端可以解开知道里面的信息。Signature 需要使用编码后的 header 和 payload 以及我们提供的一个密钥,这个密钥只有服务器才知道,不能泄露给用户,然后使用 header 中指定的签名算法(HS256)进行签名。

HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNTY3MTU2MTA5LCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwib3JpZ19pYXQiOjE1NjY1NTEzMDl9.VwVwdkQalKip4Cp1_8QjcqR0n_S9w3jgUJEf3oO4PoI
  • 签名的目的

  最后一步签名的过程,实际上是对头部以及负载内容进行签名,防止内容被篡改。如果有人对头部以及负载的内容解码之后进行修改,再进行编码,最后加上之前的签名组合形成新的JWT的话,那么服务器端会判断出新的头部和负载形成的签名和JWT附带上的签名是不一样的。如果要对新的头部和负载进行签名,在不知道服务器加密时用的密钥的话,得出来的签名也是不一样的。

注意:签名的密钥一定要妥善保管,决不能泄露

 

4.JWT 的使用方式

客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage或SessionStorage中,此后,客户端每次与服务器通信,都带上这个 JWT。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息Authorization字段里面。

 

JWT认证流程如下:

技术图片

  1. 首先,前端通过Web表单将自己的用户名和密码发送到后端的接口。这一过程一般是一个HTTP POST请求。建议的方式是通过SSL加密的传输(https协议),从而避免敏感信息被嗅探。

  2. 后端核对用户名和密码成功后,将用户的id等其他信息作为JWT Payload(负载),将其与头部分别进行Base64编码拼接后签名,形成一个JWT。形成的JWT就是一个形同lll.zzz.xxx的字符串。

  3. 后端将JWT字符串作为登录成功的返回结果返回给前端。前端可以将返回的结果保存在localStorage或sessionStorage上,退出登录时前端删除保存的JWT即可。

  4. 前端在每次请求时携带JWT进行访问,譬如放入HTTP Header中的Authorization。

  5. 后端检查是否存在,如存在验证JWT的有效性。例如,检查签名是否正确;检查Token是否过期;检查Token的接收方是否是自己(可选)。

     

5.JWT 的特点

优点(特点):

  1. JWT 加密后的字符串保存于客户端中,服务器不保存数据,减少服务器存储压力

  2. JWT 签名字符串中存储了用户部分的非私密信息,能够减少服务器数据库的开销

  3. JWT 默认是不加密,不加密的情况下,不能将秘密数据写入 JWT,但也是可以加密的,生成原始 Token 以后,可以用密钥再加密一次,加密情况下可以存放一些秘密数据

  4. JWT 能够不需要做任何额外工作,即可实现单点登录

 

缺点:

  1. JWT 的最大缺点是,由于它是无状态的,服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑(但是这样又违背了无状态的原则)。为了减少盗用,JWT 的有效期应该设置得比较短,对于一些比较重要的权限,使用时应该再次对用户进行认证。

  2. 加大了服务器的计算开销

     

6.JWT 的应用场景

适合的场景:

  1. 前后端分离的web应用

  2. 提供API服务的平台应用,如Restful API应用

  3. 一次性验证的应用,如邮箱验证

     

不适合的场景:

  1. ,如官网、博客等

  2. 需要做服务端的状态管理的应用,譬如:注销、续签(刷新失效时间)、修改密码等

 

7.项目中应用JWT

以下内容是django项目中,应用rest framework框架,在框架中使用 djangorestframework-jwt 库来实现jwt

  1. 安装

一般使用 djangorestframework-jwt 库来实现JWT

pip install djangorestframework-jwt==1.11.0

 

  1. settings:

REST_FRAMEWORK = {
   # 默认的验证是按照验证列表 从上到下 的验证
   ‘DEFAULT_AUTHENTICATION_CLASSES‘: (
       # 配置JWT认证
       ‘rest_framework_jwt.authentication.JSONWebTokenAuthentication‘,
       # 配置session_id认证
       ‘rest_framework.authentication.SessionAuthentication‘,
       # 配置默认的认证方式 base:账号密码验证
       ‘rest_framework.authentication.BasicAuthentication‘,
  )
}
?
JWT_AUTH = {
   # 允许刷新token
   ‘JWT_ALLOW_REFRESH‘: True,
   # 每次刷新后,token的有效时间
   ‘JWT_EXPIRATION_DELTA‘: datetime.timedelta(days=1),
   # 生成 token 后,最大的有效时间:在有效期内通过刷新可以保持token有效;超过这个时间后,token失效,刷新也不起作用
   ‘JWT_REFRESH_EXPIRATION_DELTA‘: datetime.timedelta(days=30),
}

 

  1. urls

在根url或子应用项目下配置JWT登录的url路由,本文配置在根urls下:

# post 登录JWT,获取token的url
path(‘api-token-auth/‘, obtain_jwt_token),

 

  1. 登录JWT,获取token

# url 参数提交
curl http://127.0.0.1:8000/api-token-auth/ -i -X POST -d "username=admin&password=qwer1234"
?
# json 方式提交
curl http://127.0.0.1:8000/api-token-auth/ -i -X POST -H "Content-Type: application/json" -d {"username":"admin","password":"qwer1234"}

 

  1. 应用JWT

访问需要登录的请求时,通过 "Authorization: JWT <your_token>"携带 第4步 获取的token

curl -i http://127.0.0.1:8000/rest/students/ -X POST -H "Content-Type: application/json" -d {"name":"jwt_stu","age":18,"sex":1} -H "Authorization:JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNTY3MzA0Mzc0LCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwib3JpZ19pYXQiOjE1NjcyMTc5NzR9.JOLiavpHXxF70bZfTtB1bmgeQ780dSVcgX8i9pVoGhU"

 

  1. 刷新token

为了防止token失效,一般需要定期进行刷新

urls中增加:

# post 刷新JWT的token 的url
path(‘api-token-refresh/‘, refresh_jwt_token),

访问:

curl -i http://127.0.0.1:8000/api-token-refresh/ -X POST -H "Content-Type: application/json" -d {"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNTY3MTU4OTI5LCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwib3JpZ19pYXQiOjE1NjY1NTQxMjl9.h8RNpuEXLhL9ZYlSCwrdXHUr5Nf-HGLZtlmMyQp3Frk"} 

 

  1. 认证token

有些应用中,有专门的服务器进行token认证,其它服务器得到token后,提交给专门的认证服务器进行认证

urls:

path(‘api-token-verify/‘, verify_jwt_token),

访问:

curl -i http://127.0.0.1:8000/api-token-verify/ -X POST -H "Content-Type: application/json" -d {"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNTY3MTU4OTI5LCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwib3JpZ19pYXQiOjE1NjY1NTQxMjl9.h8RNpuEXLhL9ZYlSCwrdXHUr5Nf-HGLZtlmMyQp3Frk"} 

说明:

认证成功返回200状态码,内容是:{"token":"your_token"}

认证失败犯规400状态码,内容是:{"non_field_errors":["Error decoding signature."]}

 

  1. 所有配置项在:rest_framework_jwtsettings.py 中可以查看到,不过一般不需要配置其它更多的配置项

以上是关于JWT的主要内容,如果未能解决你的问题,请参考以下文章

OkHttpInterceptor 从 kotlin 拦截器导航到登录片段

AttributeError: ‘str‘ object has no attribute ‘decode‘解决方法

《代码实例》jwt参与用户凭证方式,生成jwt,security整合jwt

放置jwt.sign代码时出现JWT错误

我已经在 Spring Boot 代码中实现了 JWT 令牌安全性。如何在我的代码中的任何地方获取 jwt 令牌?需要保存审核

Jwt 代码在 .NET Core 2 上不起作用