drf-jwt認證

JWT認證

drf-jwt

drf-jwt:官網http://getblimp.github.io/django-rest-framework-jwt/前端

安裝子:虛擬環境python

pip install djangorestframework-jwt

jwt模塊

模塊包:rest_framework_jwt
纔有drf-jwt框架,後期任務只須要書寫登陸
    爲何要重寫登陸:drf-jwt只完成了帳號密碼登陸,咱們還須要手機登陸,郵箱登陸
    爲何不須要重寫認證類:由於認證規則已經完成且固定不變,變得只有認證字符串的前綴,前綴能夠在配置文件中配置

工做原理

"""
1) jwt = base64(頭部).base(載荷).hash256(base64(頭部).base(載荷).密鑰)
2) base64是可逆的算法、hash256是不可逆的算法
3) 密鑰是固定的字符串,保存在服務器
"""

jwt認證規則

"""
全稱:json web token
解釋:加密字符串的原始數據是json,後臺產生,經過web傳輸給前臺存儲
格式:三段式 - 頭.載荷.簽名 - 頭和載荷纔有的是base64可逆加密,簽名纔有md5不可逆加密
內容:
    頭(基礎信息,也能夠爲空):加密方式、公司信息、項目組信息、...
    載荷(核心信息):用戶信息、過時時間、...
    簽名(安全保障):頭加密結果+載荷加密結果+服務器祕鑰 的md5加密結果
    服務器簽發(login)->web傳送給前端存儲 ->請求須要的登錄的結果在攜帶給後臺 ->服務器校驗認證組件=>權限管理
    
    
認證規則:
    後臺必定要保障 服務器祕鑰 的安全性(它是jwt的惟一安全保障)
    後臺簽發token -> 前臺存儲 -> 發送須要認證的請求帶着token -> 後臺校驗獲得合法的用戶
"""

爲何要纔有jwt認證(優勢)

1) 後臺不須要存儲token,只須要存儲簽發與校驗token的算法,效率遠遠大於後臺存儲和取出token完成校驗
2) jwt算法認證,更適合服務器集羣部署git

服務器壓力小,集羣部署更加完善。github

drf-jwt開發

使用:user/urls.py

from django.conf.urls import url

from . import views

from rest_framework_jwt.views import refresh_jwt_token
from rest_framework_jwt.views import obtain_jwt_token
from rest_framework_jwt.views import verify_jwt_token
from rest_framework_jwt.views import ObtainJSONWebToken
url(r'^obtain/$', obtain_jwt_token),  # 獲取token
    url(r'^verify/$', verify_jwt_token),  # 驗證token是否正確,正確原樣返回
    url(r'^refresh/$', refresh_jwt_token),  # 提供一個token,刷新一個token,時間日後推遲
    # url(r'^login/$', ObtainJSONWebToken.as_view())  # 實時刷新token

    url(r'^login/$', views.LoginAPIView.as_view()),

配置信息:JWT_AUTH到settings.py中

# drf-jwt配置

import datetime

JWT_AUTH = {
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),  # 過時時間

    # 'JWT_ALLOW_REFRESH': True,  # 對refresh_jwt_token 容許刷新
    # 'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7),  # 刷新的過時時間

    'JWT_AUTH_HEADER_PREFIX': 'JWT',  # 前綴
}

序列化user:user/serializers.py(本身建立)

# 只參與序列化
class UserModelSerializer(ModelSerializer):
    # 改了原數據庫字段的序列化方式
    password = SerializerMethodField()
    def get_password(self, obj):
        return '########'

    class Meta:
        model = models.User
        fields = ('username', 'password', 'mobile', 'email', 'first_name', 'last_name')

自定義response:user/utils.py

from rest_framework.response import Response


class APIResponse(Response):
    # 格式化data
    def __init__(self, status=0, msg='ok', results=None, http_status=None, headers=None, exception=False, **kwargs):
        data = {  # json的response基礎有數據狀態碼和數據狀態信息
            'status': status,
            'msg': msg
        }
        if results is not None:  # 後臺有數據,響應數據
            data['results'] = results
        data.update(**kwargs)  # 後臺的一切自定義響應數據直接放到響應數據data中
        super().__init__(data=data, status=http_status,
                         headers=headers, exception=exception)

基於drf-jwt的全局認證:user/authentications.py(本身建立)

# 自定義認證類

"""
認證模塊工做原理
1)繼承BaseAuthentication類,重寫authenticate方法
2)認證規則(authenticate方法實現體):
    沒有攜帶認證信息,直接返回None => 遊客
    有認證信息,校驗失敗,拋異常 => 非法用戶
    有認證信息,校驗出User對象 => 合法用戶
"""

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
class TokenAuthentication(BaseAuthentication):
    prefix = 'Token'
    def authenticate(self, request):
        # 拿到前臺的token
        auth = request.META.get('HTTP_AUTHORIZATION')
        # 沒有返回None,有進行校驗
        if not auth:
            return None
        auth_list = auth.split()

        if not (len(auth_list) == 2 and auth_list[0].lower() == self.prefix.lower()):
            raise AuthenticationFailed('非法用戶')

        token = auth_list[1]

        # 校驗算法
        user = _get_obj(token)
        # 校驗失敗拋異常,成功返回(user, token)
        return (user, token)

# 校驗算法(認證類)與簽發算法配套
"""
拆封token:一段 二段 三段
用戶名:b64decode(一段)
用戶主鍵:b64decode(二段)
碰撞解密:md5(用戶名+用戶主鍵+服務器祕鑰) == 三段
"""
import base64, json, hashlib
from django.conf import settings
from api.models import User
def _get_obj(token):
    token_list = token.split('.')
    if len(token_list) != 3:
        raise AuthenticationFailed('token異常')
    username = json.loads(base64.b64decode(token_list[0])).get('username')
    pk = json.loads(base64.b64decode(token_list[1])).get('pk')

    md5_dic = {
        'username': username,
        'pk': pk,
        'key': settings.SECRET_KEY
    }

    if token_list[2] != hashlib.md5(json.dumps(md5_dic).encode()).hexdigest():
        raise AuthenticationFailed('token內容異常')

    user_obj = User.objects.get(pk=pk, username=username)
    return user_obj


""" 認證類的認證核心規則
def authenticate(self, request):
    token = get_token(request)
    try:
        user = get_user(token)  # 校驗算法
    except:
        raise AuthenticationFailed()
    return (user, token)
"""

全局啓用:settings.py

# drf-jwt配置

import datetime

JWT_AUTH = {
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),  # 過時時間

    # 'JWT_ALLOW_REFRESH': True,  # 對refresh_jwt_token 容許刷新
    # 'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7),  # 刷新的過時時間

    'JWT_AUTH_HEADER_PREFIX': 'JWT',  # 前綴
}

局部啓用禁用:任何一個cbv類首行

# 局部禁用
authentication_classes = []

# 局部啓用
from user.authentications import JSONWebTokenAuthentication
authentication_classes = [JSONWebTokenAuthentication]

多方式登陸:user/utils.py

from rest_framework.serializers import ModelSerializer, CharField, ValidationError, SerializerMethodField
from . import models
from django.contrib.auth import authenticate
import re
from rest_framework_jwt.serializers import jwt_payload_handler, jwt_encode_handler
class LoginSerializer(ModelSerializer):
    username = CharField(write_only=True)
    password = CharField(write_only=True)
    class Meta:
        model = models.User
        fields = ('username', 'password')

    # 在全局鉤子中籤發token
    def validate(self, attrs):
        # user = authenticate(**attrs)
        # 帳號密碼登陸 => 多方式登陸
        user = self._many_method_login(**attrs)

        # 簽發token,並將user和token存放到序列化對象中
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)

        self.user = user
        self.token = token

        return attrs
# 多方式登陸
    def _many_method_login(self, **attrs):
        username = attrs.get('username')
        password = attrs.get('password')
        if re.match(r'.*@.*', username):
            user = models.User.objects.filter(email=username).first()  # type: models.User

        elif re.match(r'^1[3-9][0-9]{9}$', username):
            user = models.User.objects.filter(mobile=username).first()
        else:
            user = models.User.objects.filter(username=username).first()

        if not user:
            raise ValidationError({'username': '帳號有誤'})

        if not user.check_password(password):
            raise ValidationError({'password': '密碼有誤'})

        return user

配置多方式登陸:settings/dev.py

AUTHENTICATION_BACKENDS = ['user.utils.JWTModelBackend']

手動簽發JWT:瞭解 - 能夠擁有原生登陸基於Model類user對象簽發JWT

from rest_framework_jwt.settings import api_settings

jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER

payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
相關文章
相關標籤/搜索