第十篇:DRF之实现JWT认证
一、JWT的构成
在用户注册或登录之后,我们想要记录用户的登录状态,或者为用户创建身份认证的凭证,我们不在使用Session
认证机制,而是使用Json Web Token
(本质就是token)认证机制。
JWT(Json Web Token)就是一段字符串,由三段信息构成的,将这三段信息文本用.
连接在一起就构成了JWT字符串。类似于如下。
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNjI3Mjk2ODc4LCJlbWFpbCI6IiJ9.dR5BTVVTGKmMxYCktBqLOqNZrl2sgf_htm_sgsrlPqA"
第一部分我们成为头部(header),第二部分称其为载荷(payload),第三部分是签发(signature)。
二、JWT认证图
三、JWT工作原理
-
jwt分三段式:头.体.签名 (header.payload.signature)。
-
头和体使用base64进行可逆转码(不是加密,以为可以反解),让服务器可以反解出user对象,而签名是不可逆加密,从而保证整个token的安全性。
-
头体签名三部分,都是采用json格式的字符串,进行加密,可逆转码使用base64算法,不可以加密使用md5算法。
-
头中的内容是基本信息:公司信息、项目组信息、token采用的加密方式信息。
{ "company": "公司信息", ... }
-
体中的内容是关键信息:用户主键、用户名、签发时客户端信息(设备号、地址)、过期时间【每次登录请求发来的token都不一样,因为过期时间不同】.
{ "user_id": 1, ... }
-
签名中的内容是安全信息:头的转码结果 + 体的转码结果 + 服务器不对外公开的安全密钥,将其全部进行md5加密。
{ "head": "头的转码字符串", "payload": "体的转码字符串", "secret_key": "安全密钥" }
四、JWT校验流程
我们需要将客户端发来的token数据进行校验,来验证用户的身份。具体步骤如下。
- 将token按照
.
拆分成三段字符串,第一段是头加密字符串,我们一般不需要做任何处理。 - 第二段是体加密字符串,我们需要反接出用户主键,通过主键从User表中就能得到登录用户。而过期时间和设备信息都属于安全信息,是为了确保token没过期,和来自同一设备的。
- 再将 第一段 + 第二段 + 服务器安全密钥,使用md5进行不可逆加密,与得到的第三段签名字符串进行碰撞校验,通过后才能代表第二段校验得到的user对象就是合法的登录用户。
五、DRF项目中实现jwt认证
"""步骤"""
1、用账号密码访问登录接口,登录接口逻辑中调用签发token的算法,得到token
返回给客户端,客户端自己存到cookies中。
2、校验token的算法应该写在认证类中(在认证类中调用),全局配置给认证组件,
所有视图类请求,都会进行认证校验,所以请求带了token,就会反解出user对象,
在视图类中用request.user就能访问登录的用户。
"""补充"""
登录接口不能做 认证和权限校验,必须进行局部禁用。
1、安装djangorestframework-jwt
第三方应用。
pip3 install djangorestframework-jwt
2、创建一个项目,模型表中的用户表,继承AbstractUser,重写auth_user表。
"""settings.py"""
# 配置AUTH_USER_MODEL
AUTH_USER_MODEL = ‘app.userinfo‘
3、创建超级用户。
username: admin
password: admin123
4、简单使用。
"""urls.py"""
# jwt相关
from rest_framework_jwt.views import ObtainJSONWebToken, VerifyJSONWebToken, RefreshJSONWebToken, obtain_jwt_token
urlpatterns = [
# jwt相关
url(r‘^login/‘, obtain_jwt_token),
# 登录之后测试其他视图类
url(r‘^test/‘, views.Test.as_view())
]
- 全局配置
"""settings.py"""
# 全局配置jwt认证
REST_FRAMEWORK = {
‘DEFAULT_AUTHENTICATION_CLASSES‘: [
# ‘rest_framework_jwt.authentication.JSONWebTokenAuthentication‘
]
}
- 局部配置
# 测试视图类
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
class Test(APIView):
authentication_classes = [JSONWebTokenAuthentication]
...
自定义实现jwt认证
"""auth.py"""
from rest_framework_jwt.authentication import BaseAuthentication, BaseJSONWebTokenAuthentication
from rest_framework_jwt.authentication import jwt_decode_handler
from rest_framework import exceptions
class MyToken(BaseJSONWebTokenAuthentication):
def authenticate(self, request):
# 拿到get请求在请求头中的AUTHORIZATION键对应的token值
jwt_value = str(request.META.get(‘HTTP_AUTHORIZATION‘))
print(jwt_value) # eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNjI3Mjk5MzUzLCJlbWFpbCI6IiJ9.hBnpxweihHhaNWSrwjCvpbJx2YDpiAS2pzUhX0teD9o
# 认证
try:
# 将token值的第二段转化成一个用户信息【没有密码】
payload = jwt_decode_handler(jwt_value)
print(payload) # {‘user_id‘: 1, ‘username‘: ‘admin‘, ‘exp‘: 1627299353, ‘email‘: ‘‘}
except Exception:
raise exceptions.AuthenticationFailed("认证失败")
# 根据payload得到用户对象
user = self.authenticate_credentials(payload)
print(user) # admin 用户对象
# 将用户对象返回
return user, None
"""views.py"""
from app.auth import MyToken
# 测试视图类
class Test(APIView):
authentication_classes = [MyToken]
# 如何进行token认证
def get(self, request):
print(request.user) # admin 拿到用户对象
print(type(request.user)) # <class ‘app.models.UserInfo‘>
return Response(‘经过token校验,才能显示。‘)
最终效果如下所示。
我们访问登录路由。
然后我们访问测试路由,用来验证jwt认证是否生效。
如果输入错误token,则验证失败,效果如下。