用户登陆,把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天
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('认证失败')