• day77


    三大认证

    认证组件

    配置自定义认证类(需求小)

    token只能由登录接口签发

    authentication_classes = [authentications.MyAuthentication]

    配置drf-jwt框架的认证类(需求大) =>

    配置还可以在全局(认证组件只能决定request.user,不是断定权限的地方,所以一般配置全局)

    在settings文件全局配置drf-jwt框架的认证类

    REST_FRAMEWORK = {
        # 认证组件
        'DEFAULT_AUTHENTICATION_CLASSES': [
            'rest_framework_jwt.authentication.JSONWebTokenAuthentication'
        ],
    }
    

    了解:如何自定义认证类、以及认证类规则

    总结:认证组件绑定使用

    每一个视图类都要进行认证校验,且认证规则一致,所以全局配置认证类即可

    自定义认证类

    1. 如果使用session认证,drf默认提供了SessionAuthentication
    2. 如果使用drf-jwt认证框架,drf-jwt框架提供了JSONWebTokenAuthentication
    3. 如果自定义签发与校验token,才需要将校验token的算法封装到自定义的认证类中
    class MyAuthentication(BaseAuthentication):
        def authenticate(self, request):
    	pass
    

    认证规则:

    1. 从请求头中拿到前台提交的token(一般从HTTP_AUTHORIZATION中拿,也可以与前台约定)

      -- 如果设置了反爬等措施,校验一下反爬(头 token)

    2. 没有token,返回None,代表游客

    3. 有token,进入校验

      -- 抛AuthenticationFailed异常,代表非法用户

      -- 返回 (user, token),代表合法用户

    在authentication.py源码分析:

    前台在请求头用authentication携带认证字符串token给后台

    auth = request.META.get('HTTP_AUTHORIZATION', b'')    #auth  == token字符串
    
    #按空格拆分,拆分的结果长度为2才合法
    auth = get_authorization_header(request).split()
    
    #可以推论出auth的结构(简易的反爬):basic abc.def.xyz
    if not auth or auth[0].lower() != b'basic':
    没有token,认证方法直接返回None,代表游客(匿名用户)
    	return None
    
    #可以推论出auth必须拆分为2份
     if len(auth) == 1:
                msg = _('Invalid basic header. No credentials provided.')
                raise exceptions.AuthenticationFailed(msg)
                elif len(auth) > 2;
                msg = _('Invalid basic header. Credentials string should not contain spaces.')
                raise exceptions.AuthenticationFailed(msg)
                #结论:提交了token,格式有误,抛异常,代表非法用户
                
    		 try:
    		 #反解token(auth是被拆分的列表,0位是头,1位是token)
                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)
                #结论:提交了token,反解失败,抛出异常,代表非法用户
    
            userid, password = auth_parts[0], auth_parts[2]
            return self.authenticate_credentials(userid, password, request)
            #结论:提交了token,解析成功,返回(user,token)组成的元组,代表合法用户
            #元组0位user会被存储到request.user中
            #元组1位token会被存储到request.auth中,通常也可以不用保存,所以可以用None填充
            
            
          #从请求中拿到token
         jwt_value = self.get_jwt_value(request)
         #没有token,返回None,游客
         if jwt_value is None:
             return None
         #反解token,失败,抛异常,非法用户;成功反解的数据就是载荷,存放在payload中
         try:
         	payload = jwt_decode_handler(jwt_value)
         	...
         #载荷校验得到登录用户
         suer = self.authenticate_credentials(payload)
         #得到登录用户,返回(user,token)
         return (suer,jwt_value)
    

    rest_frameworkserializers源码分析:

    #登录接口,提供username和password,签发token
    class ObtainJSONWebToken(JSONWebTokenAPIView):
    
    
    #校验接口,提供token,返回原token就代表校验通过(没用)
    class VerifyJSONWebToken(JSONWebTokenAPIView):
    
    校验规则只有username和password 
    self.fields[self.username_field] = serializers.CharField()
    self.fields['password '] = PasswordField(write_only=True)
    
    if all(credentials.values()):
    用auth组件的authenticate方法进行校验,该方法也只能校验username和password
    	user = authenticate(**credentials)
    	
    #第一步:得到用户
    if user:
    	if not user.is_active:
    		msg = _('User account is disabled.')
    		raise serializers.ValidationError(msg)
    		
    #第二步:将用户信息加到载荷中
    payload = jwt_payload_handler(user)
    
    #将token和user签发在serializers对象中,在外键用request.object.get('token) | .get('user')
    return {
    #第三步:签发token
    	'token':jwt_encode_handler(payload),
    	'user:user
    	}
    	
    	
    drf-jwt签发token
    1、username、password通过auth组件的authenticate方法得到user对象
    2、user对象通过drf-jwt框架jwt_payload_handler函数包装payload载荷
    3、payload载荷  通过drf-jwt框架的jwt_encode_handler函数签发token字符串
    注:可以借助jwt_payload_handler和jwt_encode_handler两个函数完成自定义jwt-token的签发。下图
    

    自定义签发token的登陆login接口,urls中的路由
    url(r'^login/$', views.LoginAPIView.as_view()),
    然后去views.py文件中定义登录接口
    token只能由   登录接口  签发
    登录接口也只能是APIView的子类,使用一定会进行 认证、权限组件的校验
    结论:不管是系统默认、或是全局settings配置的任何认证与权限组件,登录接口不用参与任何认证与权限的校验,
    所以,登录接口一定要进行 认证与权限 的局部禁用
    post请求,序列化默认当做create动作进行校验,需要检验数据库,create动作username会抛用户已存在异常
    抛用户已存在异常是多余的,所以自定义系统校验规则即可
    用全局钩子,完成token的签发
    通过username和password完成多方式登录校验,得到user对象
    user对象包装payload载荷
    payload载荷签发token
    将user与token存储到serializers对象中,方便在视图类中使用
    邮箱,手机号字段校验
    

    权限组件

    配置自定义权限类(有需求)

    permission_classes = [permissions.MyPermission]

    配置drf自带的权限类(有需求)

    自定义权限类

    1. drf默认提供了一些权限类

      AllowAny:游客和登录用户有全权限

      IsAuthenticated:只有登录用户有全权限

      IsAdminUser:只有后台用户(admin用户)有全权限

      IsAuthenticatedOrReadOnly:游客有读权限,登录用户有全权限

    2. 如果有特殊需要,需要自定义权限类

      如:只有superuser有权限、只有vip用户有权限、只有某ip网段用户有权限、只有某个视图及其子类有权限

    class MyPermission(BasePermission):
    	def has_permission(self, request, view):
    
    1. 根据需求,request和view的辅助,制定权限规则判断条件

    2. 如果条件通过,返回True

    3. 如果条件不通过,返回False

    4. 视图类局部配置权限类

      class MyAPIView(APIView):
      	permission_classes = [permissions.VIPUserPermission]
      
    5. drf自带权限类

      IsAuthenticated, IsAdminUser, AllowAny, IsAuthenticatedOrReadOnly
      
    6. 自定义权限类

      class VIPUserPermission(BasePermission):  # 只要vip分组用户有权限
          def has_permission(self, request, view):
              for group in request.user.groups.all(): #遍历分组
                  if group.name.lower() == 'vip':  # 识别大小写vip
                      return True  # 有权限
              return False  # 无权限
      

    VIP用户权限**

    class VIPUserPermission(BasePermission):
    	def  has_permission(self, request, view):
    		for group in request.user.groups.all():
    			if group.name.lower() == 'vip':
    				return True
    		return False
    

    总结:权限组件绑定使用

    每一个视图类都要进行权限校验,默认配置的是不限制(AllowAny),但实际开发中,视图类的访问权限不尽相同,所以要在具体的视图类,配置具体的权限规则

    权限:只有VIP用户可以查看个人详细详细

    class UserViewSet(ViewSet):
        permission_classes = [permissions.VIPUserPermission]
    	def retrieve(self, request, *args, **kwargs):
            return APIResponse(results={
                'username': request.user.username,
                'email': request.user.email,
                'mobile': request.user.mobile,
                'data_joined': request.user.date_joined,
            })
    

    频率组件

    配置drf自带的频率类(有需求)

    配置自定义的频率类(需求大)

    自定义频率类

    1. drf默认提供了一些频率类

      AnonRateThrottle:只对游客进行频率限制

      UserRateThrottle:对所有用户进行频率限制

    2. 如果有特殊需要,需要自定义频率类

      如:对ip进行限次、对电话进行限制、对视图某些信息进行限次

    class MobileRateThrottle(SimpleRateThrottle):
    scope = 'mobile'
    def get_cache_key(self, request, view):
    	if not request.user.is_authenticated or not 
    		request.user.mobile:
    			return None #匿名用户  或 没有电话号的用户  都不限制
    	#只要有电话号的用户踩进行限制
    	return self.cache_format % {
                'scope': self.scope,
                'ident': request.user.mobile
            }
    
    1. 视图类局部配置频率类

      class MyAPIView(APIView):
      	throttle_classes = [throttles.MobileRateThrottle]
      
    2. drf自带频率类

      AnonRateThrottle, UserRateThrottle
      
    3. 自定义频率类

      class MobileRateThrottle(SimpleRateThrottle):
          scope = 'mobile'
          def get_cache_key(self, request, view):
              if not request.user.is_authenticated or not request.user.mobile:
                  return None  # 匿名用户 或 没有电话号的用户 都不限制
              return self.cache_format % {  # 只要有电话号的用户才进行限制
                  'scope': self.scope,
                  'ident': request.user.mobile
              }
      

      配合settings中的频率配置

       REST_FRAMEWORK = {
          # 频率组件:频率类一般做局部配置,但是频率调节在settings中配置
          'DEFAULT_THROTTLE_RATES': {
              'user': '5/min',
              'anon': '3/min',
              'mobile': '1/min'
          },
      }
      

    自定义签发token——多方式登录

    1. token只能由 登录接口 签发
    2. 登录接口也是APIView的子类,使用一定会进行 认证、权限 组件的校验

    结论:

    不管系统默认、或是全局settings配置的是何认证与权限组件,登录接口不用参与任何认证与权限的校验
    所以,登录接口一定要进行 认证与权限 的局部禁用

    1. 将请求数据交给序列化类,执行序列化校验

    2. 在序列化全局校验钩子中,完成user的认证与token的签发,保存在序列化对象的content中

    3. 在视图类中从序列化对象的content中拿user与token相关信息返回

      views.py文件代码
      return APIResponse(results={
                  'username': serializer.content.get('user').username,
                  'token': serializer.content.get('token')
              })
      

    注:多方式登录体现在 请求的账号类型可能是用户名、邮箱或手机等,采用不同字段校验数据库即可

    serializers.py文件中
        def _validate_user(self, attrs):
            username = attrs.get('username')
            password = attrs.get('password')
    
            if re.match(r'.*@.*', username):  # 邮箱
                user = models.User.objects.filter(email=username).first()  # type: models.User
            elif re.match(r'^1[3-9][0-9]{9}$', username):  # 电话
                user = models.User.objects.filter(mobile=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
    

    全局钩子完成token签发

    post请求,序列化默认当做create动作进行校验,需要校验数据库,create动作username会抛出用户已存在异常

    抛出用户已存在异常是多余的,所以自定义系统校验规则即可

    class LoginModelSerializer(serializers.ModelSerializer):
    username = serializers.CharField(min_length=3, max_length=16)
    password = serializers.CharField(min_length=3, max_length=16)
    class Meta:
    	 model = models.User
         fields = ('username', 'password')
     用全局钩子,完成token的签发
     def validate(self, attrs):
     1、通过username和password完成多方登录校验,得到user对象
     uuser = 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 = {
     	'user': user,
        'token': token
     }
     return attrs
    

    apiurls.py文件中,两个路由已经匹配了10个方法:get:retrieve、list;post:create、create;put:update、multiple_update

    视图类用ViewSet或GenericViewSet的子类,在子类中实现对应的方法,就可以响应对应的请求

  • 相关阅读:
    JavaScript字符串和字符数组
    JavaScript数组&类数组转换
    JavaScript判断值是否是NaN
    JavaScript中七种数据类型·中·一
    QRcode.js 生成二维码
    你不知道的JavasScript上篇·第五章·原型·下
    你不知道的JavasScript上篇·第五章·原型·上
    你不知道的JavasScript上篇·第四章·混合对象·类
    你不知道的JavasScript上篇·第三章·对象
    Vue2.x之父子组件数据传递
  • 原文地址:https://www.cnblogs.com/gfhh/p/12142282.html
Copyright © 2020-2023  润新知