JWT身份认证原理

官网:Json Web Token

JWT官方

0x01 认证原理

JWT,Json Web Token。用户登陆成功后,服务端返回一个 jwt 的 token,服务端不储存,客户端储存。此后客户端每次请求,都带上 token。服务端会 jwt校验 token 的有效性。优点显而易见,是不需要在服务端储存 token,服务端只需要做校验有效性的工作即可。

JWT官方解释

JWT 格式:

点分三段的字符串:

第一段:HEADER

base64url 加密的密文,可解密。

jwt 头部,包含说明第三段不可解密的编码算法名 HS256、字段数据类型标识 JWT。

第二段:PAYLOAD

base64url 加密的密文,可解密。

jwt 数据,包含自定义的数据。

第三段:VERIFY SIGNATURE

HS256 加密的密文,无法解密

校验签名,第一段密文.第二段密文“盐”进行 HS256 加密,生成密文,再做base64url 加密。

0x02 代码实例

为了实现用户登录之后的身份认证和授权管理,采用 JWT 认证。

pip install pyjwt

颁发 token

release.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import jwt
import datetime
from django.conf import settings

def create_token(payload,timeout=30):
"""
创建token的部分
payload 外部传入payload,即用户信息在外部写,传入这里输出token即可
timeout 超时时间,默认30s
返回值:token
"""
# 构造header
headers = {
"alg":"HS256",
"typ":"jwt"
}
#构造payload
payload['exp'] = datetime.datetime.utcnow() + datetime.timedelta(seconds=timeout)
token = jwt.encode(payload=payload,key=settings.SECRET_KEY,algorithm="HS256",headers=headers)
return token

认证 token

auth.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from django.conf import settings
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
import jwt
from jwt import exceptions

class JwtAuthentication(BaseAuthentication):

def authenticate(self, request):
token = request.META.get("HTTP_AAMS_TOKEN")
try:
payload = jwt.decode(token, settings.SECRET_KEY, algorithms="HS256")
except exceptions.ExpiredSignatureError:
raise AuthenticationFailed({"code": 400, "msg": "token已失效"})
except jwt.DecodeError:
raise AuthenticationFailed({"code": 400, "msg": "token认证失败"})
except jwt.InvalidTokenError:
raise AuthenticationFailed({"code": 400, "msg": "token无效"})
# 这个return是会放行的,并不是直接返回。
# 这里的意思是会把payload和token挂载在request上
# payload:储存了username(用户账号)和exp(过期时间),挂载在request.user
# token:储存了token信息,挂载在request.auth上
return (payload, token)

配置在全局

若是要哪个请求需要认证才能进行后续业务,那么:

views.py

1
2
3
4
5
from myserver.mytoken.auth import JwtAuthentication
class aView(...):
authentication_class = [JwtAuthentication,]
def get(self,request):
..

因为基本上所有的请求,都是要认证再进行业务的。所以可以配置在

settings.py

1
2
3
4
5
6
7
SECRET_KEY = "xxxxxxxxxxxx自己设置即可xxxxxxxxxxxxx"	# 前面设置的密钥(盐)

REST_FRAMEWORK = [
"DEFAULT_AUTHENTICATION_CLASS":[
"myserver.mytoken.JwtAuthentication",
]
]

注意,要在登陆视图函数取消认证配置:

1
2
3
4
class LoginView(...):
authentication_class = [] #这里设置的优先级会高于全局设置,所以最终Login会取消认证的操作,直接放行

def get(...):

login视图函数

views.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from django.views.decorators.csrf import ensure_csrf_cookie
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from .mytoken.release import create_token

from .models import Major,User
from .serializers import CourseSerializer


class Login(APIView):
authentication_classes = []

def get(self,request):
return Response({"code":400, "msg":"后端没有login的get"},status=status.HTTP_200_OK)

def post(self,request):
username = request.POST.get("username")
password = request.POST.get("password")

exists = User.objects.filter(username=username,password=password).exists()
if exists:
token = create_token({"username":username})
res = {
"code":200,
"msg":"登陆成功!",
"data":{
"aams-token":token,
}
}
return Response(res,status=status.HTTP_200_OK)
return Response({"code":400, "msg":"登陆失败!"},status=status.HTTP_400_BAD_REQUEST)

若想一天不失效,可以:

1
token = create_token({"username":username},timeout=60*60*24)    #一天不失效

0x03 认证测试

正如前面所说,我们采用JWT认证,那么测试登录和认证十分重要。

在第一次登录成功之后,应该由后端颁发一个token(我的系统命名为aams-token)。此后,每次网络请求必须带上这个aams-token作为请求头,后端会在请求头里取到aams-token,由中间件进行认证拦截,认证成功则会放行,否则请求失败。

登录测试

首先看一下能否在登录成功后得到一个正确的token:

登陆测试

认证测试

除登录API,随便选择一个API,发起请求,分别看在没有token和带有token的情况下,会收到什么响应:

注:以下所有API的response都是自行设计的code和msg。

为了方便测试,我只采用了少量的code(同时也尽量贴合状态码的原意)和msg。

code 状态码:200 处理成功;400 处理失败;301 重定向;202 接收处理但等待创建。

msg 状态信息:解释每种情况下的具体信息。

情况1:不带token

如下图,可见code状态码为400,msg状态信息为token认证失败。

情况1:不带token

情况2:带失效的token

如下图,可见code状态码为400,msg状态信息为token已失效。

情况2:带失效的token

情况3:带有效的token

如下图,可见code状态码为200,msg状态信息为获取成功。

情况3:带有效的token