JWT
Posted tian-cai-1996
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JWT相关的知识,希望对你有一定的参考价值。
1.web认证
什么是web认证?通俗的说,web认证就是在你访问web服务器时,如何让服务器认识你是谁?
-
基础的用户名和密码认证,每次访问web服务器时,都携带用户名和密码。
-
基于Session/Cookie的认证,是指登录成功后,在服务器端存储一个session,客户端存储一个Session Id。认证时,请求通过cookie携带Session Id,并由服务器从Session存储中找到对应的Session。
-
基于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和RS256HS256: 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认证流程如下:
-
首先,前端通过Web表单将自己的用户名和密码发送到后端的接口。这一过程一般是一个HTTP POST请求。建议的方式是通过SSL加密的传输(https协议),从而避免敏感信息被嗅探。
-
后端核对用户名和密码成功后,将用户的id等其他信息作为JWT Payload(负载),将其与头部分别进行Base64编码拼接后签名,形成一个JWT。形成的JWT就是一个形同lll.zzz.xxx的字符串。
-
后端将JWT字符串作为登录成功的返回结果返回给前端。前端可以将返回的结果保存在localStorage或sessionStorage上,退出登录时前端删除保存的JWT即可。
-
前端在每次请求时携带JWT进行访问,譬如放入HTTP Header中的Authorization。
-
后端检查是否存在,如存在验证JWT的有效性。例如,检查签名是否正确;检查Token是否过期;检查Token的接收方是否是自己(可选)。
5.JWT 的特点
优点(特点):
-
JWT 加密后的字符串保存于客户端中,服务器不保存数据,减少服务器存储压力
-
JWT 签名字符串中存储了用户部分的非私密信息,能够减少服务器数据库的开销
-
JWT 默认是不加密,不加密的情况下,不能将秘密数据写入 JWT,但也是可以加密的,生成原始 Token 以后,可以用密钥再加密一次,加密情况下可以存放一些秘密数据
-
JWT 能够不需要做任何额外工作,即可实现单点登录
缺点:
-
JWT 的最大缺点是,由于它是无状态的,服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑(但是这样又违背了无状态的原则)。为了减少盗用,JWT 的有效期应该设置得比较短,对于一些比较重要的权限,使用时应该再次对用户进行认证。
-
加大了服务器的计算开销
6.JWT 的应用场景
适合的场景:
-
前后端分离的web应用
-
提供API服务的平台应用,如Restful API应用
-
一次性验证的应用,如邮箱验证
不适合的场景:
-
,如官网、博客等
-
需要做服务端的状态管理的应用,譬如:注销、续签(刷新失效时间)、修改密码等
7.项目中应用JWT
以下内容是django项目中,应用rest framework框架,在框架中使用 djangorestframework-jwt 库来实现jwt
-
安装
一般使用 djangorestframework-jwt 库来实现JWT
pip install djangorestframework-jwt==1.11.0
-
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),
}
-
urls
在根url或子应用项目下配置JWT登录的url路由,本文配置在根urls下:
# post 登录JWT,获取token的url
path(‘api-token-auth/‘, obtain_jwt_token),
-
登录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"}
-
应用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"
-
刷新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"}
-
认证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."]}
以上是关于JWT的主要内容,如果未能解决你的问题,请参考以下文章
OkHttpInterceptor 从 kotlin 拦截器导航到登录片段
AttributeError: ‘str‘ object has no attribute ‘decode‘解决方法
《代码实例》jwt参与用户凭证方式,生成jwt,security整合jwt
我已经在 Spring Boot 代码中实现了 JWT 令牌安全性。如何在我的代码中的任何地方获取 jwt 令牌?需要保存审核