很久很久以前, web 站点只是作为浏览服务器资源 (数据) 和其他资源的工具, 甚少有什么用户交互之类的烦人的事情需要处理, 所以, Web 站点的开发这根本不关心什么人在什么时候访问了什么资源, 不需要记录任何数据, 有客户端请求, 我即返回数据, 简单方便, 每一个 http 请求都是新的, 响应之后立即断开连接.
而如今, 互联网的世界发生了翻天覆地的变化, 用户不仅仅需要跟其他用户沟通交流, 还需要跟服务器交互, 不管是论坛类, 商城类, 社交类, 门户类还是其他各类 Web 站点, 大家都非常重视用户交互, 只有跟用户交互了, 才能进一步留住用户, 只有留住了用户, 才能知道用户需求, 知道了用户需求, 才会产生商机, 有了用户, 就等于有了流量, 才能够骗到... 额... 是融到钱, 有了资金企业才能继续发展, 可见, 用户交互是非常重要的, 甚至可以说是至关重要的一个基础功能.
而谈到用户交互, 则必须要谈到我们今天所要学习的知识点, 认证, 权限和频率. 首先我们来看看认证.
认证组件
使用 token
大部分人都知道 cookie 和 session 这两种方式可以保存用户信息, 这两种方式不同的是 cookie 保存在客户端浏览器中, 而 session 保存在服务器中, 他们各有优缺点, 配合起来使用, 可将重要的敏感的信息存储在 session 中, 而在 cookie 中可以存储不太敏感的数据.
而 token 称之为令牌. cookie,session 和 token 都有其应用场景, 没有谁好谁坏, 不过开发数据接口类的 Web 应用, 目前用 token 还是比较多的.
token 认证的大致步骤是这样的:
用户登录, 服务器端获取用户名密码, 查询用户表, 如果存在该用户且第一次登录(或者 token 过期), 生成 token, 否则返回错误信息
如果不是第一次登录, 且 token 未过期, 更新 token 值
定义 url
- 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 , 如下所示:
- 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 值:
- 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.username, 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
类 BookSerializer
多个认证类
需要注意的是, 如果需要返回什么数据, 请在最后一个认证类中返回, 因为如果在前面返回, 在源码的 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.utils.authentication.UserAuth',
- 'authenticator.utils.authentication.UserAuth2',
- ),
- }
- ~>.<~
来源: https://www.cnblogs.com/pungchur/p/12028381.html