Skip to content
本页目录

权限是 web 应用的重要组成部分。没有权限控制,任何人都可以对任何资源进行更改操作,那就太恐怖了。

本章来看看 DRF 中如何进行权限管理。

文章与用户

依靠用户身份来限制权限是比较通用的做法。这就需要给文章模型添加用户外键,确定每篇文章的作者了。保险起见,首先删除现有的所有文章数据。

修改文章的 model,让每篇文章都对应一个作者:

python
# article/models.py

...

from django.contrib.auth.models import User

class Article(models.Model):
    author = models.ForeignKey(
        User, 
        null=True,
        on_delete=models.CASCADE, 
        related_name='articles'
    )
    ...

执行迁移:

python
> python manage.py makemigrations
> python manage.py migrate

启动服务器后,查看当前文章列表:

python
> http http://127.0.0.1:8000/api/article/
        
HTTP/1.1 200 OK
...

[]

空空如也。

接下来就拿文章列表接口开刀。

权限控制

DRF 内置了如 IsAuthenticatedIsAdminUserAllowAny 等权限控制类。

由于是个人博客,因此只准许管理员发布文章。修改文章列表视图如下:

python
# article/views.py

...
from rest_framework.permissions import IsAdminUser

class ArticleList(generics.ListCreateAPIView):
    ...
    # 新增
    permission_classes = [IsAdminUser]

permission_classes 可以接收一个列表,因此权限控制类可以设置多个,请求必须满足所有控制条件才允许被放行。

测试一下:

python
> http http://127.0.0.1:8000/api/article/
HTTP/1.1 403 Forbidden
...
{
    "detail": "Authentication credentials were not provided."
}


> http POST http://127.0.0.1:8000/api/article/ title=may body=notSuccess
HTTP/1.1 403 Forbidden
...
{
    "detail": "Authentication credentials were not provided."
}

倒是确实起作用了,但是除了管理员之外其他人连查看都没权限了,显示这不是我们想要的。

好在自定义一个权限类也不难。在文章 app 中创建 article/permissions.py 文件,写入以下代码:

python
# article/permissions.py

from rest_framework import permissions

class IsAdminUserOrReadOnly(permissions.BasePermission):
    """
    仅管理员用户可进行修改
    其他用户仅可查看
    """
    def has_permission(self, request, view):
        # 对所有人允许 GET, HEAD, OPTIONS 请求
        if request.method in permissions.SAFE_METHODS:
            return True

        # 仅管理员可进行其他操作
        return request.user.is_superuser

自定义的权限类继承了 BasePermission 这个基础的父类,并实现了父类中的钩子方法 def has_permission。此方法在每次请求到来时被唤醒执行,里面简单判断了请求的种类是否安全(即不更改数据的请求),如果安全则直接通过,不安全则只允许管理员用户通过。

再次修改视图:

python
# article/views.py

...

# from rest_framework.permissions import IsAdminUser
from article.permissions import IsAdminUserOrReadOnly

class ArticleList(generics.ListCreateAPIView):
    ...
    permission_classes = [IsAdminUserOrReadOnly]

# 顺便把详情视图的权限也更改了
class ArticleDetail(generics.RetrieveUpdateDestroyAPIView):
    ...
    permission_classes = [IsAdminUserOrReadOnly]

这就可以了。

首先在命令行测试一下用户未登录时的情况:

python
> http http://127.0.0.1:8000/api/article/
# GET 请求成功
HTTP/1.1 200 OK
...
[]


> http POST http://127.0.0.1:8000/api/article/ title="post with permission" body="new test"
# POST 请求失败
HTTP/1.1 403 Forbidden
...
{
    "detail": "Authentication credentials were not provided."
}

在后台中创建一个普通用户 Obama(这也是基础,不懂还是参考这篇文章),用普通用户身份进行请求:

python
# 普通用户 Obama,密码 admin123456

> http -a Obama:admin123456 http://127.0.0.1:8000/api/article/
# GET 请求成功
HTTP/1.1 200 OK
...
[]


> http -a Obama:admin123456 POST http://127.0.0.1:8000/api/article/ title="post with permission" body="new test"
# POST 请求失败    
HTTP/1.1 403 Forbidden
...
{
    "detail": "You do not have permission to perform this action."
}

最后,再用管理员用户 dusai 测试:

python
# 管理员用户 dusai,密码 admin123456

> http -a dusai:admin123456 http://127.0.0.1:8000/api/article/
# GET 请求成功
HTTP/1.1 200 OK
...
[]


> http -a dusai:admin123456 POST http://127.0.0.1:8000/api/article/ title="post with permission" body="new test"
# POST 请求成功
HTTP/1.1 201 Created
...
{
    "created": "2020-07-03T04:56:26.869523Z",
    "id": 2,
    "title": "post with permission"
}

任何人都可以查看资源;但是新增(CREATE)、更新(PUT)、删除(DELETE)等修改操作就只允许管理员执行。