三大认证

前戏准备

RBAC基于角色权限访问控制

六表

其中的User与Permission关系表主要解决角色表可能不能满足特殊情况下的用户权限

content_type

给Django中的所有模块中的所有表进行编号存储到content_type表中

应用一

django的content_type存放模块名和表名,没增加一张表会在表内多一条记录

应用二

解决什么问题

一个表和多个表进行关联,但具体随着业务的加深,表不断的增加,关联的数量不断的增加,怎么通过一开始通过表的设计后,不在后期在修改表,彻底的解决这个问题呢(

价格策略

三大认证

三大认证源码位置

    def initial(self, request, *args, **kwargs):
        """
        Runs anything that needs to occur prior to calling the method handler.
        """
        self.format_kwarg = self.get_format_suffix(**kwargs)

        # Perform content negotiation and store the accepted info on the request
        neg = self.perform_content_negotiation(request)
        request.accepted_renderer, request.accepted_media_type = neg

        # Determine the API version, if versioning is in use.
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme
		
		#三大认证
        # Ensure that the incoming request is permitted
        self.perform_authentication(request)
        self.check_permissions(request)
        self.check_throttles(request)
        #认证模块:校验用户是否登录
        self.perform_authentication(request)
        #权限模块:校验用户是否拥有权限
        self.check_permissions(request)
        #频率模块:访问接口的次数在设定时间范围是否过快(有多种配置)
        self.check_throttles(request)

admin密文密码添加配置

from django.contrib import admin

from . import models


# admin注册自定义User表:密文操作密码
from django.contrib.auth.admin import UserAdmin as AuthUserAdmin
class UserAdmin(AuthUserAdmin):
    add_fieldsets = (
        (None, {
            'classes': ('wide',),
            # 添加用户界面可操作的字段
            'fields': ('username', 'password1', 'password2', 'mobile', 'email', 'is_staff', 'is_active'),
        }),
    )
    #显示的字段
    list_display = ('username', 'mobile', 'email', 'is_staff', 'is_active')

# 明文操作密码,admin可视化添加的用户密码都是明文,登录时用的是密文,所以用户无法登录
# admin.site.register(models.User)
admin.site.register(models.User, UserAdmin)

认证模块

验证

游客:不带token

非法用户:错误token(抛出异常)

登录用户:正确token(返回长度为2的元组,第一个为user对象,第二个为None)

三大认证

默认配置

    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication'
    ],
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.AllowAny',
    ],

认证模块

认证模块工作原理
1)继承BaseAuthentication类,重写authenticate方法
2)认证规则(authenticate方法实现体):
    没有携带认证信息,直接返回None => 游客
    有认证信息,校验失败,抛异常 => 非法用户
    有认证信息,校验出User对象 => 合法用户

    def authenticate(self, request):
        """
        Returns a `User` if the request session currently has a logged in user.
        Otherwise returns `None`.
        """

        # Get the session-based user from the underlying HttpRequest object
        user = getattr(request._request, 'user', None)

        # Unauthenticated, CSRF validation not required
        if not user or not user.is_active:
            return None
		
		#重新启用csrf
        self.enforce_csrf(request)

        # CSRF passed with authenticated user
        return (user, None)

authorization

    def authenticate(self, request):
        """
        Returns a `User` if a correct username and password have been supplied
        using HTTP Basic authentication.  Otherwise returns `None`.
        """
        #从请求头中获取认证字符串
        auth = get_authorization_header(request).split()
		
		#如果没有字符串或者值的key不为basic返回None
        if not auth or auth[0].lower() != b'basic':
            return None
		#只有key,报错
        if len(auth) == 1:
            msg = _('Invalid basic header. No credentials provided.')
            raise exceptions.AuthenticationFailed(msg)
        #大于2报错
        elif len(auth) > 2:
            msg = _('Invalid basic header. Credentials string should not contain spaces.')
            raise exceptions.AuthenticationFailed(msg)

        try:
            auth_parts = base64.b64decode(auth[1]).decode(HTTP_HEADER_ENCODING).partition(':')
        except (TypeError, UnicodeDecodeError, binascii.Error):
            msg = _('Invalid basic header. Credentials not correctly base64 encoded.')
            raise exceptions.AuthenticationFailed(msg)

        userid, password = auth_parts[0], auth_parts[2]
        return self.authenticate_credentials(userid, password, request)

权限模块

权限模块工作原理
1)继承BasePermission类,重写has_permission方法
2)权限规则(has_permission方法实现体):
    返回True,代表有权限
    返回False,代表无权限

class IsAuthenticated(BasePermission):
    """
    Allows access only to authenticated users.
    """

    def has_permission(self, request, view):
    	#有用户信息并且通过认证
        return bool(request.user and request.user.is_authenticated)

频率认证模块

自定义频率类

1.定义类继承SimpleRateThrottle,重写get_cache_key方法,设置scope类属性
2.scope就是一个认证字符串,在配置文件中配置scope字符串对应的频率设置
3.get_cache_key的返回值是字符串,该字符串是缓存访问次数的缓存key(注意要设置每个用户有不同的key,否则公用一个)
from rest_framework.throttling import SimpleRateThrottle

class ThreeTimeUserThrottle(SimpleRateThrottle):
    scope = 'three'
    #使用用户的id使每个用户有不同的key
    def get_cache_key(self, request, view):
        return 'throttle:user_%s' % (request.user.id)

settings配置

REST_FRAMEWORK = {
    # 频率设置
    'DEFAULT_THROTTLE_RATES': {
        'three': '3/min',
    },
}

局部配置

from rest_framework.permissions import IsAuthenticated
from utils.throttles import ThreeTimeUserThrottle
class UserCenterAPIView(APIView):
    # 认证全局配置吗,权限局部配置
    # authentication_classes = []
    permission_classes = [IsAuthenticated]
    # 频率模块局部配置
    throttle_classes = [ThreeTimeUserThrottle]

    def get(self, request, *args, **kwargs):
        user = request.user
        serializer = serializers.UserModelSerializer(user)
        return APIResponse(data=serializer.data)

登录和查询数据权限案例

涉及内容

校验数据生成token

手写认证器(解析token)

手写权限认证

登录模块

view.py

class LoginAPIView(APIView):
    # 登录接口一定要做:局部禁用 认证 与 权限 校验,都没登录验证啥
    authentication_classes = []
    permission_classes = []
    
    def post(self, request, *args, **kwargs):
    	#这里要注意数据不能存到数据库里面,这是登录
        serializer = serializers.LoginModelSerializer(data=request.data)
        
        #校验数据(校验还做了生成token等操作)设置自动抛错
        serializer.is_valid(raise_exception=True)
        
        #返回登录信息
        return APIResponse(data={
            'username': serializer.user.username,
            'token': serializer.token
        })

serializers.py

注意

fields里面不能使用username和password字段,否则会走系统默认校验,会查询数据库,并抛出数据重复的异常(系统以为你要添加数据)

from django.contrib.auth import authenticate
class LoginModelSerializer(ModelSerializer):

    usr = serializers.CharField(write_only=True)
    pwd = serializers.CharField(write_only=True)
    
    class Meta:
        model = models.User
        fields = ('usr', 'pwd')
    
    #全局钩子
    def validate(self, attrs):
        usr = attrs.get('usr')
        pwd = attrs.get('pwd')
        
        #使用auth的登录账号密码封装方法,返回用户对象
        try:
            user_obj = authenticate(username=usr, password=pwd)
        except:
            raise ValidationError({'user': '提供的用户信息有误'})

        # 拓展名称空间,给views拿数据
        self.user = user_obj
        
        # 签发token
        self.token = _get_token(user_obj)

        return attrs


#user转token
# token:用户名(base64加密).用户主键(base64加密).用户名+用户主键+服务器秘钥(md5加密)

# 签发算法:b64encode(用户名).b64encode(用户主键).md5(用户名+用户主键+服务器秘钥)

拆封token:一段 二段 三段
用户名:b64decode(一段)
用户主键:b64decode(二段)
碰撞解密:md5(用户名+用户主键+服务器秘钥) == 三段
"""
def _get_token(obj):
    import base64, json, hashlib
    #获取系统加密盐
    from django.conf import settings
    
    t1 = base64.b64encode(json.dumps({'username': obj.username}).encode()).decode()
    t2 = base64.b64encode(json.dumps({'pk': obj.id}).encode()).decode()
    
    t3_json = json.dumps({
        'username': obj.username,
        'pk': obj.id,
        'key': settings.SECRET_KEY
    })
    
    t3 = hashlib.md5(t3_json.encode()).hexdigest()
    return '%s.%s.%s' % (t1, t2, t3)

登录验证和权限认证模块

思路

这里就要使用认证模块和权限判断了

authentications.py 手写token认证

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
class TokenAuthentication(BaseAuthentication):
    prefix = 'Token'
    
    #重写认证方法
    def authenticate(self, request):
        # 拿到前台的token
        auth = request.META.get('HTTP_AUTHORIZATION')
        # 没有返回None,有进行校验
        if not auth:
            return None
            
        auth_list = auth.split()
		
		#判断auth_list长度是否为2,并且key为token
        if not (len(auth_list) == 2 and auth_list[0].lower() == self.prefix.lower()):
            raise AuthenticationFailed('非法用户')
		
		#拿到加密的token值
        token = auth_list[1]

        # 校验算法,校验失败抛异常
        user = _get_obj(token)
        
        # 成功返回(user, token)
        return (user, token)

# 校验算法(认证类)与签发算法配套

拆封token:一段 二段 三段
用户名:b64decode(一段)
用户主键:b64decode(二段)
碰撞解密:md5(用户名+用户主键+服务器秘钥) == 三段

import base64, json, hashlib
from django.conf import settings
from api.models import User
def _get_obj(token):
    token_list = token.split('.')
    
    #加密字符串是否符合要求
    if len(token_list) != 3:
        raise AuthenticationFailed('token异常')
    
    #解析出前两段数据username和pk
    username = json.loads(base64.b64decode(token_list[0])).get('username')
    pk = json.loads(base64.b64decode(token_list[1])).get('pk')
	
	#封装出正确数据第3段数据
    md5_dic = {
        'username': username,
        'pk': pk,
        'key': settings.SECRET_KEY
    }
	
	#校验第三段数据的正确性
    if token_list[2] != hashlib.md5(json.dumps(md5_dic).encode()).hexdigest():
        raise AuthenticationFailed('token内容异常')
	
	#查询出真正的登录用户
    user_obj = User.objects.get(pk=pk, username=username)
    return user_obj


""" 认证类的认证核心规则
def authenticate(self, request):

    token = get_token(request)
    
    # 校验token是否正确
    try:
        user = get_user(token)  
    except:
        raise AuthenticationFailed()
        
    #最后正确,返回登录对象
    return (user, token)
"""

permissions.py 权限认证

# 自定义权限类

"""
权限模块工作原理
1)继承BasePermission类,重写has_permission方法
2)权限规则(has_permission方法实现体):
    返回True,代表有权限
    返回False,代表无权限
"""
from rest_framework.permissions import BasePermission
class SuperUserPermission(BasePermission):
    def has_permission(self, request, view):
    
    	#return (user, token),user和auth的值是考认证器的
        print(request.user)
        print(request.auth)

        return True

views.py

from utils.authentications import TokenAuthentication
from utils.permissions import SuperUserPermission
class UserListAPIView(ListAPIView):
	#配置局部的认证和权限
    authentication_classes = [TokenAuthentication]
    permission_classes = [SuperUserPermission]
	
	#查询数据
    queryset = models.User.objects.filter(is_active=True, is_superuser=False).all()
    serializer_class = serializers.UserModelSerializer

    def get(self, request, *args, **kwargs):
        response = self.list(request, *args, **kwargs)
        return APIResponse(data=response.data)

posted @ 2019-11-25 23:59  zx125  阅读(221)  评论(0编辑  收藏  举报