drf框架 7 三大认证组件的书写方式 jwt认证规则(json web token) pycharm的debug介绍

项目初始化

settings.py
INSTALLED_APPS = [
    # ...
    'rest_framework',
]

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

# 自定义用户表
AUTH_USER_MODEL = 'api.User'
主urls.py
from django.conf.urls import url, include
from django.contrib import admin

from django.views.static import serve
from django.conf import settings
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 路由分发
    url(r'^api/', include('api.urls')),

    url(r'^media/(?P<path>).*', serve, {'document_root': settings.MEDIA_ROOT}),
]
子urls.py
from django.conf.urls import url, include
from rest_framework.routers import SimpleRouter
router = SimpleRouter()
from . import views

# 路由注册
# router.register('register', views.RegisterViewSet, 'register')

urlpatterns = [

    url('', include(router.urls))
]
models.py
from django.contrib.auth.models import AbstractUser
from django.db import models


class User(AbstractUser):
    mobile = models.CharField(max_length=11, unique=True, verbose_name='移动电话')
    icon = models.ImageField(upload_to='icon', default='icon/default.png', verbose_name='头像')

    class Meta:
        db_table = 'o_user'  # 定义表名
        verbose_name_plural = '用户表'

    def __str__(self):
        return self.username


class Book(models.Model):
    name = models.CharField(max_length=64, verbose_name='书名')

    class Meta:
        db_table = 'o_book'
        verbose_name_plural = '书表'

    def __str__(self):
        return self.name


class Car(models.Model):
    name = models.CharField(max_length=64, verbose_name='车名')

    class Meta:
        db_table = 'o_car'
        verbose_name_plural = '车表'

    def __str__(self):
        return self.name

 

注册接口

urls.py
router.register('register', views.RegisterViewSet, 'register')
views.py
from rest_framework.viewsets import GenericViewSet, ModelViewSet
from rest_framework import mixins
from . import models, serializers

class RegisterViewSet(GenericViewSet, mixins.CreateModelMixin):
    queryset = models.User.objects.filter(is_active=True).all()
    serializer_class = serializers.RegisterSerializer
serializers.py
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from . import models


class RegisterSerializer(serializers.ModelSerializer):
    re_password = serializers.CharField(write_only=True, min_length=8, max_length=18)
    class Meta:
        model = models.User
        fields = ('username', 'password', 're_password', 'mobile')
        extra_kwargs = {
            'password': {
                'write_only': True,
                'min_length': 8,
                'max_length': 18
            }
        }

    # username和mobile可以自定义局部钩子校验(省略)

    def validate(self, attrs):
        password = attrs.get('password')
        re_password = attrs.pop('re_password')
        if password != re_password:
            raise ValidationError({'re_password': 'password confirm error'})
        return attrs

    # 需要重写create,创建用户需要密文
    def create(self, validated_data):  # 重写modelserializer创建表方法
        return models.User.objects.create_user(**validated_data)  # auth组件功能

 

用户中心接口

urls.py
router.register('user/center', views.UserCenterViewSet, 'center')
views.py
class UserCenterViewSet(GenericViewSet, mixins.RetrieveModelMixin):
    queryset = models.User.objects.filter(is_active=True).all()
    serializer_class = serializers.UserCenterSerializer
serializers.py
class UserCenterSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.User
        fields = ('username', 'mobile', 'icon', 'email')

 

图书资源接口

urls.py
router.register('books', views.BookViewSet, 'book')
views.py
class BookViewSet(ModelViewSet):
    queryset = models.Book.objects.all()
    serializer_class = serializers.BookSerializer
serializers.py
class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        fields = ('name', )

 

本质上视图集,GenericAPIView都是继承了APIView

"""
1)所有直接或间接继承APIView的子类,都具有以下属性的默认值,默认配置走drf的settings文件
class APIView(View):
    renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
    parser_classes = api_settings.DEFAULT_PARSER_CLASSES
    authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
    throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
    permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES

2)如果在项目的settings.py中配置了如下代码
REST_FRAMEWORK = {
    # 自定义三大认证配置类们
    'DEFAULT_AUTHENTICATION_CLASSES': [],
    'DEFAULT_PERMISSION_CLASSES': [],
    'DEFAULT_THROTTLE_CLASSES': [],
}
那所有直接或间接继承APIView的子类,配置信息不走drf的settings文件,走自定义的(自定义没规定的才走drf默认的)

3)如果直接在APIView的子类中,设置类属性,代码如下
class MyAPIView(APIView):
    authentication_classes = ['类1', ..., '类n']
    throttle_classes = ['类1', ..., '类n']
    permission_classes = ['类1', ..., '类n']
那所有的视图类,配置直接在类自己的内部就完成了,不再走任何settings文件

总结:1是默认配置;2是全局配置;3是局部配置 | 查找顺序:局部 > 全局 > 默认
"""

 

认证组件

重点

得到request.user,不管权限任何事

"""
1)认证规则
2)如何自定义认证类
3)我们一般不需要自定义认证类,在settings中全局配置第三方 jwt 认证组件提供的认证类即可
"""

自定义认证类

"""
1)自定义认证类,继承 BaseAuthentication 类
2)必须重写 authenticate(self, request) 方法
    没有认证信息,返回None:匿名用户(游客) => 匿名用户request.user也有值,就是"匿名对象(Anonymous)"
    有认证信息,且通过,返回(user, token):合法用户 => user对象会存到request.user中
    有认证信息,不通过,抛异常:非法用户
"""

 

权限组件

重点

"""
1)权限规则
2)如何自定义权限类
3)我们一般在视图类中局部配置drf提供的权限类,但是也会自定义权限类完成局部配置
"""

自定义权限类

"""
1)自定义权限类,继承 BasePermission 类
2)必须重写 has_permission(self, request, view): 方法
    设置权限条件,条件通过,返回True:有权限
    设置权限条件,条件失败,返回False:有权限
    
3)drf提供的权限类:
AllowAny:匿名与合法用户都可以
IsAuthenticated:必须登录,只有合法用户(登陆用户)可以
IsAdminUser:必须是admin后台用户  # 判断request.user.is_staff是否为true
IsAuthenticatedOrReadOnly:匿名只读,合法用户无限制(可写)
"""

 

jwt认证示意图

json web token
"""
jwt优势 1)没有数据库写操作,高效 2)服务器不存token,低耗 3)签发校验都是算法,集群 """

 

jwt认证算法:签发与校验

"""
1)jwt分三段式:头.体.签名 (head.payload.sign)
2)头和体是可逆加密,让服务器可以反解出user对象;签名是不可逆加密,保证整个token的安全性的
3)头体签名三部分,都是采用json格式的字符串,进行加密,可逆加密一般采用base64算法,不可逆加密一般采用hash(md5)算法
4)头中的内容是基本信息:公司信息、项目组信息、token采用的加密方式信息
{
    "company": "公司信息",
    ...
}
5)体中的内容是关键信息:用户主键、用户名、签发时客户端信息(设备号、地址)、过期时间
{
    "user_id": 1,
    ...
}
6)签名中的内容时安全信息:头的加密结果 + 体的加密结果 + 服务器不对外公开的安全码 进行md5加密
{
    "head": "头的加密字符串",
    "payload": "体的加密字符串",
    "secret_key": "安全码"
}
"""
签发:根据登录请求提交来的 账号 + 密码 + 设备信息 签发 token
"""
1)用基本信息存储json字典,采用base64算法加密得到 头字符串
2)用关键信息存储json字典,采用base64算法加密得到 体字符串
3)用头、体加密字符串再加安全码信息存储json字典,采用hash md5算法加密得到 签名字符串

账号密码就能根据User表得到user对象,形成的三段字符串用 . 拼接成token返回给前台
"""
校验:根据客户端带token的请求 反解出 user 对象
"""
1)将token按 . 拆分为三段字符串,第一段 头加密字符串 一般不需要做任何处理
2)第二段 体加密字符串,要反解出用户主键,通过主键从User表中就能得到登录用户,过期时间和设备信息都是安全信息,确保token没过期,且是同一设备来的(不是同一设备会被检测出)
3)再用 第一段 + 第二段 + 服务器安全码 不可逆md5加密,与第三段 签名字符串 进行碰撞校验,通过后才能代表第二段校验得到的user对象就是合法的登录用户
"""

 

drf项目的jwt认证开发流程(重点)

"""
1)用账号密码访问登录接口,登录接口逻辑中调用 签发token 算法,得到token,返回给客户端,客户端自己存到cookies中

2)校验token的算法应该写在认证类中(在认证类中调用),全局配置给认证组件,所有视图类请求,都会进行认证校验,所以请求带了token,就会反解出user对象,在视图类中用request.user就能访问登录的用户

注:登录接口需要做 认证 + 权限 两个局部禁用
"""

 

drf-jwt框架基本使用

安装(终端)
>: pip install djangorestframework-jwt

django也有原生的jwt, go也有go的jwt。jwt用的默认的是auth6表,如果重定义了auth6表,就不能用这个jwt,得自己写

签发token(登录接口):视图类已经写好了,配置一下路由就行(urls.py)
# api/urls.py
from rest_framework_jwt.views import ObtainJSONWebToken
urlpatterns = [
    # ...
    url('^login/$', ObtainJSONWebToken.as_view()),  # 签发token(此内置方法只能账号密码登录,改为多方式登录比较好)源码写死username和password字段,根据user表判断用户密码是否正确
]

# Postman请求:/api/login/,提供username和password即可

校验token(认证组件):认证类已经写好了,全局配置一下认证组件就行了(settings.py)
# from rest_framework_jwt import settings # 从settings中看源码还有哪些jwt配置
# drf-jwt的配置(该配置必须写在drf配置的上面才能起作用)
import datetime        # 配置必须放在drf配置的上面,才能起作用
JWT_AUTH = {
    # 配置过期时间
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),
}


# drf配置(把配置放在最下方)
REST_FRAMEWORK = {
    # 自定义三大认证配置类们
    'DEFAULT_AUTHENTICATION_CLASSES': ['rest_framework_jwt.authentication.JSONWebTokenAuthentication'],  # 校验token的算法走该配置,相当于所有视图装了该装饰器。不带token就是游客,带token就校验出,把user放到request.user中
    # 'DEFAULT_PERMISSION_CLASSES': [],   # 全局设置默认权限策略,这样不用每个视图设置认证权限类
    # 'DEFAULT_THROTTLE_CLASSES': [],
}

''' 例如如下配置
'DEFAULT_PERMISSION_CLASSES': (
# 'rest_framework.permissions.AllowAny', # 全局匿名和合法用户都可以
'rest_framework.permissions.IsAuthenticated', # 全局必须登录,合法用户才行
),
'''
设置需要登录才能访问的接口进行测试(views.py)
from rest_framework.permissions import IsAuthenticated
class UserCenterViewSet(GenericViewSet, mixins.RetrieveModelMixin):
    # 设置必须登录才能访问的权限类
    permission_classes = [IsAuthenticated, ]

    queryset = models.User.objects.filter(is_active=True).all()
    serializer_class = serializers.UserCenterSerializer
测试访问登录认证接口(Postman)
"""
1)用 {"username": "你的用户", "password": "你的密码"} 访问 /api/login/ 接口等到 token 字符串

2)在请求头用 Authorization 携带 "jwt 登录得到的token" 访问 /api/user/center/1/ 接口访问个人中心
"""

 

pycharm的debug介绍

1.向下一步,如果遇到调用方法,函数,当成一步走      2. 向下一步,如果遇到调用方法,函数,进入其中      3.向下一步,只走自己的代码(较少用到)       

4.向下完成代码,直到下一个断点     5.显示所有断点(可编辑)       6.取消所有断点,再点击会再次出现断点      7.跳出当前方法,函数(完成),往下继续

 

小结

"""
1)注册接口、个人中心接口、图书接口

2)认证组件
    i)如何自定义认证类:继承谁、实现什么方法、方法体逻辑就是认证规则
    ii)认证规则:游客、合法用户、方法用户
    
3)权限组件
    i)如何自定义权限类:继承谁、实现什么方法、方法体逻辑就是权限规则
    ii)权限规则:有权限、无权限

4)认证权限配置:局部 > 全局 > 默认
    认证一般都是全局配置,所有登录注册接口要局部禁用
    权限一般做局部配置 => 电商类项目,90%以上接口游客可以访问
    权限一般做全局配置 => 邮箱项目,几乎所有接口都需要登录才能访问
    
5)jwt签发校验规则

6)drf-jwt框架的简单使用
"""

 

posted @ 2020-02-25 00:27  战斗小人  阅读(280)  评论(0编辑  收藏  举报