认证组件
jwt只负责认证和签发token,对于权限、频率的校验还是走但drf的permmsion和throtting或自定义的校验规则
认证可以配置在局部、全局,但是因为用户登录后所访问的接口都要携带token 进行验证,所以配在全局比较合适,不然我们所写的视图类要一一配置,很麻烦;而权限的校验,根据用户的角色不同,对某接口访问的权限也不同,所以权限的配置只能在具体的视图类中,限制只能某一类角色能访问。
# *局部配置drf-jwt框架的认证类
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
1) authentication_classes = [JSONWebTokenAuthentication] # 视图类定义我们的认证类属性
# * settings文件全局配置drf-jwt框架的认证类
2) REST_FRAMEWORK = {
# 认证组件,走jwt的认证类
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_jwt.authentication.JSONWebTokenAuthentication'
],
}
# 如何自定义认证类、以及认证类规则?
'''
1)自定义类继承BaseAuthentication
2)重写authenticate方法
3)方法体(认证规则):
- 从请求头HTTP_AUTHORIZATION中拿token
- 没有token,返回None,游客(匿名用户)
- 有token,解析异常,抛异常AuthenticationFaild,非法用户 # 前台收到Unauthorized 401
- 有token,解析正常,返回(user, token),合法用户
使用:全局配置drf-jwt框架的认证类
'''
# 局部配置自定义的认证类,一般在全局settings中配置:
1) authentication_classes = [authentications.MyAuthentication, ] # 视图类定义我们的认证类属性
# * settings文件全局配置自定义的认证类
2) REST_FRAMEWORK = {
# 认证组件,走自定义的认证类
'DEFAULT_AUTHENTICATION_CLASSES': [
'api.authentication.MyAuthentication'
],
}
权限组件
# 视图类局部配置drf自带或自定义权限类
class MyAPIView(APIView):
permission_classes = [permissions.VIPUserPermission]
'''
1.drf自带权限类:IsAuthenticated, IsAdminUser, AllowAny, IsAuthenticatedOrReadOnly
2.自定义权限类
1)自定义类继承BasePermission
2)重写has_permission方法
3)方法体(权限规则):
- 根据需求制定判断条件
- 返回True时,代表有权限
- 返回False,代表无权限
使用:局部配置 drf提供的 或 自定义的
'''
# permission.py
class VIPUserPermission(BasePermission): # 只有vip分组用户有权限
def has_permission(self, request, view):
for group in request.user.groups.all():
if group.name.lower() == 'vip':
return True # 有权限
return False # 无权限
频率组件
"""
系统频率类
1) drf默认提供了一些频率类
AnonRateThrottle:只对游客进行频率限制
UserRateThrottle:对所有用户进行频率限制
2)如果有特殊需要,需要自定义频率类
如:对ip进行限次、对电话进行限制、对视图某些信息进行限次
"""
* 视图类局部配置drf自带或自定义频率类
class MyAPIView(APIView):
throttle_classes = [throttles.MobileRateThrottle]
# throttles.py 自定义频率类
from rest_framework.throttling import SimpleRateThrottle
class MobileRateThrottle(SimpleRateThrottle):
"""
1)1)自定义类继承SimpleThrottle
2)设置scope字符串类属性,同时在settings配置文件配置scope的字符串值对应的频率限制条件
REST_FRAMEWORK = {
# 频率组件:频率类一般做局部配置,但是频率调节在settings中配置
'DEFAULT_THROTTLE_RATES': {
'user': '5/min',
'anon': '3/min',
'mobile': '1/min'
},
}
3)重写get_catch_key方法:
返回与限制条件有关的字符串,表示限制(要根据不同的用户,动态变化的)
返回None,表示不限制(某些条件下,不要进行频率限制)
"""
scope = 'mobile'
# rate = '3/min'
def get_cache_key(self, request, view): # 继承SimpleRateThrottle类,重写get_cache_key方法
if not request.user.is_authenticated or not request.user.mobile:
return None # 匿名用户 或 没有电话号的用户 都不限制
# 只要有电话号的用户踩进行限制
return self.cache_format % {
'scope': self.scope,
'ident': request.user.mobile
}
多方式登录并签发token
'''
# 多方式登录
1)post请求走序列化类,默认当做create动作处理,所以系统字段默认会校验数据库,可能会出现 数据已存在 异常,所以要摒弃数据库的校验,需要自定义反序列化字段(其实就是去掉数据库校验而已)
username = serializers.CharField(...)
2)多个数据整体操作,不管是校验,还是类似于签发token等逻辑,都可以在全局校验钩子中完成
3)多方式登录体现在 请求的账号类型可能是用户名、邮箱或手机等,采用不同字段校验数据库即可
'''
# views.py
from rest_framework.views import APIView
from .response import APIResponse
from . import serializers
class LoginAPIView(APIView):
'''
1) token只能由登录接口签发
2) 登录接口也是APIView的子类,使用一定会进行认证、权限、频率组件的校验
结论:不管系统默认,还是全局settings配置的是何认证与权限组件,登录接口不用参与任何认证与权限的校验
所以,登录接口一定要进行认证与权限的局部禁用
'''
authentication_classes = []
permission_classes = []
def post(self,request,*args, **kwargs):
request_data = request.data
serializer = serializers.LoginModelSerializer(data=request_data)
serializer.is_valid(raise_exception=True) # 内部在全局钩子中完成token的签发
return APIResponse(results={
'username':serializer.content.get('username'),
'token':serializer.content.get('token')
})
# serializers.py
from rest_framework import serializers
from .import models
class UserModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.User
fields = ('username','email','phone')
from rest_framework_jwt.serializers import jwt_payload_handler,jwt_encode_handler
import re
class LoginModelSerializer(serializers.ModelSerializer):
# post请求,序列化默认当作create动作去校验,系统会去校验数据库该字段的的规则,发现该用户名已经存在会抛异常;
# 抛用户存在异常是多余的,所以自定义系统校验规则即可
username = serializers.CharField(max_length=64, min_length=3)
password = serializers.CharField(max_length=20, min_length=3)
class Meta:
model = models.User
fields = ('username','password')
# 用全局钩子,完成token的签发
def validate(self, attrs):
# 1) 通过username和password完成多方式登录校验,得到user对象
user = self._validate_user(attrs)
# 2) user对象包装payload载荷
payload = jwt_payload_handler(user)
# 3) payload载荷签发token
token = jwt_encode_handler(payload)
# 4)将user与token存储到serializer对象中,方便在视图类中使用
self.content = {
'username': user.username,
'token': token
}
return attrs
def _validate_user(self,attrs):
username = attrs.get('username')
password = attrs.get('password')
if re.match(r'.*@*.com',username):# 邮箱
user = models.User.objects.filter(email=username).first()
elif re.match(r'1[3-9][0-9]{9}',username):# 电话
user = models.User.objects.filter(phone=username).first()
else: # 用户名
user = models.User.objects.filter(username=username).first()
if not user or not user.check_password(password):
raise serializers.ValidationError({'message':'用户信息异常'})
return user