前言
近期在研究用Django开发后端接口,采用restful标准,JWT认证机制。因此国庆期间特别对这方面知识进行补充,因此对学习过程要点做个记录,由于查阅的资料太多了,本文只浓缩介绍了主干知识点,重点在于实用,很多拓展的知识没介绍,有兴趣的可以自行百度。
提出问题
1.什么是JWT?
2.Django自带的认证机制和drf的认证机制有什么区别?
3.如何使用drf框架搭建一个JWT认证过程?
4.Django自带认证类和rest framework自定义的认证类如何实现?
问题一:什么是JWT?
JWT是JSON Web Token的简写,是一个跨域身份验证解决方案。简单说就是一个带Token的认证机制。JWT的数据结构由三部分组成:头部,有效载荷,签名,格式为:zzzz.xxxx.yyyy
JWT认证在Django中实现的流程图:
问题二:Django自带的认证机制,和drf的认证机制有什么区别?
2.1 Django自带的认证机制:
在调用了django.contrib.auth.authenticate()函数时进入自带的认证机制。它接收两个参数username和password,认证通过则返回一个User model对象,认证失败则返回None。
一般会在Django内置的login()视图中使用到。(rest_framework_jwt.views. obtain_jwt_token视图中也会使用到,后续会介绍到)
2.2 drf认证机制:
- 在view.py中的视图类,只要是继承了rest framework的APIView的类,就会进入drf认证机制。drf默认认证机制中不会用到Django自带的认证。
urls.py
----------------------------------
from app import views
urlpatterns = [
url(r'/auth/token/test/', views.AutoTestView.as_view()), # as_view() 类视图拥有自动查找指定方法的功能
]
app/view.py
----------------------------------
from django.http import HttpResponse
from rest_framework.views import APIView
class AutoTestView(APIView):
"""
1.用户发起option请求
2.请求进入此视图后,会先进行JWT认证,只有认证成功后才会执行option()函数
3.认证成功则返回:JWT,认证失败则返回drf内置错误信息
"""
@staticmethod
def options(request, *args, **kwargs):
return HttpResponse("JWT")
- drf的认证机制在django的settings.py文件中设置,认证顺序依次由上至下。
①.认证类通过则返回一个元组(User对象,userToken对象),可通过request.user和request.auth 调用。
②.当返回None时,说明跳过当前的认证模式,进行下一个认证模式。
③.认证失败则返回内置失败报错信息。
settings.py
-----------------------
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication', # JWT认证方式
'rest_framework.authentication.SessionAuthentication', # 基于session
'rest_framework.authentication.BasicAuthentication', # 基于用户名密码
),
}
问题三(重点):如何使用drf框架搭建一个JWT认证过程?
根据问题一中的JWT认证在Django中实现的流程图(忘记的回去再看一遍),要实现JWT认证需要分两大部分:
第一部分:通过POST方式传入username和password获取到Token值
第二部分:将Token带入请求头部进行请求,通过JWT认证,获取到资源。
3.1 第一部分实现:
① 安装 rest_framework
pip install djangorestframework-jwt
② 设置路由,rest framework自带的视图函数obtian_jwt_token,实现了通过POST请求携带username和password参数进行认证,并返回JWT令牌的操作。
urls.py
----------------
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
url(r'auth/token/obtain/', obtain_jwt_token),
]
至此第一部分配置完成,可以通过postman模拟一个POST请求(如果使用django默认的自带认证方式,参数username和password必须是在django数据库auth_user表里的存在的,不然认证不通过)
响应结果示例
--------------------
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNjAzNTQ1MDAyLCJlbWFpbCI6ImFkbWluQDE2My5jb20ifQ.1619RAA0V0EVqlxp9ipwRTBpuVRwHeMW0Y5DeoyiA0o"
}
3.2 第二部分实现:
① rest framework自带的认证模式(在问题二中有介绍)中JSONWebTokenAuthentication实现了JWT的认证,因此需要在配置文件中指定认证方式。
settings.py
-----------------------
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
),
}
②设置路由,as_view()类视图拥有自动查找指定方法的功能,能根据不同的请求方式(GET,POST,PUT,DELETE)实现不同的响应。
urls.py
----------------
from app import views
urlpatterns = [
url(r'auth/token/test/', views.AutoTestView.as_view()),
]
③设置视图函数,要使用rest framework的认证,视图函数就必须继承APIView类。
app/view.py
----------------------------------
from django.http import HttpResponse
from rest_framework.views import APIView
class AutoTestView(APIView):
# GET请求则进入此函数
def get(request, *args, **kwargs):
return HttpResponse("this is GET")
# POST请求则进入此函数
def post(request, *args, **kwargs):
return HttpResponse("this is POST")
# PUT请求则进入此函数
def pue(request, *args, **kwargs):
return HttpResponse("this is PUT")
至此第二部分配置完成。通过postman模拟请求,请求时带上头部Authorization格式为:自定义前缀 + 空格 + Token,自定义前缀可通过在settings.py配置'JWT_AUTH_HEADER_PREFIX': 'JWT'实现。
请求头部示例
----------------
# 自定义前缀为JWT
Authorization:"JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNjAzNTQ1MDAyLCJlbWFpbCI6ImFkbWluQDE2My5jb20ifQ.1619RAA0V0EVqlxp9ipwRTBpuVRwHeMW0Y5DeoyiA0o"
问题四(拓展):Django自带认证类和rest framework 自定义的认证类如何实现?
在实际业务使用中django自带的认证机制逻辑和rest framework的JWT认证逻辑并不能满足需求,因此需要自行定义逻辑。
4.1 Django自带认证类自定义方式
Django自带的认证方式默认是调用类:django.contrib.auth.backends.ModelBackend,通过类中的authenticate()方法实现认证逻辑,因此要自定义认证只需要继承ModelBackend类,并重写类中的authenticate()即可。
app/utils/my_auth.py
----------------------
from django.contrib.auth.backends import ModelBackend
class MyAuthBackend(ModelBackend):
# 方法名称,形参格式都是固定格式,不建议修改。
def authenticate(self, request, username=None, password=None, **kwargs):
"""
在此处根据业务需要写入新的认证逻辑
"""
return user # User model 对象
完成自定义认证类后,需要在配置文件中添加配置,让Django将认证方式指向自定义的认证类。
setting.py
----------------------
AUTHENTICATION_BACKENDS = [
'app.utils.my_auth.MyAuthBackend',
]
4.2 rest framework 自定义的认证类
rest framework的认证类都是继承于BaseAuthentication类,并通过该类中的authenticate()方法实现认证逻辑。因此要自定义认证类,只要创建一个类,并且在authenticate()方法中重写认证逻辑即可。此处可选择性继承BaseAuthentication类。
app/utils/my_auth.py
----------------------
from rest_framework_jwt.serializers import VerifyJSONWebTokenSerializer
from rest_framework.exceptions import AuthenticationFailed
from rest_framework import serializers
class TokenAuth:
# 以实现JWT的认证为例
@staticmethod
def authenticate(request):
# 获取请求头的 Authorization 字段
headers_token = request.headers.get('Authorization', None)
# 自定义报错信息
if not headers_token:
raise serializers.ValidationError({'Authorization': '该字段是必填项。'})
"""
此处可增加逻辑:
1.检验是否符合 前缀+空格+token格式。
2.自定义业务逻辑。
3.处理headers_token,获取token部分
4.返回None,表示当前认证不处理,跳过此认证逻辑,进入下一个认证
"""
......
......
# 使用rest framework自带的认证方法验证token有效性,返回包含user model对象和token对象的字典
valid_data = VerifyJSONWebTokenSerializer().validate(token)
user = valid_data["user"]
token = valid_data["token"]
# 认证成功,有返回值,必须是元组:(request.user,request.auth),认证失败,返回异常
if user:
return (user,token)
else:
raise AuthenticationFailed('认证失败')
完成自定义认证类后,需要配置此认证类。配置方式分为全局配置和局部配置。
4.2.1 全局配置
在settings.py文件中直接配置,所有继承了APIView类的视图函数均需要进行此认证。
settings.py
-----------------------
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'app.utils.my_auth.TokenAuth', # 自定义的认证类
),
}
4.2.2 局部配置
在需要认证的视图函数中设置变量为:authentication_classes = (TokenAuth,),如果想设置某个视图不使用认证,则设置变量为 :authentication_classes = ()
app/view.py
----------------------------------
from django.http import HttpResponse
from rest_framework.views import APIView
class AutoTestView(APIView):
# 此视图只使用TokenAuth类的认证逻辑
authentication_classes = (TokenAuth,)
# GET请求则进入此函数
def get(request, *args, **kwargs):
return HttpResponse("this is GET")
# POST请求则进入此函数
def post(request, *args, **kwargs):
return HttpResponse("this is POST")
参考资料
https://blog.csdn.net/gymaisyl/article/details/84259743
https://blog.csdn.net/submarineas/article/details/83547854
https://www.lagou.com/lgeduarticle/34000.html
https://www.cnblogs.com/lxgbky/p/12132117.html
http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html