–
P14day129-01 今日内容概要
P15day129-02 内容回顾
P16day129-03 rest framework之用户登录
P17day129-04 rest framework之基于token实现基本用户认证
P18day129-05 rest framework之认证基本流程源码分析
P19day129-06 rest framework之匿名用户配置
P20day129-07 rest framework之内置基本认证
P21day129-08 rest framework之认证内容总结
P22day129-09 rest framework之权限的基本使用
P23day129-10 rest framework之权限源码流程
P24day129-11 rest framework之权限的内置类
P25day129-12 rest framework之访问频率控制基本实现
P26day129-13 rest framework之访问频率控制源码流程
P27day129-14 rest framework之基于内置类实现访问频率控制
P28day129-15 rest framework之访问频率内容梳理
P29day129-016 今日作业
P14day129-01 今日内容概要 主要讲版本,认证,权限和频率 P15day129-02 内容回顾 P16day129-03 rest framework之用户登录 用自己的方法实现一个用户登录,返回token 这里数据库使用的是mysql 下面是settings文件里面内容 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'drftest', # 数据库名字 'USER': 'root', # 账号 'PASSWORD': '12345', # 密码 'HOST': '192.168.170.20', # IP 'PORT': '3306', # 端口 # 这里引擎用innodb(默认myisam) # 因为后面第三方登录时,要求引擎为INNODB # 'OPTIONS':{'init_command': 'SET storage_engine=INNODB'}, #这样设置会报错,改为 "OPTIONS": {"init_command": "SET default_storage_engine=INNODB;SET sql_mode='STRICT_TRANS_TABLES'"} } } 下面是urls.py文件 urlpatterns = [ # url(r'^admin/', admin.site.urls), url(r'api/v1/auth/$', views.AuthView.as_view()) ] 下面是models.py文件 from django.db import models class UserInfo(models.Model): user_type_choices = ( (1, '普通用户'), (2, 'VIP'), (3, 'SVIP') ) user_type = models.IntegerField(choices=user_type_choices) username = models.CharField(max_length=32) password = models.CharField(max_length=64) class UserToken(models.Model): user = models.OneToOneField(to='UserInfo') token = models.CharField(max_length=64) 下面是views.py文件 from django.http import JsonResponse from rest_framework.views import APIView from app01 import models def md5(user): import hashlib import time ctime = str(time.time()) m = hashlib.md5(bytes(user, encoding='utf-8')) m.update(bytes(ctime,encoding='utf-8')) return m.hexdigest() class AuthView(APIView): def post(self,request, *args, **kwargs): ret = {'code':1000, 'msg':None} try: user = request._request.POST.get('username') pwd = request._request.POST.get('password') obj = models.UserInfo.objects.filter(username=user, password=pwd).first() if obj: token = md5(user) models.UserToken.objects.update_or_create(user=obj, defaults={'token': token}) ret['token'] = token else: ret['code'] = 1001 ret['msg'] = '用户名或密码错误' except Exception as e: print(e) ret['code'] = 1002 ret['msg'] = '请求异常' print(ret) return JsonResponse(ret) 然后在user表中插入几条示例数据 DROP TABLE IF EXISTS `app01_userinfo`; CREATE TABLE `app01_userinfo` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_type` int(11) NOT NULL, `username` varchar(32) NOT NULL, `password` varchar(64) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of app01_userinfo -- ---------------------------- INSERT INTO `app01_userinfo` VALUES ('1', '1', 'xuhaohua', '123'); INSERT INTO `app01_userinfo` VALUES ('2', '2', 'mingruijie', '1234'); INSERT INTO `app01_userinfo` VALUES ('3', '2', 'wangyuanming', '12345'); INSERT INTO `app01_userinfo` VALUES ('4', '3', 'zhangguikun', '123456'); 然后使用postman进行测试 http://127.0.0.1:8000/api/v1/auth/ 表单内容填入用户名和密码可以测试 P17day129-04 rest framework之基于token实现基本用户认证 本节实现功能 http://127.0.0.1:8000/api/v1/auth 调用这个接口,输入用户名和密码。正确后返回给用户token http://127.0.0.1:8000/api/v1/order/?token=b80f0dea945159cbe33467f700bc6f62 用户使用token来查询订单列表,当token错误,返回权限未通过 下面是urls.py内容 from django.conf.urls import url # from django.contrib import admin from app01 import views urlpatterns = [ # url(r'^admin/', admin.site.urls), url(r'api/v1/auth/$', views.AuthView.as_view()), url(r'api/v1/order/$', views.OrderView.as_view()), ] 下面是models.py内容 from django.db import models class UserInfo(models.Model): user_type_choices = ( (1, '普通用户'), (2, 'VIP'), (3, 'SVIP') ) user_type = models.IntegerField(choices=user_type_choices) username = models.CharField(max_length=32) password = models.CharField(max_length=64) class UserToken(models.Model): user = models.OneToOneField(to='UserInfo') token = models.CharField(max_length=64) 下面是views.py里面的内容 from django.http import JsonResponse from rest_framework.views import APIView from rest_framework import exceptions from app01 import models 下面是验证登录部分 def md5(user): import hashlib import time ctime = str(time.time()) m = hashlib.md5(bytes(user, encoding='utf-8')) m.update(bytes(ctime,encoding='utf-8')) return m.hexdigest() class AuthView(APIView): def post(self,request, *args, **kwargs): ret = {'code':1000, 'msg':None} try: user = request._request.POST.get('username') pwd = request._request.POST.get('password') obj = models.UserInfo.objects.filter(username=user, password=pwd).first() if obj: token = md5(user) models.UserToken.objects.update_or_create(user=obj, defaults={'token': token}) ret['token'] = token else: ret['code'] = 1001 ret['msg'] = '用户名或密码错误' except Exception as e: print(e) ret['code'] = 1002 ret['msg'] = '请求异常' print(ret) return JsonResponse(ret) 下面是订单部分,获取订单时会查询token是否有效 ORDER_DICT = { 1:{ 'name':'apple', 'color':'blue', 'price': 18 }, 2:{ 'name':'banana', 'color':'yellow', 'price':20 } } class Authtication(object): def authenticate(self, request): token = request._request.GET.get('token') token_obj = models.UserToken.objects.filter(token=token).first() if not token_obj: raise exceptions.AuthenticationFailed('用户认证失败') return (token_obj.user, token_obj) def authenticate_header(self,request): pass class OrderView(APIView): authentication_classes = [Authtication,] def get(self, request, *args, **kwargs): ret = {'code':1000, 'msg':None, 'data':None} try: ret['data'] = ORDER_DICT except Exception as e: pass print(request.user.username, request.auth.token) return JsonResponse(ret) 当还有其他类有需要登录才能操作的需求,只需要引入:authentication_classes = [Authtication,] P18day129-05 rest framework之认证基本流程源码分析 为什么在函数中直接raise错误,drf会自动捕获 在drf的dispatch方法里面会有try except方法,子类中任何异常,这里都可以捕获,然后执行except里面方法,返回给用户错误信息 def dispatch(self, request, *args, **kwargs): try: xxxxxxx except Exception as exc: response = self.handle_exception(exc) 为什么最后的元组里面的数据会赋值给user和auth 在最里面的 def _authenticate(self): """ Attempt to authenticate the request using each authentication instance in turn. """ for authenticator in self.authenticators: #执行认证的方法 #如果认证方法异常,执行_not_authenticated方法 #如果认证成功,函数返回的元组赋值给self.user, self.auth #返回None,我不管,下一个认证来处理 try: user_auth_tuple = authenticator.authenticate(self) except exceptions.APIException: 这里如果认证失败,会给用户返回一个默认的用户,一般是匿名用户,可以自定义 self._not_authenticated() raise if user_auth_tuple is not None: self._authenticator = authenticator self.user, self.auth = user_auth_tuple return 如果数组里面是空,则执行认证失败里面方法 self._not_authenticated() P19day129-06 rest framework之匿名用户配置 主要看配置还能怎么改 如果settings里面有配置文件,先读这里面的配置文件 可以将上面的在类里面定义的list,放到settings里面,则全局生效 类的名字包括前面的路径,如果有多个认证类,都可以追加到后面去 创建单独的文件来存放认证方法 app01/utils/authen.py,里面添加下面文件 from rest_framework import exceptions from app01 import models class MyAuthtication(object): def authenticate(self, request): token = request._request.GET.get('token') token_obj = models.UserToken.objects.filter(token=token).first() if not token_obj: raise exceptions.AuthenticationFailed('用户认证失败') return (token_obj.user, token_obj) def authenticate_header(self,request): pass 然后在views.py里面将原有的注释掉,这里使用全局配置文件 class OrderView(APIView): #authentication_classes = [MyAuthtication,] def get(self, request, *args, **kwargs): #self.dispatch() ret = {'code':1000, 'msg':None, 'data':None} try: ret['data'] = ORDER_DICT except Exception as e: pass print(request.user.username, request.auth.token) return JsonResponse(ret) 在settings.py中添加 REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES":["app01.utils.authen.MyAuthtication",] } 可以达到和上面的效果一样,这个是全局配置,所有类都生效 如果某个类不想认证就能访问,比如认证接口,则在认证接口里面单独设置,list置空即可 authentication_classes = [] 就能达到效果 坑爹啊,这里有个问题,认证内容必须放到单独的文件中,刚开始放到views里面报错,怎么也找不到问题, https://stackoverflow.com/questions/30567264/custom-authentication-class-as-a-default-authentication-classes 匿名用户配置 REST_FRAMEWORK = { 'UNAUTHENTICATED_USER': lambda:'匿名用户', 'UNAUTHENTICATED_TOKEN': None, "DEFAULT_AUTHENTICATION_CLASSES":["app01.utils.authen.MyAuthtication",] } 这样后执行:print(request.user)将会返回:匿名用户 P20day129-07 rest framework之内置基本认证 from rest_framework.authentication import BaseAuthentication BaseAuthentication里面有几种已经写好的认证方式,可以参考 尤其是 class TokenAuthentication(BaseAuthentication): P21day129-08 rest framework之认证内容总结 上面的内容总结 P22day129-09 rest framework之权限的基本使用 问题:不同视图只能给不同的权限访问 这里是一个示例,两个类 order类只允许svip用户访问,必须登录 userinfo类只允许vip用户访问,必须登录 这里的操作和登录认证的过程类似 下面是app01.views文件 from django.http import JsonResponse from rest_framework.views import APIView from app01 import models from app01.utils.permission import MyPermissionVIP, MyPermissionSVIP def md5(user): import hashlib import time ctime = str(time.time()) m = hashlib.md5(bytes(user, encoding='utf-8')) m.update(bytes(ctime,encoding='utf-8')) return m.hexdigest() class AuthView(APIView): authentication_classes = [] def post(self,request, *args, **kwargs): ret = {'code':1000, 'msg':None} try: user = request._request.POST.get('username') pwd = request._request.POST.get('password') obj = models.UserInfo.objects.filter(username=user, password=pwd).first() if obj: token = md5(user) models.UserToken.objects.update_or_create(user=obj, defaults={'token': token}) ret['token'] = token else: ret['code'] = 1001 ret['msg'] = '用户名或密码错误' except Exception as e: print(e) ret['code'] = 1002 ret['msg'] = '请求异常' print(request.user) return JsonResponse(ret) ORDER_DICT = { 1:{ 'name':'apple', 'color':'blue', 'price': 18 }, 2:{ 'name':'banana', 'color':'yellow', 'price':20 } } class OrderView(APIView): #订单只允许svip访问 message = '订单只允许svip访问' permission_classes = [MyPermissionSVIP,] def get(self, request, *args, **kwargs): self.dispatch() ret = {'code':1000, 'msg':None, 'data':None} try: ret['data'] = ORDER_DICT except Exception as e: pass print(request.user.username, request.auth.token) return JsonResponse(ret) class UserInfo(APIView): #用户信息只允许vip访问 message = '用户信息只允许vip访问' permission_classes = [MyPermissionVIP,] def get(self, request, *args, **kwargs): ret = {'user':'test'} return JsonResponse(ret) 创建文件utils/permission.py,内容如下 class MyPermissionSVIP(object): def has_permission(self, request, view): if request.user.user_type == 3: return True return False class MyPermissionVIP(object): def has_permission(self, request, view): if request.user.user_type == 2: return True return False P23day129-10 rest framework之权限源码流程 如果没有设置,使用全局的,如果在全局中设置一个 "DEFAULT_PERMISSION_CLASSES":['app01.utils.permission.MyPermissionSVIP'] 如果单个类不需要权限就能访问,则在这个类里面将list置空 如果想自定义返回错误信息,在自定义类里面添加message变量即可, 在最里层的函数中会取这里面内容,如果自定义,使用自定义信息 P24day129-11 rest framework之权限的内置类 权限类应该都继承基础类 from rest_framework.permissions import BasePermission 权限类返回值: - True:有权访问 - False:无权访问,里面的方法会抛出无权访问异常 P25day129-12 rest framework之访问频率控制基本实现 P26day129-13 rest framework之访问频率控制源码流程 P27day129-14 rest framework之基于内置类实现访问频率控制 P28day129-15 rest framework之访问频率内容梳理 P29day129-016 今日作业 py程序在反代后面,源ip不好获取,以上内容暂时用不到
–
–
–
评论前必须登录!
注册