登陆模块
认证
任何的项目都需要认证,用户输入了用户名和密码,验证通过,代表用户登录成功~~~
那HTTP请求是无状态的,下次这个用户再请求,我们是不可能识别这个用户是否登录的~~
所以我们就要有自己的方式来实现这个认证,也就是说~用户登录成功以后~~~我们给他们
生成一个随机字符串~~以后这个用户再请求~~都要携带这个随机字符串~~
我们就可以根据这个字符串进行判断这个用户是否登录~~~~
那么大家想一个问题~~就是我们给登录的用户生成的随机字符串放在哪里呢~~~
我们放哪里都可以~~目的是前端发送请求的时候带过来就可以了~~~
以前的cookie,session是我们的一种解决方案~~我们讲认证的时候也用过token的这种解决方案~~
1. 先创建redis连接池
import redis pool = redis.ConnectionPool(host='127.0.0.1', port=6379, decode_responses=True, max_connections=5)
2. 认证
from app01.models import Account import redis from utils.redis_pool import pool # 基于redis的token认证 conn = redis.Redis(connection_pool=pool) class LoginAuth(BaseAuthentication): def authenticate(self, request): # 认证通过 返回(user对象, token) # 不通过抛异常 # 1. 拿到前端传过来的token # 2. 判断token是否存在 # 3. 以及token是否过期 if request.method == 'OPTIONS': return None token = request.META.get('HTTP_AUTHENTICATION', '') if not token: raise AuthenticationFailed('没有携带token') print(conn.exists(token), 1111111111111111) print(token) if not conn.exists(token): raise AuthenticationFailed('token过期') user_id = conn.get(token) user_obj = Account.objects.filter(id=user_id).first() return (user_obj, token)
TOKEN
用户登录成功后,生成一个随机字符串token给前端返回~~~
那么前端以后都携带这个token来访问~~这样我们只需要鉴别这个token~来做认证~~
前端如果发送请求~把token放在请求头中~~我们看下我们的认证要怎么写~~
在写认证之前,我们先把登录注册功能写了~~~
1. 扩展用户表
class Account(models.Model): username = models.CharField(max_length=32, verbose_name="用户姓名", unique=True) password = models.CharField(max_length=32, verbose_name="用户密码") # head_img = models.CharField(max_length=256, default='/static/frontend/head_portrait/logo@2x.png', # verbose_name="个人头像") token = models.UUIDField(null=True, blank=True) def __str__(self): return self.username class Meta: verbose_name = "11-用户表" db_table = verbose_name verbose_name_plural = verbose_name
2. 编写登录注册视图
# 创建两个视图 一个注册的 一个登录的 from django.shortcuts import render from rest_framework.views import APIView from rest_framework.response import Response from utils.base_response import BaseResponse from .serializers import UserSerializer from course.models import Account import uuid # Create your views here. class UserView(APIView): # 注册用户 def post(self, request): res = BaseResponse() ser_obj = UserSerializer(data=request.data) if ser_obj.is_valid(): ser_obj.save() res.data = ser_obj.validated_data else: res.code = 1010 res.data = ser_obj.errors return Response(res.dict) class LoginView(APIView): # 登录视图 def post(self, request): res = BaseResponse() # 这里要获取我们的用户名密码 进行验证是否有这个用户 # 而且我们这个密码前端一定是密文传过来 我们通过密文对比进行验证 username = request.data.get("username", "") password = request.data.get("password", "") user_obj_queryset = Account.objects.filter(username=username, password=password) if not user_obj_queryset: res.code = 1003 res.error = "用户名或密码错误" try: token = uuid.uuid4() user_obj_queryset.update(token=token) res.data = token except Exception as e: res.code = 1004 res.error = "生成token失败" return Response(res.dict)
登录注册写完后, 开始写认证
from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import AuthenticationFailed from course.models import Account # django 提供的拿时间的接口 提供的是根据django配置的时区拿到的当前时间 from django.utils.timezone import now class MyAuth(BaseAuthentication): def authenticate(self, request): if request.method == "OPTIONS": return None # print(request.META) token = request.META.get("HTTP_AUTHENTICATION", "") print(token) if not token: raise AuthenticationFailed({"code": 1021, "error": "缺少token"}) user_obj = Account.objects.filter(token=token).first() if not user_obj: raise AuthenticationFailed({"code": 1020, "error": "无效的token"}) else: old_time = user_obj.create_token_time if (now() - old_time).days > 7: raise AuthenticationFailed({"code": 1020, "error": "无效的token"}) return user_obj, token
# 查看购物车是需要登录后才可以 # 所有这是一个需要认证的接口 class ShoppingCarView(APIView): authentication_classes = [MyAuth, ] # 展示购物车数据 def get(self, request, *args, **kwargs): print(request.user) return Response("test")
基于请求头的token认证就完成了~~~