#app.models.py :表结构 from django.db import models class User(models.Model): user = models.CharField(max_length=32) password = models.CharField(max_length=32) def __str__(self): return self.user class UserToken(models.Model): token = models.CharField(max_length=64) user = models.OneToOneField(to='User') #app.objectjson.py :序列化模块 from rest_framework import serializers from app import models class UserJson(serializers.ModelSerializer): class Meta: model = models.User fields = '__all__' #app.views.py from rest_framework.views import APIView from rest_framework.response import Response from app import models, common, objectjson class Login(APIView): def post(self, request): data_dic = request.data user = models.User.objects.filter(**data_dic).first() if user: # 登录成功操作token token = common.get_token() # token的数据库操作,第一次产生token是新建,再次就是更新 models.UserToken.objects.update_or_create(user=user, defaults={'token': token}) user_data = objectjson.UserJson(user).data return Response({ 'status': 0, 'message': 'login success', 'token': token, # 将token返回给前台 'results': user_data }) return Response({ 'status': 1, 'message': 'login failedr' })
#源码分析 # as_view()=> APIView.dispatch => self.initial(request,*args,**kwargs) => 封装后的drf的request => request.user => self._authenticate() => authenticate(self,reuquest)的源码根据地 #app.views.py from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import AuthenticationFailed class LoginAuthenticate(BaseAuthentication): def authenticate(self, request): # 登录逻辑:如果用户登录了,登录操作产生了token,且前后台同步了 # 登录判断:再次发生请求,没有token代表没登录,错误token,代表无效的登录,token正确才是正常的登录用户 # 如何将token取出,规定token用请求头传递 token = request.META.get('HTTP_TOKEN') result = models.UserToken.objects.filter(token=token).first() print(result) if result: # 认证通过,可以返回None(有多个认证时),可以返回两个值:user,auth return result.user, token else: # 认证失败,抛出APIException或其子类对象 raise AuthenticationFailed('认证失败') class Books(APIView): authentication_classes = [LoginAuthenticate] # 视图函数处理的逻辑 def get(self, request): # 通过认证后,用request.user拿到当前登录的用户,用request.auth拿到认证值(该值通常自定义) print(request.user) return Response({ 'status': 0, 'message': 'ok', 'results': [] })
#app.auth.py 完成校验的所有逻辑 from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import AuthenticationFailed from app import models class LoginAuthenticate(BaseAuthentication): def authenticate(self, request): token = request.META.get('HTTP_TOKEN') result = models.UserToken.objects.filter(token=token).first() print(result) if result: return result.user, token else: raise AuthenticationFailed('认证失败') class Books(APIView): #添加登录认证即可 authentication_classes = [auth.LoginAuthenticate] def get(self, request): print(request.user) return Response({ 'status': 0, 'message': 'ok', 'results': [] })
# 1.在settings.py中配置 # 全局认证 REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.BasicAuthentication', 'app.auth.LoginAuthenticate' ), } #2.所有的CBV类都不需要添加类属性:authentication_classes # 局部禁用 # 3.在不需要认证的CBV类中添加类属性:authentication_classes =[]
class Logout(APIView): # 在全局认证情况下,如果能走get方法,代表已经通过登录认证(登录状态),就可以注销 def get(self, request): models.UserToken.objects.update_or_create(user=request.user, defaults={'token': common.get_token()}) return Response({ "status": 0, "msg": 'logout success', })
# jwt: json web tokens # jwt规范:就是对token的规范,头.体.签名 ''' 1.token一定是后台产生,拥有请求认证,所以要前后台统一 2.token的组成部分通常有三分部组成:header.payload.signature 3.header:{"token的加密方式": "base64", "是否有签名": True} 加密处理后的结果 4.payload:{"账号": *, "密码": *, "发行者": *, "过期时间": *, "浏览器信息": *} 加密处理后的结果 5.signature:{"header": *, "payload": *, "salt": *} 加密处理后的结果 eg: eyJ0eXAiOiJK.eyJ1c2VyX2lkIjoxLCJ1cNTgzMDM1NDR9.4j5QypLwuf '''
# 安装 djangorestframework-jwt # -- pip3 install djangorestframework-jwt # 采用 djangorestframework-jwt框架 产生 jwt规范的 token from rest_framework_jwt.settings import api_settings def get_jwt_token(user): # 自定义生成token,基于某user对象,且该user对象必须有username、password两个字段 jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER payload = jwt_payload_handler(user) token = jwt_encode_handler(payload) return token