• 用户注册登录认证模块


      在项目中创建新应用:Login,来实现注册、登录、认证功能。

    一、注册接口

    1、创建注册路由

      首先在工程路由中添加login应用路由:

    from django.contrib import admin
    from django.urls import path, include, re_path
    from django.views.static import serve
    from LuffyCity import settings
    
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('api/course/', include("Course.urls")),
        path('api/', include("Login.urls")),
    
        # media路径配置
        # path('media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}),
        re_path('media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT})
    ]

      随后在login应用中添加路由文件./Login/urls.py:

    from django.urls import path
    from .views import RegisterView
    
    urlpatterns = [
        path('register', RegisterView.as_view())
    ]

    2、创建注册序列化器

      添加新文件:./Login/serializers.py,内容如下所示:

    from rest_framework import serializers
    from Course.models import Account      # 账户表
    import hashlib      #
    
    
    class RegisterSerializer(serializers.ModelSerializer):
        # 注册序列化器
        class Meta:
            model = Account
            fields = "__all__"
    
        def create(self, validated_data):
            # 重写pwd,用md5加盐
            pwd = validated_data["pwd"]
            pwd_salt = "mao_password" + pwd
            md5_str = hashlib.md5(pwd_salt.encode()).hexdigest()    # hexdigest方法拿到md5的str
            user_obj = Account.objects.create(username=validated_data["username"], pwd=md5_str)
            return user_obj

    3、配置消息响应

      在工程根目录创建utils目录,添加base_response.py文件:

    class BaseResponse(object):
    
        def __init__(self):
            self.code = 1000    # 默认码1000
            self.data = None
            self.error = None   # 错误信息
    
        @property   # 方法变属性
        def dict(self):
            print('222', self.__dict__)
            return self.__dict__

      发送请求,可以看到这里dict函数中会打印如下信息:222 {'code': 1000, 'data': {'id': 33, 'username': 'alex', 'pwd': '7ab71bb07cb065c4f5261ea81159c100'}, 'error': None}

    4、编写注册视图

      在./Login/views.py中编写注册视图:

    from rest_framework.views import APIView
    from rest_framework.response import Response
    from django.http import JsonResponse, HttpResponse
    from .serializers import RegisterSerializer    # 引入序列化器
    from utils.base_response import BaseResponse
    # Create your views here.
    
    
    class RegisterView(APIView):
    
        def post(self, request):
            res = BaseResponse()   # 实例化response
            # 用序列化器做校验
            ser_obj = RegisterSerializer(data = request.data)
            if ser_obj.is_valid():
                # 检验通过
                ser_obj.save()
                res.data = ser_obj.data
            else:
                # 检验失败
                res.code = 1020
                res.error = ser_obj.errors
            print('1111', res.data, res.dict)
            return Response(res.dict) 

      发送请求会打印如下信息:1111 {'id': 33, 'username': 'alex', 'pwd': '7ab71bb07cb065c4f5261ea81159c100'} {'code': 1000, 'data': {'id': 33, 'username': 'alex', 'pwd': '7ab71bb07cb065c4f5261ea81159c100'}, 'error': None}

    5、测试注册请求

      

    二、登录接口

      因为HTTP请求是无状态的,要区分用户,需要给用户发一个会话标识。前后端不分离的项目,是用cookie和session来解决这个问题。对于前后端分离的项目,则通常是给用户生成一个唯一标识——token令牌。

    1、配置路由和redis连接池

      在Login/urls.py中添加登录路由:

    from django.urls import path
    from .views import RegisterView, LoginView, TestView
    
    
    urlpatterns = [
        path('register', RegisterView.as_view()),
        path('login', LoginView.as_view()),
    ]

      创建utils/redis_pool.py,配置redis连接池:

    import redis
    
    POOL = redis.ConnectionPool(host='127.0.0.1', port=6379, decode_responses=True,
                                max_connections=10)    # 最大连接数

    2、登录视图

    from rest_framework.views import APIView
    from rest_framework.response import Response
    from .serializers import RegisterSerializer    # 引入序列化器
    from utils.base_response import BaseResponse
    from Course.models import Account
    from utils.redis_pool import POOL
    import redis
    import uuid
    
    class LoginView(APIView):
        def post(self, request):
            res = BaseResponse()
            username = request.data.get("username", "")
            pwd = request.data.get("pwd", "")
            user_obj = Account.objects.filter(username=username, pwd=pwd).first()  # 查询用户表拿到用户对象
            if not user_obj:
                res.code = 1030
                res.error = "用户名或密码错误"
                return Response(res.dict)
            # 用户登录成功生成一个token写入redis
            # 写入redis   token(唯一): user_id
            conn = redis.Redis(connection_pool=POOL)
            try:
                token = uuid.uuid4()   # 生成随机字符串,类型是:<class 'uuid.UUID'>
                conn.set(str(token), user_obj.id, ex=120)   # ex:过期时间120秒
                res.data = token
            except Exception as e:
                print(e)
                res.code = 1031
                res.error = "创建令牌失败"
            return Response(res.dict)

      注意uuid不能作为redis的key,需要转化为字符串、数字等数据类型。

      另外需要注意到conn.set()方法的参数:

    class Redis(object):
        def set(self, name, value, ex=None, px=None, nx=False, xx=False):
            """
            Set the value at key ``name`` to ``value``
    
            ``ex`` sets an expire flag on key ``name`` for ``ex`` seconds.
    
            ``px`` sets an expire flag on key ``name`` for ``px`` milliseconds.
    
            ``nx`` if set to True, set the value at key ``name`` to ``value`` only
                if it does not exist.
    
            ``xx`` if set to True, set the value at key ``name`` to ``value`` only
                if it already exists.
            """

      在Redis中设置值,默认,不存在则创建,存在则修改。各个参数的含义:

    • ex:过期时间(秒)
    • px:过期时间(毫秒)
    • nx:如设置为True,则只有name不存在时,当前set操作才执行,值存在,就修改不了,执行没有效果。
    • xx:如设置为True,则只有name存在时,当前set操作才执行,值存在才能修改,值不存在,不会设置新值。

    3、登录测试

      图中data就是生成的token。

      

    三、认证接口

      校验请求头中携带的token信息。

    1、添加认证测试路由

      在/Login/urls.py中添加认证测试路由:

    from django.urls import path
    from .views import RegisterView, LoginView, TestView
    
    
    urlpatterns = [
        path('register', RegisterView.as_view()),
        path('login', LoginView.as_view()),
        path('test_auth', TestView.as_view()),
    ]

    2、添加自定义登录验证

      在utils中添加my_auth.py文件:

    from rest_framework.authentication import BaseAuthentication
    from rest_framework.exceptions import AuthenticationFailed
    from Course.models import Account
    from .redis_pool import POOL
    import redis
    
    CONN = redis.Redis(connection_pool=POOL)
    
    class LoginAuth(BaseAuthentication):
        def authenticate(self, request):
            # 从请求头中获取前端带过来的token
            token = request.META.get("HTTP_AUTHENTICATION", "")   # request.META是一个Python字典,包含本次HTTP请求的Header信息
            if not token:
                raise AuthenticationFailed("没有携带token")
            # 有token,去redis中比对
            user_id = CONN.get(str(token))    # 取不到值会报:None
            if user_id == None:
                # token不合法
                raise AuthenticationFailed("token已经过期")
            user_obj = Account.objects.filter(id=user_id).first()
            return user_obj, token

    3、添加测试认证视图

    from utils.my_auth import LoginAuth
    
    class TestView(APIView):
        authentication_classes = [LoginAuth, ]   # 局部认证,该接口必须登录认证
    
        def get(self, request):
            return Response("认证测试")

    4、测试认证

      通过POST   http://127.0.0.1:8008/api/login,获取到token信息后,执行测试认证如下所示:

      

       在登录视图中曾设置超时时间120秒,120秒后再次测试认证如下所示:

      

  • 相关阅读:
    js数组合并
    火狐浏览器打开新标签不断刷新,怎么解决?
    python获取当前路径
    python模块os
    python模块sys
    python玩丢手绢问题,出局的顺序
    python list元素为dict时的排序
    利用等概率Rand5产生等概率Rand3(转)
    python垃圾回收机制(转)
    负数在计算机中如何表示?(转)
  • 原文地址:https://www.cnblogs.com/xiugeng/p/12092798.html
Copyright © 2020-2023  润新知