• DRF的JWT用户认证


    DRF的JWT用户认证

    从根本上来说,JWT是一种开放的标准(RFC 7519), 全称为json web token ,其存在的意义在于,对于前后端分离的项目来说,后端不需要存储token,主需要存储签发和校验token的算法,所以我们需要在前端存储token,然后通过JWT加密之后传送给后端,从而完成校验.

    JWT算法认证的最大优点在于适合服务器集群部署,这样对于比较大的项目可以从根本上提升并发量.

    JWT的认证规则

    JWT的格式

    jwt的格式是三段式,即header(头) . payload(负载) . Signature(签名),其中头和载负载采用的是base64可逆的加密,签名采用的则是md5不可逆的加密,具体如下:

    • 头(基础信息): 包含两部分,token类型以及采用的加密算法
    • 载负载(核心信息): 主要是用户信息,过期时间等
    • 签名(安全保证): 包括三方面,即头加密结果+载荷加密结果+服务器秘钥,采用的是md5加密

    我们的后台一定要保证服务器秘钥的安全,因为在jwt里面服务器秘钥是唯一的安全保障

    JWT认证的流程

    后台签发token -> 前台存储 -> 前台想后台发送需要认证的请求且携带token -> 后台校验token得到合法的用户

    JWT模块的导入为

    # 在cmd或者Terminal窗口中:
    pip install djangorestframework-jwt
    
    # 模块包名为rest_framework_jwt
    

    JWT的使用

    JWT最常用的是三个接口,签发token,校验token以及刷新token,我们可以分别在urls.py里面导入

    # /urls.py
    from django.conf.urls import url
    from . import views
    
    from rest_framework_jwt.views import ObtainJSONWebToken, obtain_jwt_token, verify_jwt_token, refresh_jwt_token
    urlpatterns = [
        # drf-jwt三个视图接口
        url(r'^login/$', obtain_jwt_token),
        url(r'^verify/$', verify_jwt_token),
        url(r'^refresh/$', refresh_jwt_token),
    ]
    
    # 导入之后,我们还需要在settings.py里面配置相关的JWT的配置项
    # /settings.py
    
    import datetime
    JWT_AUTH = {
        # 设置JWT的过期时间
        'JWT_EXPIRATION_DELTA':datetime.timedelta(days=7),
        
        # 自定义jwt插件的配置
        'JWT_PEFRESH_EXPIRATION_DELTA':detetime.timedelta(days=7),
        
        # 设置JWT的头
        'JWT_AUTH_HEADER_PREFIX':'JWT'
    }
    

    下面我们就用代码来实现jwt的整个逻辑,大概逻辑如下:

    1. 视图类中,将请求数据交给序列化完成校验,然后返回用户信息和对应token
    2. 序列化类,自定义反序列化的字段,利用全局钩子校验数据得到user和token,并且保存在序列化类对象中
    3. token可以采用jwt插件的rest_framework_jwt.serializers中jwt_payload_handler,jwt_encode_handler来完成签发

    下面我们做一个实例,该小例子实现了用户的多方式登录:

    # urls.py
    from django.conf.urls import url
    from . import views
    
    urlpatterns = [
    
        url(r'^login/$', views.LoginAPIView.as_view())
    ]
    
    # serializers.py
    from rest_framework.serializers import ModelSerializer, CharField, ValidationError, SerializerMethodField
    from . import models
    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 = self._many_method_login(**attrs)
            # 将数据存放到序列化对象中
            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() 
                
            # 如果是11位数字且开头为1,判定为手机登录
            elif re.match(r'^1[0-9]{10}$', 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
        
    # views.py
    from rest_framework.views import APIView
    from . import models, serializers
    from utils.response import APIResponse
    class LoginAPIView(APIView):
        authentication_classes = []
        permission_classes = []
        
        # 以post的方式接受前台发送的数据
        def post(self, request, *args, **kwargs):
            # 将前台传来的数据传送到序列化对象中,完成校验
            serializer = serializers.LoginSerializer(data=request.data)
            serializer.is_valid(raise_exception=True)
            return APIResponse(msg='login success', data={
                'username': serializer.user.username,
                'token': serializer.token
            })
    
    
  • 相关阅读:
    Trying to reload asset from disk that is not stored on disk
    学习,再学习!
    关于webQQ3.0
    java 之 枚举
    部队的日子
    大兵
    关于webQQ3
    Ubuntu下gedit的java编译设置
    晒晒
    chrome中行网银插件(Linux下可用,可以淘宝支付宝)
  • 原文地址:https://www.cnblogs.com/Xu-PR/p/11938161.html
Copyright © 2020-2023  润新知