文章目录
drf框架之接口、认证
项目初始化
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):
return models.User.objects.create_user(**validated_data)
用户中心接口
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
serializer.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
serialziers.py
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = ('name', )
认证
路由组件
from .router import SimpleRouter
router = SimpleRouter()
router.register('v2/cars'.views.Bookv2viewSet,'car')
urlpatterns = [
url('',include(router.urls))
]
这个跟admin里面的写法差不多,以后就用这个代替
认证组件
重点
- 认证规则
- 如何自定义认证类
- 我们一般不需要自定义认证类,在settings中全局配置第三方 jwt 认证组件提供的认证类即可
自定义认证类
-
自定义认证类,继承 BaseAuthentication 类
-
必须重写 authenticate(self,request) 方法
没有认证信息,返回None:匿名用户(游客)=> 匿名用户request.user也有值,就是"匿名对象(Anonymous)"
有认证信息,且通过,返回(user,token):合法用户 => 对象回存到request.user中
有认证信息,不通过,抛异常:非法用户
权限组件
重点
- 权限规则
- 如何自定义权限类
- 我们一般在视图中局部配置drf提供权限类,但是也会自定义权限类完成局部配置
自定义权限类
-
自定义权限类,继承 BasePermission类
-
必须重写 has_permission(self,request,view):方法
设置权限条件,条件通过,返回True:有权限
设置权限条件,条件失败,返回False:有权限
-
drf提供的权限类:
AllowAny:匿名与合法用户都可以 IsAuthenticated:必须登录,只有合法用户才可以 IsAdminUser:必须是admin后台用户 IsAuthenticatedOrReadOnly:匿名只读,合法用户无限制
jwt认证示意图
jwt优势
- 没有数据库写操作,高效
- 服务器不存token,低耗
- 签发校验都是算法,集群
认证权限配置
认证一般都是全局配置,所有登录注册接口要局部禁用
权限一般做局部配置 => 电商类项目,90%以上接口游客可以访问
权限一般做全局配置 => 邮箱项目,几乎所有接口都需要登录才能访问
jwt签发校验规则
jwt算法:签发与校验
-
jwt分三段式:头.体.签名 (head.payload.sgin)
-
头和体是可逆加密,让服务器可以反解出user对象;签名是不可逆加密,保证整个token的安全性
-
头体前名三部分,都是采用json格式的字符串,进行加密,可逆加密一般采用base64算法,不可逆加密一般采用hash(md5)算法
-
头中的内容是基本信息:公司信息、项目信息、项目组信息、token采用的加密方式信息
{ "company":"公司信息" .... }
-
体中的内容是关键信息:用户主键、用户名、签发时客户端信息(设备号、地址)、过期时间
{ "user_id":1, ... }
-
签名中的内容是安全信息:头的加密结果+ 体的加密结果 + 服务器不对外公开的安全码 进行md5加密
{ "head":"头的加密字符串", "payload":"体的加密字符串", "secret_key":"安全码" }
签发
根据登录请求提交来的 账号 + 密码 + 设备信息 签发 token
- 用基于信息存储json字典,采用base64算法加密的到 头字符串
- 用关键信息存储json字典,采用base64算法加密得到 体字符串
- 用头、体加密字符串再加安全码信息存储json字典,采用hash md5算法加密得到 签名字符串
账号密码就能根据User表得到user对象,形成的三段字符串用 . 拼接成token返回给前台
校验
根据客户端带token的请求 反解出 user 对象
- 将token按 . 拆分为三段字符串,第一段 头加密字符串 一般不需要做任何处理
- 第二段 体加密字符串,要反解出用户主键,通过主键User表中就能得到登录用户,过期时间和设备信息都是安全信息,确保token没过期,且是同一设备来的
- 再用 第一段 + 第二段 + 服务器安全码 不可逆md5加密,与第三段 签名字符串 进行碰撞校验,通过后才能代表第二段校验得到的user对象就是合法的登录用户
drf项目的jwt认证开发流程(重点)
- 用账号密码访问登录接口,登录接口逻辑中调用 签发token 算法,得到token,返回给客户端,客户端自己存到cookies中
- 校验token的算法应该写在认证类中(在认证类中调用),全局配置给认证组件,所有视图类请求,都是会进行校验,所以请求带了token,就会反解出user对象,在视图类中用requeset.user就能访问登录的用户
==注:==登录接口需要做 认证 + 权限 两个局部禁用(登录、注册)
drf-jwt框架的简单使用
安装(终端)
pip install djangorestframework-jwt
签发token(登录接口):视图类已经写好了,配置一下路由就行(urls.py)
# api/urls.py
urlpatterns = [
# ...
url('^login/$', ObtainJSONWebToken.as_view()),
]
# Postman请求:/api/login/,提供username和password即可
校验token(认证组件):认证类已经写好了,全局配置一下认证组件就行了(settings.py)
# drf-jwt的配置
import datetime
JWT_AUTH = {
# 配置过期时间
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),
}
# drf配置(把配置放在最下方)
REST_FRAMEWORK = {
# 自定义三大认证配置类们
'DEFAULT_AUTHENTICATION_CLASSES': ['rest_framework_jwt.authentication.JSONWebTokenAuthentication'],
# 'DEFAULT_PERMISSION_CLASSES': [],
# 'DEFAULT_THROTTLE_CLASSES': [],
}
设置需要登录才能访问的接口进行测试(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/ 接口访问个人中心