zoukankan      html  css  js  c++  java
  • drf-jwt认证

    JWT认证

    drf-jwt

    drf-jwt:官网http://getblimp.github.io/django-rest-framework-jwt/

    安装子:虚拟环境

    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算法认证,更适合服务器集群部署

    服务器压力小,集群部署更加完善。

    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)
    
  • 相关阅读:
    【51NOD 1478】括号序列的最长合法子段
    【BZOJ 3527】【ZJOI 2014】力
    【BZOJ 2194】快速傅立叶之二
    【CodeVS 3123】高精度练习之超大整数乘法 &【BZOJ 2197】FFT快速傅立叶
    【BZOJ 2693】jzptab
    【BZOJ 2154】Crash的数字表格
    【BZOJ 3529】【SDOI 2014】数表
    【BZOJ 2820】YY的GCD
    【BZOJ 2301】【HAOI 2011】Problem b
    【POJ 3294】Life Forms 不小于k个字符串中的最长子串
  • 原文地址:https://www.cnblogs.com/SkyOceanchen/p/11923014.html
Copyright © 2011-2022 走看看