Django可以用LoginRequiredMixinPermissionRequiredMixin给类视图添加认证和权限,DRF做了高级封装,提供了更简洁的实现方式。我们通过继续学习官网教程来进行了解。

更新model

首先修改Snippet模型,添加2个字段:owner,存储snippet创建者,highlighted,存储高亮HTML。同时重写save方法,在同步数据库的时候,使用pygments包把code格式化后存到highlighted字段。修改后的snippets/models.py完整代码如下:

from django.db import models
from pygments.lexers import get_all_lexers
from pygments.styles import get_all_styles
from pygments.lexers import get_lexer_by_name
from pygments.formatters.html import HtmlFormatter
from pygments import highlight LEXERS = [item for item in get_all_lexers() if item[1]]
LANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])
STYLE_CHOICES = sorted([(item, item) for item in get_all_styles()]) class Snippet(models.Model):
created = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=100, blank=True, default='')
code = models.TextField()
linenos = models.BooleanField(default=False)
language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)
style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)
owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)
highlighted = models.TextField() class Meta:
ordering = ['created'] def save(self, *args, **kwargs):
"""
Use the `pygments` library to create a highlighted HTML
representation of the code snippet.
"""
lexer = get_lexer_by_name(self.language)
linenos = 'table' if self.linenos else False
options = {'title': self.title} if self.title else {}
formatter = HtmlFormatter(style=self.style, linenos=linenos,
full=True, **options)
self.highlighted = highlight(self.code, lexer, formatter)
super(Snippet, self).save(*args, **kwargs)

接着删除数据库和migrations,重新迁移数据库:

rm -f db.sqlite3
rm -r snippets/migrations
python manage.py makemigrations snippets
python manage.py migrate

并创建超级管理员:

python manage.py createsuperuser

User添加Endpoint

Endpoint,表示API的具体网址。我们按照models.pyserializers.pyviews.pyurls.py的代码编写顺序,给User模型添加Endpoint。

models.py

直接使用Django默认User模型,不需要修改代码。

serializers.py

添加UserSerializer,由于User没有snippets字段,所以需要显式添加:

from django.contrib.auth.models import User

class UserSerializer(serializers.ModelSerializer):
snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all()) class Meta:
model = User
fields = ['id', 'username', 'snippets']

views.py

添加只读的列表视图UserList和详情视图UserDetail,分别用到了ListAPIViewRetrieveAPIView

from django.contrib.auth.models import User
from snippets.serializers import UserSerializer class UserList(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer class UserDetail(generics.RetrieveAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer

urls.py

添加访问路径:

path('users/', views.UserList.as_view()),
path('users/<int:pk>/', views.UserDetail.as_view()),

关联User和Snippet

如果使用POST方法请求http://127.0.0.1:8000/snippets/,尝试添加1条数据:

会发现接口报错了:

owner_id不能为空?因为前面只给Snippet添加了owner字段,还没有写反序列化更新模型的代码,所以通过请求访问视图,再尝试反序列化的时候,报错了。我们先修改视图SnippetList来修复这个问题:

def perform_create(self, serializer):
serializer.save(owner=self.request.user)

SnippetList视图中重写perform_create()方法,意思是在保存时,把request.user值赋给owner字段。perform_create()方法的源码是:

class CreateModelMixin:
"""
Create a model instance.
"""
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) def perform_create(self, serializer):
serializer.save()

再修改snippets/serializers.py,添加owner字段,支持序列化:

class SnippetSerializer(serializers.ModelSerializer):
# ReadOnlyField表示只能序列化为JSON,不能反序列化更新模型
# 也可以改成CharField(read_only=True)
owner = serializers.ReadOnlyField(source='owner.username') class Meta:
model = Snippet
fields = ['id', 'title', 'code', 'linenos', 'language', 'style', 'owner']

注意Meta.fields也要加上owner哦。

再请求一次:

刚才的错误没有了,但是报了个新的错误:Snippet.owner必须是User实例,给它赋值的是AnonymousUser(匿名用户),导致ValueError了。这个报错是发生这条代码:

serializer.save(owner=self.request.user)

也就是说请求访问视图后,进行反序列化了,但是反序列化失败了。非常奇怪!我们的请求中并没有用户信息,正常来说在访问视图的时候就该被拦截了。

给视图添加认证

我们需要让API更符合常规,让未认证的用户不能执行视图中的代码。DRF提供了rest_framework .permissions来给视图添加认证:

其中IsAuthenticatedOrReadOnly表示只有认证了才能读写,否则只能读。把它添加到SnippetListSnippetDetail视图中:

from rest_framework import permissions

permission_classes = [permissions.IsAuthenticatedOrReadOnly]

再请求试试,刚才的错误没有了,API返回的是需要提供用户凭证:

登录视图

如果用浏览器打开http://127.0.0.1:8000/snippets/,会发现只有GET方法没有POST,这是因为需要添加DRF登录视图,在tutorial/urls.py中添加rest_framework.urls

urlpatterns += [
path('api-auth/', include('rest_framework.urls')),
]

api-auth/可以自定义。

刷新页面右上角就会出现Log in按钮,登录后就能POST了。

对象级权限

为了更细粒度的控制权限,让用户只能编辑自己创建的snippet,新建snippets/permissions.py

from rest_framework import permissions

class IsOwnerOrReadOnly(permissions.BasePermission):
"""
Custom permission to only allow owners of an object to edit it.
""" def has_object_permission(self, request, view, obj):
# Read permissions are allowed to any request,
# so we'll always allow GET, HEAD or OPTIONS requests.
if request.method in permissions.SAFE_METHODS:
return True # Write permissions are only allowed to the owner of the snippet.
return obj.owner == request.user

新增IsOwnerOrReadOnly权限,继承了permissions.BasePermission,重写了has_object_permission()方法。接着在snippets/views.py中给SnippetDetail加上:

from snippets.permissions import IsOwnerOrReadOnly

permission_classes = [permissions.IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly]

试下访问其他用户创建的snippet,发现只能查看:

访问自己创建的snippet,可以修改和删除:

自定义权限

以上是官网的示例,我在Postman测试了下,发现超管dongfanger可以创建snippet

普通用户player也可以创建snippet

我想让普通用户不能创建,只能超管创建。仿照官网示例,在snippets/permissions.py中添加IsAdminOrReadOnly

class IsAdminOrReadOnly(permissions.BasePermission):
def has_permission(self, request, view):
return request.user.is_superuser

接着给SnippetList加上:

permission_classes = [permissions.IsAuthenticatedOrReadOnly,
IsAdminOrReadOnly]

用普通用户尝试创建,提示没有权限:

用超级管理员尝试创建,成功:

其他认证方式

本文使用的认证方式是默认的SessionAuthenticationBasicAuthentication,只要数据库的用户名、密码和请求中的用户凭证(用户名、密码)匹配上了,就认为认证成功。如果要实现token或jwt认证,需要使用到rest_framework.authentication

rest_framework_jwt.authentication

pip install djangorestframework-jwt

这一部分内容官网教程中并没有提及,等我们把教程学完了,以后再找时间来介绍。

东方说

DRF实现认证和权限的关键在于新增permissions.py模块,编写class,继承permissions.BasePermission,重写has_permission()has_object_permission()方法,再添加class到类视图的permission_classes中。这块的内容比Django的认证系统那套简洁,但是有点混淆,另外我之前参照网上实现了一版JWT,也有点不一样。看来还得写篇对比的文章才行。

参考资料:

https://www.django-rest-framework.org/tutorial/4-authentication-and-permissions/

DRF比Django的认证和权限高在哪里的更多相关文章

  1. DRF的版本、认证、权限

    DRF的版本 版本控制是做什么用的, 我们为什么要用 首先我们要知道我们的版本是干嘛用的呢~~大家都知道我们开发项目是有多个版本的~~ 当我们项目越来越更新~版本就越来越多~~我们不可能新的版本出了~ ...

  2. drf之组件(认证、权限、排序、过滤、分页等)和xadmin、coreapi

    认证Authentication 可以在配置文件中配置全局默认的认证方案 REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_fr ...

  3. django身份认证、权限认证、频率校验使用及源码分析

    一. 身份认证源码分析 1.1 APIView源码的分析 APIView源码之前分析过https://www.cnblogs.com/maoruqiang/p/11135335.html,里面主要将r ...

  4. DRF之版本控制、认证和权限组件

    一.版本控制组件 1.为什么要使用版本控制 首先我们开发项目是有多个版本的当我们项目越来越更新,版本就越来越多,我们不可能新的版本出了,以前旧的版本就不进行维护了像bootstrap有2.3.4版本的 ...

  5. DRF 认证、权限、限制

    DRF 认证.权限.限制   认证: 定义一个用户表和一个保存用户的Token表 # ======================day96======================= class ...

  6. DRF(4) - 认证、权限组件

    一.引入 通过前面三节课的学习,我们已经详细了解了DRF提供的几个重要的工具,DRF充分利用了面向对象编程的思想,对Django的View类进行了继承,并封装了其as_view方法和dispatch方 ...

  7. drf框架中认证与权限工作原理及设置

    0909自我总结 drf框架中认证与权限工作原理及设置 一.概述 1.认证 工作原理 返回None => 游客 返回user,auth => 登录用户 抛出异常 => 非法用户 前台 ...

  8. drf框架 - 三大认证组件 | 认证组件 | 权限组件 | 频率组件

    RBAC 基于用户权限访问控制的认证 - Role-Based Access Control Django框架采用的是RBAC认证规则,RBAC认证规则通常会分为 三表规则.五表规则,Django采用 ...

  9. Django编写RESTful API(四):认证和权限

    欢迎访问我的个人网站:www.comingnext.cn 前言: 按照前面几篇文章里那样做,使用Django编写RESTful API的基本功能已经像模像样了.我们可以通过不同的URL访问到不同的资源 ...

随机推荐

  1. ABBYY FineReader 15 对比文档功能

    想必大家在办公的时候都有着要处理各种各样文档的烦恼,一个文档经过一个人或不同人的多次修订都是常有的事,拥有文档对比功能的软件也就应势而生.ABBYY FineReader 15 有许多能够帮助我们办公 ...

  2. 攻克弹唱第九课(如何运用好G大调和弦)

    在本期文章中,笔者将使用guitar pro7软件与大家分享如何运用好G大调音阶的经验. 众所周知,在我们学习吉他的过程中,先从C大调开始,再以G大调为深入,然后才走过入门的阶段.很多朋友都觉得自己对 ...

  3. Codeforces Round #660 (Div. 2) A、B、C题解

    A. Captain Flint and Crew Recruitment #构造 题目链接 题意 定义一类正整数,能够被\(p*q\)表示,其中\(p.q(1<p<q)\)均为素数,称之 ...

  4. 《Spring Boot 实战纪实》之如何攥写需求文档

    目录 前言 (思维篇)人人都是产品经理 1.需求文档 1.1 需求管理 1.2 如何攥写需求文档 1.3 需求关键点文档 2 原型设计 2.1 缺失的逻辑 2.2 让想法跃然纸上 3 开发设计文档 3 ...

  5. Appium上下文和H5测试(二)

    坚持原创输出,点击蓝字关注我吧 作者:清菡 博客:oschina.云+社区.知乎等各大平台都有. 文章总览图 一.往期回顾 loc='new UiSelector().text("全程班&q ...

  6. 流量控制--6.Classful Queuing Disciplines (qdiscs)

    Classful Queuing Disciplines 可以使用classful qdisc的代理来解锁Linux流量控制的灵活性和控制力.classful qdisc可以附加过滤器,允许将报文重定 ...

  7. CentOS6.5&7更改开机启动时的CentOS标题

    #现有CentOS6.5改以下配置文件 sed -i 's/CentOS/DntOS/g' /etc/centos-release sed -i 's/CentOS/DntOS/g' /etc/iss ...

  8. 总结一下 php连接oracle,完全可用。

    大致有两种方法 第一种 开启php_pdo_oci扩展,一般集成环境都会有这个扩展. 这个东西还是比较简单的,去官网查看吧 http://php.net/manual/zh/book.pdo.php ...

  9. AndroidStudio中默认不导入org.apache.http等包的解决方法

    参考:http://www.cnblogs.com/xiadongqing/p/5942459.html Eclipse ADT中默认引入了org.apache.http包,而AndroidStudi ...

  10. 从docker介绍及其简介

    一.引言 1.我本地代码运行没问题啊,但是别人机器运行不了,从而导致环境不一致的问题 2.那个兄弟又写死循环了,怎么这么卡,在多用户的操作系统下,会相互影响. 天猫双十一的情况下,用户量暴涨,从而导致 ...