任何的項目都須要認證,當用戶輸入了用戶名和密碼,驗證經過,表明用戶登陸成功前端
那HTTP請求是無狀態的,下次這個用戶再請求,是不可能識別這個用戶是否登陸的redis
就要有本身的方式來實現這個認證,用戶登陸成功之後,生成一個隨機字符串,之後這個用戶再請求,都要攜帶這個隨機字符串。數據庫
就能夠根據這個字符串進行判斷這個用戶是否登陸,目的是前端發送請求的時候帶過來就能夠,之前的cookie,session是的一種解決方案,認證的時候也用過token的這種解決方案。django
用戶登陸成功後,生成一個隨機字符串token給前端返回json
那麼前端之後都攜帶這個token來訪問這樣只須要鑑別這個token來作認證後端
前端若是發送請求把token放在請求頭中看下的認證要怎麼寫服務器
序列化註冊數據cookie
測試的時候用的重寫create方法,真正開發前端傳過來的就是加密後的密文密碼
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 = validated_data["pwd"] pwd_salt = "luffy_password" + pwd # 加鹽處理 md5_str = hashlib.md5(pwd_salt.encode()).hexdigest() user_obj = Account.objects.create(username=validated_data["username"], pwd=md5_str) return user_obj
註冊,登陸的視圖session
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 from utils.my_auth import LoginAuth from utils.geetest import GeetestLib from django.http import HttpResponse import json # Create your views here. class RegisterView(APIView): def post(self, request): res = BaseResponse() # 實例化響應類 # 用序列化器作校驗 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 return Response(res.dict) 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() conn.set(str(token), user_obj.id, ex=10000) # conn.set(str(token), user_obj.id) # str(token) 用做key res.data = token except Exception as e: print(e) res.code = 1031 res.error = "建立令牌失敗" return Response(res.dict)
class BaseResponse(object): """ 封裝響應的類 """ def __init__(self): self.code = 1000 self.data = None self.error = None @property def dict(self): return self.__dict__ # 返回字典
http頭部發送的字段信息 key:valueide
在服務器接受時,須要加上前綴(而且大寫)例如 HTTP_KEY ,經過 request.META.get(HTTP_KEY) 獲取值
import redis from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import AuthenticationFailed from .redis_pool import POOL from Course.models import Account CONN = redis.Redis(connection_pool=POOL) class LoginAuth(BaseAuthentication): def authenticate(self, request): # 從請求頭中獲取前端帶過來的token。或者跟前端商量好 token = request.META.get("HTTP_AUTHENTICATION", "") # print(request.META) # print(token) if not token: raise AuthenticationFailed("沒有攜帶token") # 去redis比對 user_id = CONN.get(str(token)) # get沒有的值返回 None if user_id == None: raise AuthenticationFailed("token過時") # 從新登陸設置token user_obj = Account.objects.filter(id=user_id).first() return user_obj, token
用到的redis鏈接池
import redis POOL = redis.ConnectionPool(host="127.0.0.1", port=6379, decode_responses=True, max_connections=10)
註冊用戶測試
測試發送密碼時爲密文。前端傳過來,後端加密後再與數據庫中的比對
以token爲key存放在redis中,登陸成功後返回生成的token數據(有有效期的),下次登陸時攜帶上。後端以傳過來的token 去reids中找,能找到就拿到用戶信息,不能就說明過時須要從新登陸,或者token不存在.