好久好久之前,Web站点只是做为浏览服务器资源(数据)和其余资源的工具,甚少有什么用户交互之类的烦人的事情须要处理,因此,Web站点的开发这根本不关心什么人在何时访问了什么资源,不须要记录任何数据,有客户端请求,我即返回数据,简单方便,每个http请求都是新的,响应以后当即断开链接。django
而现在,互联网的世界发生了翻天覆地的变化,用户不只仅须要跟其余用户沟通交流,还须要跟服务器交互,不论是论坛类、商城类、社交类、门户类仍是其余各种Web站点,你们都很是重视用户交互,只有跟用户交互了,才能进一步留住用户,只有留住了用户,才能知道用户需求,知道了用户需求,才会产生商机,有了用户,就等于有了流量,才可以骗到…额…是融到钱,有了资金企业才能继续发展,可见,用户交互是很是重要的,甚至能够说是相当重要的一个基础功能。浏览器
而谈到用户交互,则必需要谈到咱们今天所要学习的知识点,认证、权限和频率。首先咱们来看看认证。服务器
大部分人都知道cookie和session这两种方式能够保存用户信息,这两种方式不一样的是cookie保存在客户端浏览器中,而session保存在服务器中,他们各有优缺点,配合起来使用,可将重要的敏感的信息存储在session中,而在cookie中能够存储不太敏感的数据。cookie
而token称之为令牌。cookie、session和token都有其应用场景,没有谁好谁坏,不过开发数据接口类的Web应用,目前用token仍是比较多的。session
token认证的大体步骤是这样的:app
定义 url dom
from django.urls import path, re_path from DrfOne import views urlpatterns = [ path("books/", views.BookView.as_view({ "get": "list", "post": "create", })), re_path('books/(?P<pk>\d+)/', views.BookView.as_view({ 'get': 'retrieve', 'put': 'update', 'delete': 'destroy' })), # 登录 path('login/', views.LoginView.as_view()), ]
建立两个 model ,以下所示:ide
from django.db import models
class UserInfo(models.Model): username = models.CharField("姓名", max_length=32) password = models.CharField("密码", max_length=32) age = models.IntegerField("年龄") gender = models.SmallIntegerField("性别", choices=((1, "男"), (2, "女")), default=1) user_type_entry = ((1, "普通用户"), (2, "VIP"), (3, "SVIP")) user_type = models.SmallIntegerField("用户级别", choices=user_type_entry) def __str__(self): return self.username class UserToken(models.Model): user = models.ForeignKey(to="UserInfo", on_delete=models.CASCADE) token = models.CharField(max_length=128)
post 方法接口,视图类以下所示:工具
from django.http import JsonResponse from rest_framework.views import APIView from DrfTwo.models import UserInfo, UserToken class LoginView(APIView): def post(self, request): # 定义返回信息 ret = dict() try: # 定义须要的信息 fields = {"username", "password"} # 定义一个用户信息字典 user_info = dict() # 判断fields是不是request.data的子集 if fields.issubset(set(request.data.keys())): for key in fields: user_info[key] = request.data.get(key) user_instance = UserInfo.objects.filter(**user_info).first() # 用户验证 if user_instance: # 自定义generate_token()方法,获取token值,在后面 access_token = generate_token() # 用户登录成功,建立token,token存在更新token, defaults为更新内容 UserToken.objects.update_or_create(user=user_instance, defaults={ "token": access_token }) ret["status_code"] = 200 ret["status_message"] = "登陆成功" ret["access_token"] = access_token ret["user_role"] = user_instance.get_user_type_display() else: ret["status_code"] = 201 ret["status_message"] = "登陆失败,用户名或密码错误" except Exception as e: ret["status_code"] = 202 ret["status_message"] = str(e) return JsonResponse(ret)
简单写了个获取随机字符串的方法用来生成token值:post
import uuid def generate_token(): random_str = str(uuid.uuid4()).replace("-", "") return random_str
以上就是token的简单生成方式,固然,在生产环境中不会如此简单,关于token也有相关的库,而后在构造几条数据以后,能够经过POSTMAN工具来建立几个用户的token信息。
接下来,如何对已经登陆成功的用户实现访问受权呢?也就是说,只有登陆过的用户(有token值)才能访问特定的数据,该DRF的认证组件出场了
首先,新建一个认证类,以后的认证逻辑就包含在这个类里面:
# 1.定义认证类 class UserAuth(object): # 认证逻辑 def authenticate(self, request): user_token = request.GET.get('token') token_object = UserToken.objects.filter(token=user_token).first() if token_object: return token_object.user, token_object.token else: raise APIException("认证失败")
实现方式看上去很是简单,到 token 表里面查看 token 是否存在,而后根据这个信息,返回对应信息便可,而后,在须要认证经过才能访问的数据接口里面注册认证类便可:
from rest_framework.viewsets import ModelViewSet from rest_framework.exceptions import APIException from DrfOne.models import Book, UserToken from DrfOne.drf_serializers import BookSerializer # 1.定义认证类 class UserAuth(object): # 认证逻辑 def authenticate(self, request): user_token = request.GET.get('token') token_object = UserToken.objects.filter(token=user_token).first() if token_object: return token_object.user.username, token_object.token else: raise APIException("认证失败") class BookView(ModelViewSet): # 2.指定认证类,固定写法 authentication_classes = [UserAuth] # 获取数据源, 固定写法 queryset = Book.objects.all() # 序列化类, 固定写法 serializer_class = BookSerializer
序列类 BookSerializer
from rest_framework import serializers from DrfOne import models class BookSerializer(serializers.ModelSerializer): class Meta: model = models.Book fields = "__all__" extra_kwargs = { # 仅写 "publish": {'write_only': True}, "authors": {'write_only': True}, } publish_name = serializers.CharField(max_length=32, read_only=True, source="publish.name") publish_address = serializers.CharField(max_length=32, read_only=True, source="publish.address") author_name = serializers.SerializerMethodField() def get_author_name(self, book_obj): author_list = list() for author in book_obj.authors.all(): # 注意列表添加字段,author.name而不是author author_list.append(author.name) return author_list
须要注意的是,若是须要返回什么数据,请在最后一个认证类中返回,由于若是在前面返回,在源码的 self._authentication() 方法中会对返回值进行判断,若是不为空,认证的过程就会停止,多个认证类的实现方式以下:
# 1.定义认证类 class UserAuth(object): # 认证逻辑 def authenticate(self, request): user_token = request.GET.get('token') token_object = UserToken.objects.filter(token=user_token).first() if token_object: return token_object.user.username, token_object.token else: raise APIException("认证失败") class UserAuthTwo(object): def authenticate(self, request): raise APIException("就是这么简单!") class BookView(ModelViewSet): # 2.指定认证类,固定写法 authentication_classes = [UserAuth, UserAuthTwo] # 获取数据源, 固定写法 queryset = models.Book.objects.all() # 序列化类, 固定写法 serializer_class = BookSerializer
若是但愿全部的数据接口都须要认证怎么办?很简单,若是认证类本身没有 authentication_classes ,就会到 settings 中去找,经过这个机制,咱们能够将认证类写入到 settings 文件中便可实现全局认证:
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'authenticator.views.UserAuth', 'authenticator.views.UserAuthTwo', ), }
与认证组件几乎差很少,只要判断该用户的级别,是否给予经过
定义权限类
from rest_framework.permissions import BasePermission class UserPermission(BasePermission): message = "您没有权限访问该数据" def has_permission(self, request, view): # 用户级别是否超过普通用户 if request.user.user_type > 1: return Truereturn False
一样的逻辑,一样的方式,只是执行权限的方法名与执行认证的方法名不同而已,名为 has_permission ,而且须要将当前的视图类传递给该方法。
视图类中加入 permission_classes 变量:
class BookView(ModelViewSet): # 指定认证类,固定写法 authentication_classes = [UserAuth] # 指定权限类,固定写法 permission_classes = [UserPermission] # 获取数据源, 固定写法 queryset = models.Book.objects.all() # 序列化类, 固定写法 serializer_class = BookSerializer
~>.<~