基于用户传入的token进行认证
需求:对于一些api需要用户登录成功之后才能访问,有些api又不需要登录就能访问
解决思路: a:创建两张表
b.用户来访问的时候带上服务器发给用户token值
1.创建models.py
1 from django.db import models 2 3 4 class UserInfo(models.Model): 5 user_choices_type = ( 6 (1, '普通用户'), 7 (2, 'vip用户'), 8 (3, 'svip用户'), 9 ) 10 user_type = models.IntegerField(choices=user_choices_type) 11 username = models.CharField(max_length=32, unique=True) 12 password = models.CharField(max_length=64) 13 14 15 class UserToken(models.Model): 16 user = models.OneToOneField(to='UserInfo', on_delete=models.CASCADE) 17 token = models.CharField(max_length=64)
2.urls.py
1 urlpatterns = [ 2 re_path('api/v1/auth/$', views.AuthView.as_view()), 3 re_path('api/v1/order/$', views.OrderView.as_view()), 4 ]
3.认证类
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 from rest_framework.authentication import BaseAuthentication 5 from rest_framework.exceptions import AuthenticationFailed 6 from api import models 7 8 9 class Authticate(BaseAuthentication): 10 11 def authenticate(self, request): 12 token = request._request.GET.get('token') 13 token_obj = models.UserToken.objects.filter(token=token).first() 14 if not token_obj: 15 raise AuthenticationFailed('用户认证失败') 16 return (token_obj.user, token_obj) 17 18 def authenticate_header(self, request): 19 """ 20 认证失败之后返回给浏览器响应头 21 :param request: 22 :return: 23 """ 24 pass
4.views.py
1 from django.http import JsonResponse 2 from rest_framework.views import APIView 3 from api import models 4 5 6 order_dict = { 7 1: { 8 'name': '拉皮条', 9 'age': 18, 10 'gengder': '男', 11 'content': '买了个充气的' 12 }, 13 2: { 14 'name': '热狗', 15 'age': 18, 16 'gengder': '男', 17 'content': '买了个非酋' 18 } 19 } 20 21 22 def md5(user): 23 import hashlib 24 import time 25 ctime = str(time.time()) 26 m = hashlib.md5(user.encode('utf8')) 27 m.update(ctime.encode('utf8')) 28 return m.hexdigest() 29 30 31 class AuthView(APIView): 32 """ 33 登录相关的逻辑 34 """ 35 authentication_classes = [] 36 def post(self, request, *args, **kwargs): 37 38 ret = {'code': 1000, 'msg': None, 'token': None} 39 try: 40 user = request._request.POST.get('username') 41 pwd = request._request.POST.get('password') 42 obj = models.UserInfo.objects.filter(username=user, password=pwd).first() 43 if not obj: 44 ret['code'] = 1001 45 ret['msg'] = '用户名或者密码错误' 46 # 为登录用户创建token 47 token = md5(user) 48 models.UserToken.objects.update_or_create(user=obj, defaults={'token': token}) 49 ret['token'] = token 50 except Exception as e: 51 ret['code'] = 1002 52 ret['msg'] = '请求异常' 53 54 return JsonResponse(ret) 55 56 57 class OrderView(APIView): 58 """ 59 订单相关业务 60 """ 61 62 def get(self, request, *args, **kwargs): 63 print(request.user, request.auth) 64 ret = {'code': 2000, 'msg': None, 'data': None} 65 try: 66 ret['data'] = order_dict 67 except Exception as e: 68 ret['code'] = 2001 69 ret['msg'] = '请求异常' 70 return JsonResponse(ret)
5.settings.py
1 REST_FRAMEWORK = { 2 'DEFAULT_AUTHENTICATION_CLASSES': ['api.utils.auth.Authticate', ], 3 # 'DEFAULT_AUTHENTICATION_CLASSES': [], # AnonymousUser None配置文件中有 4 # 'UNAUTHENTICATED_USER': lambda: '匿名用户', 5 # 'UNAUTHENTICATED_USER': None, 6 # 'UNAUTHENTICATED_TOKEN': None, 7 }
总结:实现的思路就是每次用户登录的时候,需要带上token值,如果没有带就获取不到相应的资源,如果登录成功就给用户创建token值,并且给用户返回,若第二次用户登录就给用户更新,同时也返回给用户,当然用户没登录成功就给用户返回相关的错误信息。
认证组件的源码流程分析
请求进来先找自己的dispatch方法,然而没有,就走父类的APIView,发现有dispatch方法 ,执行self.initialize_request方法,封装request,之前还有解析器-->执行self.get_authenticators()-->[auth() for auth in self.authentication_classes]列表式生成一个个认证对象--->authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES默认去配置文件中,若自己有,执行initial(self, request, *args, **kwargs)方法---->执行self.perform_authentication(request)------>request.user这是个静态属性----->self._authenticate()---->_authenticate(self)---->authenticator.authenticate(self)---->返回必须是一个元组。
_authenticate(self):循环认证类的所有对象,执行认证类的authenticate方法,1.如果authenticate方法抛异常,执行self._not_authenticated()2.有返回值必须是元组(request.user,request.auth)3.如果返回None,进入下一个认证对象处理,如果全部都是None跳出循环,执行_not_authenticated(self)方法去配置文件了if api_settings.UNAUTHENTICATED_USER就是匿名用户AnonymousUser,否则是slef.user=None,self.auth = api_settings.UNAUTHENTICATED_TOKEN()为None否则self.auth=None。
内置认证类
BaseAuthentication
我们写认证类的时候,必须继承它实现authenticate方法不写直接抛错,authenticate_header这个方法是认证失败给用户返回的响应头信息401认证不通过,也是必须写的方法,但是一般不做什么处理。
BasicAuthentication
这个类大概是通过浏览器把用户的密码和用户名通过base64加密之后发给服务端。
SessionAuthentication
通过django的session做的认证。
TokenAuthentication
其实跟我哪个认证token差不多。
RemoteUserAuthentication
这个类是通过认证http请求头。