Restframework的认证,权限,节流
1.认证
流程:请求到达REST framework的时候,会对request进行二次封装,在封装的过程中会对客户端发送过来的request封装进认证,选择,解析等功能。request方法封装完成之后,执行initial方法时,又会再次对客户端的请求执行认证操作,确保请求的合法性
生命周期:
发送请求-->Django的wsgi-->中间件-->路由系统_执行CBV的as_view(),就是执行内部的dispath方法-->在执行dispath之前,有版本分析 和 渲染器-->在dispath内,对request封装-->版本-->认证-->权限-->限流-->视图-->如果视图用到缓存( request.data or request.query_params )就用到了 解析器-->视图处理数据,用到了序列化(对数据进行序列化或验证) -->视图返回数据可以用到分页
自定义认证:
models.py(创建完后自行添加几条数据)
from django.db import models # 用户信息
class UserInfo(models.Model):
username = models.CharField(max_length=32, unique=True)
password = models.CharField(max_length=32)
# 小整数字段
type = models.SmallIntegerField(
choices=((1,"普通用户"),(2,"VIP用户")),
default=1
) # 用户认证
class Token(models.Model):
token = models.CharField(max_length=32)
user = models.OneToOneField(to="UserInfo") # 评论表
class Comment(models.Model):
content = models.CharField(max_length=128)
user = models.ForeignKey(to='UserInfo', on_delete=models.CASCADE, default=1)
url.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'login/$', views.LoginView.as_view()),
] from rest_framework.routers import DefaultRouter
router = DefaultRouter()
# 注册路由,表示路径comment对应视图函数CommentViewSet
router.register(r'comment', views.CommentViewSet)
urlpatterns += router.urls
views.py
import time
import hashlib
from app01 import models
from rest_framework.views import APIView
from rest_framework.response import Response
from app01 import app01_serializers # 自定义的序列化
from rest_framework.viewsets import ModelViewSet # 生成token函数
def get_token_code(username):
"""
根据用户名和时间戳生成用户登陆成功的随机字符串
:param username:字符串格式的用户名
:return:字符串格式的token
"""
timestamp = str(time.time())
m = hashlib.md5(bytes(username,encoding="utf-8"))
m.update(bytes(timestamp,encoding="utf-8"))
return m.hexdigest() class LoginView(APIView):
"""
1. 接收用户发过来(POST)的用户名和密码数据
2. 校验用户名密码是否正确
- 成功就返回登陆成功(发Token)
- 失败就返回错误提示
"""
def post(self,request):
res = {"code":0}
username = request.data.get("username")
password = request.data.get("password")
user_obj = models.UserInfo.objects.filter(
username=username,
password=password,
).first()
if user_obj:
token = get_token_code(username)
# 保存token
# 如果有记录就更新defaults里传的参数, 没有记录就用defaults里传的参数创建一条数据
models.Token.objects.update_or_create(defaults={"token": token}, user=user_obj)
# 将token返回给用户
res["token"] = token
else:
res["token"] = 1
res["error"] = "用户名或密码错误"
return Response(res) # 对文章进行增删改查操作
class CommentViewSet(ModelViewSet):
queryset = models.Comment.objects.all()
serializer_class = app01_serializers.CommentSerializer
app01_serializers.py(app下创建的py文件)
from app01.models import Comment
from rest_framework import serializers class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = "__all__"
通过在postman上模拟post发送请求登录,如果用户名和密码正确的话,会生成token值,下次该用户再登录时,token的值就会更新
当用户名或密码错误时,抛出异常
上面看是没有什么问题,但是任何用户都能访问评论,如何只让登陆用户查看信息呢?这里我们就需要添加一个认证类
在app01(应用名)目录下创建目录utils,在此目录下创建auth.py,用于放置自定义的认证类
auth.py
from app01 import models
from rest_framework.authentication import BaseAuthentication
# 导入处理REST框架引发的异常的模块
from rest_framework.exceptions import AuthenticationFailed class MyAuth(BaseAuthentication):
# 重写BaseAuthentication里的方法
def authenticate(self, request):
if request.method in ["POST","PUT",]:
# 取token值
token = request.data.get("token")
# 去数据库查询有没有这个token
token_obj = models.Token.objects.filter(token=token).first()
if token_obj:
# token_obj有2个属性,详见models.py中的Token。
# return后面的代码,相当于分别赋值。例如a=1,b=2等同于a,b=1,2
# return多个值,返回一个元组
# 在rest framework内部会将这两个字段赋值给request,以供后续操作使用
return token_obj.user, token
else:
raise AuthenticationFailed("无效的token")
else:
return None, None
views.py下的CommentViewSet类:
# app01.utils.auth表示app01目录下的utils下的auth.py
from app01.utils.auth import MyAuth class CommentViewSet(ModelViewSet):
queryset = models.Comment.objects.all()
serializer_class = app01_serializers.CommentSerializer
authentication_classes = [MyAuth, ] # 局部使用认证方法MyAuth
验证:发送一个空的post请求或者错误的post请求
发送正确的post请求:
## 以上只是做了一个局部的认证,对于全局认证:
需要在setting中配置:
REST_FRAMEWORK = {
# 表示app01-->utils下的auth.py里面的MyAuth类
"DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.auth.MyAuth", ]
}
这时候CommentViewSet中的authentication_classes就可以注释掉了,效果也是一样的
2.权限
自定义我们的权限,如只有让VIP用户才能看的内容
注意:这里我把上面的认证权限设立在了全局,在settings里面设置全局认证,所有业务都需要经过认证,只有这样配置执行下面代码时你的permissions.py下的request才不会始终返回AnonymousUser(匿名用户)
2.1 验证一
permissions.py(在utils文件下下创建方认证文件)
from rest_framework.permissions import BasePermission class MyPermission(BasePermission):
# 重写BasePermission里的方法,点击源码,里面只给了框架
def has_permission(self, request, view):
"""
判断该用户有没有权限
"""
print('我要进行自定义的权限判断啦....')
print(request)
print(request.user)
return True
views.py(其他的不变动)
from app01.utils.permissions import MyPermission
class CommentViewSet(ModelViewSet):
queryset = models.Comment.objects.all()
serializer_class = app01_serializers.CommentSerializer
permission_classes = [MyPermission, ]
发送get请求,request.user 打印值为None
发送post请求,并携带之前用户登录加载的token值,request.user 打印值为UserInfo object
2.2验证二(验证request到底是啥玩意)
修改permission.py
class MyPermission(BasePermission):
# 重写BasePermission里的方法,点击源码,里面只给了框架
def has_permission(self, request, view):
print('我要进行自定义的权限判断啦....')
print(request)
print(request.user)
print(request.user.username)
print(request.user.type)
return True
再次发送相同的post请求,request.user.username打印值为携带当前token值的用户名
request.user.type打印值为携带当前token值的用户的type
Request有个user方法,加 @property 表示调用user方法的时候不需要加括号“user()”,可以直接调用:request.user
在rest framework内部会将这两个字段赋值给request,以供后续操作使用
return (token_obj.user,token_obj) 上面的return的值,来源于app01\utils\auth.py里面的MyAuth类中的return token_obj.user, token
通过认证之后,它会request进行再次封装,所以调用request.user时,得到了一个对象
这个对象就是执行models.Token.objects.filter(token=token).first()的结果
如果ORM没有查询出结果,它就一个匿名用户(AnonymousUser)!
2.3 验证三(只有VIP用户登录才能操作内容)
修改permission.py,仅限VIP用户登录操作文章内容
permission.py
from rest_framework.permissions import BasePermission class MyPermission(BasePermission):
# 重写BasePermission里的方法,点击源码,里面只给了框架
# 自定义输出语句
message = '快滚,没有权限!'
def has_permission(self, request, view):
if request.method in ['POST', 'PUT', 'DELETE']:
print(request.user.username)
print(request.user.type)
if request.user.type == 2: # 是VIP用户
return True
else:
return False
else:
return True
效果(VIP客户):
comment内容:
非VIP客户:
2.4 验证四(使用普通用户的token发送delete类型的请求)
重写BasePermission里的has_object_permission方法
from rest_framework.permissions import BasePermission class MyPermission(BasePermission):
# 重写BasePermission里的方法,点击源码,里面只给了框架
# 自定义输出语句
message = '快滚,没有权限!'
def has_permission(self, request, view):
print("源码里面这个方法下啥也没有")
return True # 源码中给的另一方法
def has_object_permission(self, request, view, obj):
"""
判断当前评论用户的作者是不是你当前的用户
只有是本人才能修改或者删除自己的评论
"""
print('这是在自定义权限类中的has_object_permission')
print(obj.id)
if request.method in ['PUT', 'DELETE']:
if obj.user == request.user:
# 当前要删除的评论的作者就是当前登陆的用户
return True
else:
return False
else:
return True
使用普通用户的token发送delete类型的请求:
使用VIP用户的token发送delete类型的请求,返回结果为空,删除成功
全局设置权限:
在setting中配置:
REST_FRAMEWORK = {
# 表示app01-->utils下的auth.py里面的MyPermission类
"DEFAULT_PERMISSION_CLASSES": ["app01.utils.permission.MyPermission", ]
}
再注释掉局部设置
3.限制(节流)
对IP做限制,用户固定时间内只能访问固定次数
在app01\utils下面创建throttle.py
throttle.py
from rest_framework.throttling import BaseThrottle
import time D = {} # {'127.0.0.1': [1533302442, 1533302439,...]} class MyThrottle(BaseThrottle):
# 点进源码直接看当中携带的参数
def allow_request(self, request, view):
"""
Return `True` if the request should be allowed, `False` otherwise.
"""
# 访问当前IP
ip = request.META.get('REMOTE_ADDR')
print(ip)
now = time.time()
if ip not in D:
D[ip] = [] # 初始化一个空的访问历史列表 history = D[ip]
# 当历史列表中有元素,并且当前时间戳减去最后一个元素的时间戳大于10
while history and (now - history[-1]) > 10:
history.pop()
# 判断最近10秒内的访问次数是否超过了阈值(3次)
if len(history) >= 3:
return False
else:
# 把这一次的访问时间加到访问历史列表的第一位
D[ip].insert(0,now)
return True
views.py
from app01.utils.throttle import MyThrottle
class CommentViewSet(ModelViewSet):
queryset = models.Comment.objects.all()
serializer_class = app01_serializers.CommentSerializer
throttle_classes = [MyThrottle, ] # 局部配置
或全局配置,在setting中加入:
REST_FRAMEWORK = {
"DEFAULT_THROTTLE_CLASSES": ["app01.utils.throttle.MyThrottle", ]
}
效果:使用postman发送GET请求,十秒内点击数大于三次就会提示你:
等待10秒后又能使用
3.1使用内置的SimpleRateThrottle类
throttle.py
from rest_framework.throttling import SimpleRateThrottle
class MyThrottle(SimpleRateThrottle):
scope = "rate" # rate是名字,可以随便定义!
def get_cache_key(self, request, view):
return self.get_ident(request) # 远程IP地址
修改全局配置setting:
REST_FRAMEWORK = {
"DEFAULT_THROTTLE_CLASSES": ["app01.utils.throttle.MyThrottle", ],
"DEFAULT_THROTTLE_RATES": {
# rate对应的是throttle.py里面MyThrottle定义的scope属性的值
"rate": "3/m", # 1分钟3次
}
}
效果:
更过关于节流的代码原理性操作猛戳这里
Restframework的认证,权限,节流的更多相关文章
- rest-framework框架 -- 认证权限流程源码
认证权限 解析BaseAuthentication源码 # 做认证权限 from rest_framework import exceptions from ..models import * cla ...
- (四) DRF认证, 权限, 节流
一.Token 认证的来龙去脉 摘要 Token 是在服务端产生的.如果前端使用用户名/密码向服务端请求认证,服务端认证成功,那么在服务端会返回 Token 给前端.前端可以在每次请求的时候带上 To ...
- S11 day 96 RestFramework 之认证权限
一.设计一个简易的登录 1. 建立一个模型 class UserInfo(models.Model): username =models.CharField(max_length=) password ...
- drf(请求封装/认证/权限/节流)
1.请求的封装 class HttpRequest(object): def __init__(self): pass @propery def GET(self): pass @propery de ...
- rest-framework框架——认证、权限、频率组件
一.rest-framework登录验证 1.models.py添加User和Token模型 class User(models.Model): name = models.CharField(max ...
- restful知识点之三restframework认证-->权限-->频率
认证.权限.频率是层层递进的关系 权限业务时认证+权限 频率业务时:认证+权限+频率 局部认证方式 from django.conf.urls import url,include from djan ...
- 【原】无脑操作:IDEA + maven + Shiro + SpringBoot + JPA + Thymeleaf实现基础认证权限
开发环境搭建参见<[原]无脑操作:IDEA + maven + SpringBoot + JPA + Thymeleaf实现CRUD及分页> 需求: ① 除了登录页面,在地址栏直接访问其他 ...
- Kafka认证权限配置(动态添加用户)
之前写过一篇Kafka ACL使用实战,里面演示了如何配置SASL PLAINTEXT + ACL来为Kafka集群提供认证/权限安全保障,但有一个问题经常被问到:这种方案下是否支持动态增加/移除认证 ...
- django 之(三) --- 认证|权限
用户模块 登陆注册1:Django2.0 [ 1:N ] user/url.py from django.urls import path from user.views0 import UserT ...
随机推荐
- 微信小程序转百度小程序修改
百度小程序对比微信小程序(最初版):[设备]项里没有内存监控.iBeacon.wifi.蓝牙.用户截屏.手机联系人.NFC[位置]项里没有打开地图选择位置[界面]项里没有绘图功能.没有节点信息获取功能 ...
- smarty中函数的使用以及二维数组的使用
1.虽然讲究前后台分离,但是如果如果有的项目,前后台分离的不彻底,或者有些必须要在HTML中处理,还是要用到PHP中的函数的: <% if $Role|in_array:$menuRole[$c ...
- SpringSecurity 3.2入门(10)自定义权限控制认证及授权的过程
上一章的代码实现还存在一些问题,如角色表.权限表的用处没有体现出来,但是已经能完成URL拦截功能,后面将会继续完善认证及授权的过程. 认证及授权的过程如下: 1.容器启动,MyInvocationSe ...
- 解决:用nvm成功安装nodejs,却无法使用nvm和node命令
问题 一个前端朋友,用Macbook pro成功安装nvm以及node环境,系统查询不到 $ nvm $ -bash: nvm: command not found $ node $ -bash: n ...
- Java线程入门第一篇
Java线程的状态有6种 1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法. 2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running) ...
- 编程提取字符串"Java is a programming language"中的各个单词,并打印输出。
import java.lang.String; import java.util.StringTokenizer; public class StringGetWord{ /* 编程提取字符串&qu ...
- Dubbo与Zookeeper、Spring整合使用 maven+springmvc+dubbo+zookeeper
为什么要用dubbo? 还是让官方来解释吧: http://dubbo.io/User+Guide-zh.htm http://dubbo.io/ 一般 nginx+tomcat | - ...
- lxml模块(应用xpath技术)
一.lxml介绍 第三方库lxml是第一款表现出高性能特征的python xml库,天生支持Xpath1.0.XSLT1.0.定制元素类,甚至python风格的数据绑定接口.lxml是通过Cpytho ...
- Visual Studio Code 设置中文语言版本
设置方法有两种: 方法一1.选择扩展 搜索“Language”,在下列选项选择 Chinese (Simplified) Language Pack for Visual Studio Code安装, ...
- C++基础--extern的用法
extern作为外部变量扩展的用法: 1. 主要作用是扩展变量或者函数的应用范围: 2. extern的用法是相对于全局变量而言: 3. 在看到extern这个关键字的时候说明这个变量已经在别的源文件 ...