REST-Framework: 认证组件 | token的介绍和使用

一 认证简介
Django 自带一个用户认证系统,这个系统处理用户帐户、组、权限和基于 cookie 的会话.
只有认证通过的用户才能访问指定的url地址,比如:查询课程信息,需要登录之后才能查看,没有登录,就不能查看,这时候需要用到认证组件

Rest-Framework中的认证

在restframework中,认证即是通过继承BaseAuthentication重构认证的类,认证的逻辑在类的authenticate方法中实现,通过判断用户传递的信息,如果认证成功返回(用户,用户Token)元组,会将用户对象封装到request里,通过request.用户可以获得用户的相关信息。

安装

pip install djangorestframework-jwt

局部使用的时候, 哪个类需要加验证,就在哪个类中加上该属性:  authentication_classes = [TokenAuth, ]

  • 要注意列表中的类,必须是已经写好的用来验证的类,

  • 列表中有多个类的时候, 如果第一个类就有两个返回值, 那么后面的类将不会再继续进行, 是否进行的关键在于验证成功后返回的值是否为空, 为空则继续循环验证类, 反之则停止

 

class Course(APIView):
    authentication_classes = [TokenAuth, ]
 
    def get(self, request):
        return HttpResponse('get')
 
    def post(self, request):
        return HttpResponse('post')

三 认证组件全局使用

全局使用的时候会导致一个尴尬的问题就是我们写的登录CBV也需要登录认证才行

全局使用的时候,我们实现部分CBV禁用的方式是,在需要的CBV中写上:authentication_classes = [ ]

当我们写了authentication_classes = [ ] 为空的时候,将不会有返回值,就会让我们进行正常登录
 

REST_FRAMEWORK = {
    #异常处理
    'EXCEPTION_HANLER':'mall.utils.exceptions.exception_handler',
    'DEFAULT_RENDERER_CLASSES': (  # 默认响应渲染类
        'rest_framework.renderers.JSONRenderer',  # json渲染器
        'rest_framework.renderers.BrowsableAPIRenderer',  # 浏览API渲染器
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        # 这里是全局的 return [auth() for auth in self.authentication_classes]
        # 加上后 所有接口都500
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication', #token
        'rest_framework.authentication.BasicAuthentication',  # 基本认证
        'rest_framework.authentication.SessionAuthentication',  # session认证
    ),
}
JWT_AUTH = {
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=2), # 指明token的有效期
    'JWT_RESPONSE_PAYLOAD_HANDLER': 'users.utils.jwt_response_payload_hanler',
}
  • JWT_EXPIRATION_DELTA 指明token的有效期
# -*- coding: utf-8 -*-
def jwt_response_payload_hanler(token, user=None, request=None):
    """自定义jwt认证返回数据"""
    data = {"id": user.id,"username": user.username,"mobile": user.mobile,"token":token}
    return data

 

使用

Django REST framework JWT 扩展的说明文档中提供了手动签发JWT的方法

from rest_framework_jwt.settings import api_settings

jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER

payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)

在注册成功后,连同返回token,需要在注册视图中创建token。

修改CreateUserSerializer序列化器,在create方法中增加手动创建token的方法

# -*- coding: utf-8 -*-
import re

from rest_framework import serializers
from users.models import User


class CreateUserSerializer(serializers.ModelSerializer):
    password2 = serializers.CharField(label="确认密码",write_only=True)
    sms_code = serializers.CharField(label="短信验证码",write_only=True)
    allow = serializers.CharField(label="同意协议",write_only=True)
    token = serializers.CharField(label="登录状态token",read_only=True)

    class Meta:
        model = User
        fields = ("id","username","password","password2","sms_code","mobile","allow","token")
        extra_kwargs = {
            "username":{
                "min_length":5,
                "max_length":20,
                "error_messages":{
                    "min_length":"仅允许5-20字符的用户名",
                    "max_length":"仅允许5-20字符的用户名",
                }
            },
            "password": {
                "write_only": True,
                "min_length": 8,
                "max_length": 20,
                "error_messages": {
                    "min_length": "仅允许8-20个字符的密码",
                    "max_length": "仅允许8-20个字符的密码",
                }
            }
        }


    def validated_mobile(self,value):
        print(value)
        # 校验 手机号
        if not re.match(r'^1[3-9]\d{9}',value):
            raise serializers.ValidationError("手机号码格式错误")
        return value

    def validated_allow(self,value):
        # 校验是否同意协议
        if value != "true":
            raise serializers.ValidationError("请您勾选用户协议")
        return value

    def validate(self, data):
        # 判断两次密码
        if data['password'] != data['password2']:
            raise serializers.ValidationError("两次输入的密码不一致")

        # # 判断短信验证码
        # mobile = data['mobile']
        # redis_conn = get_redis_connection('verify_codes')
        # real_sms_code = redis_conn.get("sms_%s" % mobile)  # 二进制
        # if real_sms_code is None:
        #     raise serializers.ValidationError("无效的短信验证码")
        # if data['sms_code'] != real_sms_code.decode():
        #     raise serializers.ValidationError("短信验证码错误")
        return data

    def create(self, validated_data):
        # 创建新用户
        # 移除数据库用户表里没有的字段
        del validated_data['password2']
        del validated_data['sms_code']
        del validated_data['allow']

        # user = User.objects.create(**validated_data)
        user = super().create(validated_data)
        user.set_password(validated_data['password']) #继承的一个加密密码的方法
        user.save()

        # 加上 token
        from rest_framework_jwt.settings import api_settings
        payload_handler = api_settings.JWT_PAYLOAD_HANDLER
        encode_handler = api_settings.JWT_ENCODE_HANDLER

        payload = payload_handler(user)
        token = encode_handler(payload)
        user.token = token

        return user


 注意上面是全局

认证组件全局使用
全局使用的时候会导致一个尴尬的问题就是我们写的登录CBV也需要登录认证才行

全局使用的时候,我们实现部分CBV禁用的方式是,在需要的CBV中写上:authentication_classes = [ ]

当我们写了authentication_classes = [ ] 为空的时候,将不会有返回值,就会让我们进行正常登录
 

注册/验证手机号不需要token

import random

from django.http import HttpResponse
from rest_framework.views import APIView
from rest_framework.generics import GenericAPIView
from verifications.serializers import ImageCodeCheckSerializer
from rest_framework.response import Response
# Create your views here.

from libs.captcha.captcha import captcha
from libs.aliyunsms.sms_send import send_sms_code

'''
 图片验证
 1.第三方插件  captcha
'''
class ImageCodeView(APIView):
    authentication_classes = []
    # 图片验证码
    def get(self,request,image_code_id):
        # 获取图片的验证码
        # 1.调用图片模块 接收图片验证码和图片对象
        text,image = captcha.generate_captcha()
        print(text)
        # 2.保存这个图片验证码的真实值 redis
        #redis_conn = get_redis_connection('verify_codes')
        # redis_conn.setex("img_%s" % image_code_id, constants.IMAGE_CODE_REDIS_EXPIRES, text)
        # 保存到session
        request.session['info'] = text
        # 获取 request.session.info
        print(request.session.get("info"))
        return HttpResponse(image,content_type="image/png")


# GET /sms_codes/(P<mobile>1[3-9]\d{9})/?text=1234&image_code_id=uuid
class SMSCodeView(GenericAPIView):
    authentication_classes = []
    serializer_class = ImageCodeCheckSerializer
    # 发送短信验证码
    def get(self,request,mobile):
        # 1.校验参数 由序列化器完成
        serializer = self.get_serializer(data=request.query_params)
        serializer.is_valid(raise_exception=True)
        # 2.发送短信需生成一个随机的验证码
        sms_code = "%06d" % random.randint(0,999999)
        print("短信验证码: %s" % sms_code)
        # 3.保存短信验证码
        request.session['sms_code'] = sms_code
        # 4.发送短信
        send_sms_code(mobile,sms_code)
        return Response({"message":"ok"})
       # 给这个发送短信的接口做限流 一天最或发5次。


总结:局部使用,只需要在视图类里加入:

authentication_classes = [TokenAuth, ]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值