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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335