Django搭建jwt认证后端

前言

近期在研究用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中实现的流程图:
自己画的.jpg
问题二: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认证机制:
  1. 在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")
  1. 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

你可能感兴趣的:(Django搭建jwt认证后端)