用戶登錄和認證組件

用戶登錄,把token放到數據庫中前端

import uuid
import redis
import datetime
from rest_framework.views import APIView
from rest_framework.response import Response
from app02 import models
from django.contrib import auth
from app02.utils.myexception import MyErrorException

# 點擊登錄,滑動驗證提交數據,返回True或者False
from app02.utils.captcha_verify import verify

redis_conn = redis.Redis(decode_responses=True)

class Login(APIView):

    def post(self,request):
        # 獲取前端發過來的數據,驗證須要的三個數據是否成功,成功返回True
        status = verify(request.data)
        if not status:
            raise MyErrorException(1010,'滑動驗證失敗')

        # 200:成功  400:失敗
        login_dic = {'code': None, 'username': None, 'msg': None, 'token': None}
        uname = request.data.get('username')
        upwd = request.data.get('password')
        # 直接驗證UserInfo表,且密碼是加密去進行驗證的,獲得的仍是用戶信息的對象
        userinfo_obj = auth.authenticate(username=uname,password=upwd)
        if userinfo_obj:

            # 若是登錄過的用戶,再次登錄的話,數據庫中token值會變化,
            # 則redis裏面的token必須刪除掉,由於已經沒用了,用戶再用這個token訪問
            # 就會告訴用戶認證失敗.
            try :
                if redis_conn.get(userinfo_obj.auth_token.key) :
                    redis_conn.delete(userinfo_obj.auth_token.key)

            except Exception as e:
                pass

            # 設置一個隨機的字符串,用來賦值給token
            random_str = uuid.uuid4()

            #(1)添加數據到token表
            models.Token.objects.update_or_create(
                #  驗證帳號密碼成功,就把token和時間(添加或更新)到表中
                defaults={'key':random_str,'created':datetime.datetime.now()},
                user = userinfo_obj
            )

            login_dic['code'] = 200
            login_dic['username'] = uname
            login_dic['msg'] = '登錄成功'
            login_dic['token'] = random_str

            return Response(login_dic)

        else :
            login_dic['code'] = 400
            login_dic['msg'] = '登錄失敗'

            return Response(login_dic)

  

認證組件,數據庫中token過時時間14天,redis中存放的 token,用戶id(k,v) 過時時間爲7天python

import datetime
import pytz
import redis
from app02 import models
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed


# 設置一個redis緩存數據庫的內存
redis_conn = redis.Redis(decode_responses=True)


# 認證組件
class My_Authentication(BaseAuthentication):

    def authenticate_header(self, request):
        pass

    def authenticate(self, request):

        # 請求的token放到了請求頭裏面
        # 名字 Authentication:token ,請求頭裏面的內容都會被自動處理成HTTP_大寫的鍵
        # token = request.META.get('HTTP_AUTHENTICATION')

        # 請求路徑?後面的值,GET.get拿取路徑(?token=...)的token值
        token = request.query_params.get('token')

        # 來redis數據庫來取token爲鍵的值,若是存在,說明緩存中的token數據有效,認證成功
        # 若是沒有值得話,代碼繼續向下執行
        redis_user_id = redis_conn.get(token)
        if redis_user_id :
            print('redis緩存認證成功')
            # 返回這個request.user爲token對應的用戶信息的id, request.auth爲token值
            return redis_user_id,token

        # 從數據庫中進行驗證,看是否能夠認證成功
        token_obj = models.Token.objects.filter(key=token).first()

        if token_obj :
            # 把當前的時間進行轉換,由於數據庫存的時間是通過地區轉換的時間,轉換才能加減
            now = datetime.datetime.now()
            now = now.replace(tzinfo=pytz.timezone('UTC'))
            # 建立或更新token時的時間
            created_time = token_obj.created
            # 此次使用token,距離建立token表的時間有多久
            right_time = now - created_time
            # 若是距離token表的時間超過了14天,則說明token表雖然存在,可是過時了.
            if right_time > datetime.timedelta(days=14):
                raise AuthenticationFailed('認證過時了')

            else :
                # 此次使用距離token值過時還有多少時間
                # 這個時間是爲了避免讓redic過時時間超過數據庫的過時時間
                lift_time = datetime.timedelta(days=14) + created_time - now
                # 把token做爲鍵,token對應的用戶信息id做爲值,添加到redis
                # token緩存在redis裏面是7天,在數據庫中是14天
                redis_conn.set(token,token_obj.user.pk,min(lift_time.total_seconds(),7*24*3600))

                print('數據庫token認證')

                # 返回這個request.user爲token對應的用戶信息的id,request.auth爲token值
                return token_obj.user.pk,token

        #(若是數據庫token表中驗證路徑中token的值不存在}
        else:
            raise AuthenticationFailed('認證失敗')
相關文章
相關標籤/搜索