DRF-jwt认证

Posted gaohuayan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DRF-jwt认证相关的知识,希望对你有一定的参考价值。

DRF-jwt认证(三大认证)

authentication认证

"""
系统:session认证
rest_framework.authentication.SessionAuthentication
ajax请求通过认证:
cookie中要携带 sessionid、csrftoken,请求头中要携带 x-csrftoken

第三方:jwt认证 
rest_framework_jwt.authentication.JSONWebTokenAuthentication
ajax请求通过认证:
请求头中要携带 authorization,值为 jwt空格token

自定义:基于jwt、其它
1)自定义认证类,继承BaseAuthentication(或其子类),重写authenticate
2)authenticate中完成
    拿到认证标识 auth
    反解析出用户 user
    前两步操作失败 返回None => 游客
    前两步操作成功 返回user,auth => 登录用户
    注:如果在某个分支抛出异常,直接定义失败 => 非法用户
"""

自定义认证类:基于jwt

from rest_framework.exceptions import AuthenticationFailed
import jwt
from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication
from rest_framework_jwt.authentication import jwt_decode_handler

class JWTAuthentication(BaseJSONWebTokenAuthentication):
    # 自定义认证类,重写authenticate方法
    def authenticate(self, request):
        # 认证通过,返回user,auth
        # 认证失败,返回None
        auth = request.META.get('HTTP_AUTH')  # 前台用auth携带token
        if not auth:
            return None

        try:
            payload = jwt_decode_handler(auth)

        # 出现jwt解析异常,直接抛出异常,代表非法用户,也可以返回None,作为游客处理
        except jwt.ExpiredSignature:
            raise AuthenticationFailed('token已过期')
        except:
            raise AuthenticationFailed('token非法')


        user = self.authenticate_credentials(payload)
        return (user, auth)

普通自定义认证类

from rest_framework.authentication import BaseAuthentication
def authenticate(self, request):
    auth = 从request中得到
    user = 从auth中得到
    if not user:
        return None
    return user, auth

permission权限

"""
系统:
1)AllowAny:允许所有用户,校验方法直接返回True
2)IsAuthenticated:只允许登录用户
    必须request.user和request.user.is_authenticated都通过
3)IsAuthenticatedOrReadOnly:游客只读,登录用户无限制
    get、option、head 请求无限制
    前台请求必须校验 request.user和request.user.is_authenticated
4)IsAdminUser:是否是后台用户
    校验 request.user和request.user.is_staff    is_staff(可以登录后台管理系统的用户)
    

自定义:基于auth的Group与Permission表
1)自定义权限类,继承BasePermission,重写has_permission
2)has_permission中完成
    拿到登录用户 user <= request.user
    校验user的分组或是权限
    前两步操作失败 返回False => 无权限
    前两步操作成功 返回True => 有权限
"""

自定义权限类:为 管理员 分组成员

from rest_framework.permissions import BasePermission

class AdminPermission(BasePermission):
    # 继承BasePermission,重写has_permission
    def has_permission(self, request, view):
        # 有权限,返回True
        # 无权限,返回False
        user = request.user
        if not user:
            return False
        # 用户是 管理员 分组 (管理员分组是Group表中的一条自定义记录)
        if not user.groups.filter(name='管理员'):
            return False
        # 登录的用户必须是自定义管理员分组成员
        return True

throttle频率

"""
系统:
1)AnonRateThrottle:对同一IP游客的限制
2)UserRateThrottle:对同一IP登录用户的限制
必须在settings.py中
'DEFAULT_THROTTLE_RATES': 
    'user': '10/min',  # 登录的用户一分钟可以访问10次
    'anon': '3/min',  # 游客一分钟可以访问3次

在视图类中:
class TempAPIView(APIView):
    ...
    throttle_classes = [AnonRateThrottle, UserRateThrottle]
    
    

自定义:基于auth的Group与Permission表
1)自定义频率类,继承SimpleRateThrottle,重写get_cache_key,明确scope
    SimpleRateThrottle已经帮我们实现了 allow_request、wait
2)scope与settings.py的DEFAULT_THROTTLE_RATES配合使用
3)get_cache_key中完成
    拿到限制信息 ident <= request中获取
    没有限制信息 返回None => 不限制
    有限制信息 返回限制信息字符串 => 有限制
"""

自定义频率类:一分钟一个手机号只允许访问一次接口

from rest_framework.throttling import SimpleRateThrottle

class ThreeMinRateThrottle(SimpleRateThrottle):
    scope = 'sms'
    def get_cache_key(self, request, view):
        # 对手机号频率限制
        ident = request.data.get('mobile')
        if not ident:  # 为发现限制条件,返回None代表不进行频率限制
            return None
        return self.cache_format % 
            'scope': self.scope,
            'ident': ident
        
    
    
# settings.py  配置文件
'DEFAULT_THROTTLE_RATES': 
    'user': '10/min',  # 登录的用户一分钟可以访问10次
    'anon': '3/min',  # 游客一分钟可以访问3次
    'sms': '1/min',

session认证 实现登录接口反序列化操作

from rest_framework.views import APIView
from django.contrib import auth

# views.py
# 登录成功后产生cookie,cookie中携带 sessionid、csrftoken
class LoginSessionAPIView(APIView):
    # 登录要禁用认证与权限
    authentication_classes = []
    permission_classes = []
    def post(self, request, *args, **kwargs):
        username = request.data.get('username')
        password = request.data.get('password')
        if not (username and password):
            return Response('信息有误')
        # user = models.User.objects.filter(username=username).first()  # type: models.User
        # user.check_password(password)
        user = auth.authenticate(request, username=username, password=password)
        if not user:
            return Response('登录失败')
        auth.login(request, user=user)
        return Response('登录成功')
    
 
# postman 请求头中要携带 x-csrftoken
from rest_framework.permissions import IsAuthenticatedOrReadOnly
from rest_framework.authentication import SessionAuthentication
class CarModelViewSet(ModelViewSet):
    queryset = models.Car.objects.filter(is_delete=False)
    serializer_class = serializers.CarModelSerializer
    permission_classes = [IsAuthenticatedOrReadOnly]
    authentication_classes = [SessionAuthentication]
    
    def create(self, request, *args, **kwargs):
        return super().create(request, *args, **kwargs)

    def destroy(self, request, *args, **kwargs):
        obj = self.get_object()
        obj.is_delete = True
        obj.save()
        return Response('删除成功')
 

# urls.py
 # 游客只可以查看,登录后可以增删改
 url(r'^cars/$', views.CarModelViewSet.as_view(
      'get': 'list',
      'post': 'create'
  ))

jwt认证 实现登录接口反序列化操作

import re
from utils.response import APIResponse
from rest_framework_jwt.serializers import jwt_encode_handler, jwt_payload_handler
class LoginJWTAPIView(APIView):
    authentication_classes = ()
    permission_classes = ()
    def post(self, request, *args, **kwargs):
        # username可能携带的不止是用户名,可能还是用户的其它唯一标识 手机号 邮箱
        username = request.data.get('username')
        password = request.data.get('password')

        # 如果username匹配上手机号正则 => 可能是手机登录
        if re.match(r'1[3-9][0-9]9', username):
            try:
                # 手动通过 user 签发 jwt-token
                user = models.User.objects.get(mobile=username)
            except:
                return APIResponse(1, '该手机未注册')

        # 邮箱登录 等

        # 账号登录
        else:
            try:
                # 手动通过 user 签发 jwt-token
                user = models.User.objects.get(username=username)
            except:
                return APIResponse(1, '该账号未注册')

        # 获得用户后,校验密码并签发token
        if not user.check_password(password):
            return APIResponse(1, '密码错误')
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)
        return APIResponse(0, 'ok', results=
            'username': user.username,
            'mobile': user.mobile,
            'token': token
        )
    
 
# 自定义认证类 JWTAuthentication
from rest_framework.permissions import IsAuthenticatedOrReadOnly
from api.authentications import JWTAuthentication
class CarV3ModelViewSet(ModelViewSet):
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticatedOrReadOnly]
    queryset = models.Car.objects.filter(is_delete=False)
    serializer_class = serializers.CarModelSerializer
    def destroy(self, request, *args, **kwargs):
        obj = self.get_object()
        obj.is_delete = True
        obj.save()
        return Response('删除成功')
    
    
# urls.py
 # 游客只可以查看,登录后可以增删改
 url(r'^v3/cars/$', views.CarV3ModelViewSet.as_view(
      'get': 'list',
      'post': 'create'
  ))

以上是关于DRF-jwt认证的主要内容,如果未能解决你的问题,请参考以下文章

drf-JWT认证

drf-jwt认证

频率类组件-认证规图分析-JWT认证-drf-jwt插件

drf-jwt第三方插件,DRF的三大认证的具体使用,多方式登陆的实现

基于drf-jwt的全局 局部 认证(校验 token)

jwt认证规则