Django REST框架 Authentication & Permissions

教程4:身份验证和权限

目前,我们的API对谁可以编辑或删除snippets没有任何限制。我们希望有一些更高级的行为,以确保:

  • snippets始终与创建者相关联。
  • 只有经过身份验证的用户才能创建摘要。
  • 只有snippets的创建者可以更新或删除它。
  • 未经身份验证的请求应具有完全只读访问权限。

向我们的模型添加信息

我们将对我们的Snippet模型类进行一些更改。首先,让我们添加几个字段。其中一个字段将用于表示创建snippets的用户。另一个字段将用于存储代码的突出显示的HTML表示。

将以下两个字段添加到Snippet模型中models.py

owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)
highlighted = models.TextField()

我们还需要确保在保存模型时,使用pygments代码突出显示库填充突出显示的字段。

我们需要一些额外的import:

from pygments.lexers import get_lexer_by_name
from pygments.formatters.html import HtmlFormatter
from pygments import highlight

现在我们可以.save()Snippet模型类添加一个方法:

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)

完成所有操作后,我们需要更新数据库表。通常我们会创建一个数据库迁移来执行此操作,但是出于本教程的目的,我们只需删除数据库并重新开始。

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

您可能还想创建一些不同的用户,以用于测试API。最快的方法是使用createsuperuser命令。

python3 manage.py createsuperuser

为我们的用户模型添加端点

既然我们已经有一些用户可以使用,我们最好将这些用户的表示添加到我们的API中。创建新的序列化器很简单。另外serializers.py

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')

因为在User模型上'snippets'反向关系,所以在使用ModelSerializer类时默认情况下不会包含它,因此我们需要为它添加显式字段。

我们还会添加几个视图views.py。我们希望只使用只读视图为用户表示,所以我们将使用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

最后,我们需要将这些视图添加到API中,方法是从URL conf中引用它们。将以下内容添加到模式中snippets/urls.py

    url('users/([0-9]+)/', views.UserDetail.as_view()),
    url('users/', views.UserList.as_view()),

将代码snippets与users关联起来

现在,如果我们创建了snippets,则无法将创建snippets的用户与snippets实例相关联。用户不是作为序列化表示的一部分发送的,而是传入请求的属性。

我们处理的.perform_create()方法是通过覆盖我们的snippets视图上的方法,允许我们修改实例保存的管理方式,并处理传入请求或请求的URL中隐含的任何信息。

SnippetList视图类上,添加以下方法:

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

create()我们的序列化器的方法现在将传递一个额外的'owner'字段,以及来自请求的验证数据。

更新我们的serializer

现在,片段与创建它们的用户相关联,让我们更新我们SnippetSerializer以反映这一点。将以下字段添加到序列化程序定义中serializers.py

class SnippetSerializer(serializers.ModelSerializer):
    owner = serializers.ReadOnlyField(source='owner.username')
    class Meta:
        model = Snippet
        fields = ('id', 'title', 'code', 'linenos', 'language', 'style','owner')

注意:确保您还要添加'owner',到内部Meta类中的字段列表。

这个领域做得非常有趣。的source哪个属性参数控制用于填充的字段,并且可以在对串行化实例的任何属性点。它也可以采用上面显示的虚线表示法,在这种情况下,它将以与Django模板语言一样的方式遍历给定的属性。

我们添加了字段是类型化ReadOnlyField类,相对于其他类型的字段,如CharFieldBooleanField等...类型化ReadOnlyField始终是只读的,并且将用于序列化表示形式,但不会被用于更新模型它们被反序列化的实例。我们也可以CharField(read_only=True)在这里使用。

向视图添加所需权限

既然代码片段与用户相关联,我们希望确保只有经过身份验证的用户才能创建,更新和删除snippets。

REST框架包含许多权限类,我们可以使用这些权限来限制谁可以访问给定视图。在这种情况下,我们正在寻找的是IsAuthenticatedOrReadOnly,这将确保经过身份验证的请求获得读写访问权限,未经身份验证的请求将获得只读访问权限。

首先在views模块中添加以下导入

from rest_framework import permissions

接着,下面的属性添加到SnippetListSnippetDetail视图类。

permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

添加登录到Browsable API

如果您此时打开浏览器并导航到可浏览的API,您将发现您无法再创建新的snippets。为此,我们需要能够以用户身份登录。

我们可以通过编辑tutorial级urls.py文件中的URLconf来添加用于可浏览API的登录视图。

在文件顶部添加以下导入:

from django.conf.urls import include

并且,添加一个模式以包含可浏览API的登录和注销视图。

urlpatterns = [
    url('^', include('snippets.urls')),
    url(r'^api-auth/', include('rest_framework.urls')),
]

'api-auth/'模式的一部分实际上可以是您想要使用的任何URL。

现在,如果您再次打开浏览器并刷新页面,您将在页面右上角看到“登录”链接。如果您以之前创建的用户之一登录,则可以再次创建snippets。

创建几个snippets后,导航到'/ users /'端点,并注意该表示包含每个用户的“snippets”字段中与每个用户关联的snippetsID列表。

对象级权限

实际上,我们希望所有人都可以看到所有snippets,但也要确保只有创建snippets的用户才能更新或删除它。

为此,我们需要创建自定义权限。

在snippets应用中,创建一个新文件, permissions.py

from rest_framework import permissions
from snippets.permissions import IsOwnerOrReadOnly

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

现在,我们可以通过编辑视图类的permission_classes属性将该自定义权限添加到我们的snippets实例端点SnippetDetail

permission_classes = (permissions.IsAuthenticatedOrReadOnly,
                      IsOwnerOrReadOnly,)

现在,如果再次打开浏览器,如果您以创建snippets的同一用户身份登录,则会发现“DELETE”和“PUT”操作仅显示在snippets实例端点上。

使用API进行身份验证

因为我们现在拥有API的一组权限,所以如果我们想要编辑任何snippets,我们需要对它们的请求进行身份验证。我们还没有设置任何身份验证类,因此当前应用了默认值,即SessionAuthenticationBasicAuthentication

当我们通过Web浏览器与API交互时,我们可以登录,然后浏览器会话将为请求提供所需的身份验证。

如果我们以编程方式与API交互,我们需要在每个请求上明确提供身份验证凭据。

如果我们尝试在不进行身份验证的情况下创建snippets,则会收到错误消息:

http POST http://127.0.0.1:8000/snippets/ code="print(123)"

{
    "detail": "Authentication credentials were not provided."
}

我们可以通过包含我们之前创建的用户之一的用户名和密码来成功提出请求。

http -a admin:password123 POST http://127.0.0.1:8000/snippets/ code="print(789)"

{
    "id": 1,
    "owner": "admin",
    "title": "foo",
    "code": "print(789)",
    "linenos": false,
    "language": "python",
    "style": "friendly"
}

转载于:https://www.jianshu.com/p/bf4306e76bc6

猜你喜欢

转载自blog.csdn.net/weixin_34167043/article/details/91068877