用戶注冊登錄認證模塊


  在項目中創建新應用: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秒后再次測試認證如下所示:

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM