教程4--认证和权限

教程4–认证和权限

目前我们的API对于谁可以编辑或者删除代码段没有任何的限制。我们希望能有一些更加高级的行为来确保:

  • 代码段总是能够关联到一个创建者。
  • 只有认证通过的用户才能够创建代码段。
  • 只有代码段的创建者才能够更新或者删除代码段。
  • 没有通过认证的用户拥有只读权限。

为我们的模型添加信息

让我们给之前的Snippet模型添加一些信息。首先,先添加一些字段。因为要关联到谁创建了代码段,所以要添加creator字段。其他字段用来存储高亮的HTML。

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

我们也需要确保当模型被保存时,使用pygments代码高亮库来填充highlighted字段。
我们需要一些额外的引入:

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

此时,我们可以为我们的模型添加.save()方法:

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 = self.linenos and 'table' or False
    options = self.title and {'title': self.title} or {}
    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 tmp.db db.sqlite3
rm -r snippets/migrations
python manage.py makemigrations snippets
python manage.py migrate

你可能想要多创建几个不同的用户用以测试,最方便的方法莫过于使用createsuperuser命令了。

python manage.py createsuperuser

为User模型添加端点

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

我们使用了ModelSerializer,它会包含User这个Model的所有字段。但是由于’snippets’对于User来说是一个反向的关系,所以UserSerializer默认是不会包含snippets的,我们需要自行添加。

我们也需要添加些View,但对于User来说,只读就可以了,因此我们使用ListAPIViewRetrieveAPIView来生成基于类的视图。

class UserList(generics.ListAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer


class UserDetail(generics.RetrieveAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer

最后添加上新的urls映射:

    url(r'^users/$', views.UserList.as_view()),
    url(r'^user/(?P[0-9]+)/$', views.UserDetail.as_view())

将Users合并到Snippets中

到目前为止,如果我们创建一个代码段,还没有办法把User的信息添加到Snippet对象中去。
我们的解决方案是重写snippet视图的.perform_create()方法,允许我们修改实例的保存方式,并且处理所有隐含在请求或者请求URL中的信息。

在SnippetList中,添加如下方法:

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

现在snippet的create()方法在调用时将会传入一个owner字段,和validated_data一起传入。

更新序列化器

现在,代码片段已经和创建他们的用户相关联。让我们来更新SnippetSerializer。在serializers.py中添加如下代码:

owner = serializers.ReadOnlyField(source='owner.username')

注意:请确保你在Meta中也添加了owner,
这个字段做了一些很有意思的事情。source字段控制哪个属性用于填充字段,并且可以指向序列化实例的任何一个属性。而且还可以像上面一样,用点号获取,这样它会遍历给定的属性,就类似于Django的模板语言。
这里我们添加的Field是一个无类型Field——ReadOnlyField。不同于有类型的Field,该类型的Filed只读,只用于做展示,不能用于更新。这里我们也可以使用CharField(read_only=True)

为视图添加权限

现在我们已经将code snippets和它们的创建者关联起来了,我们想要确保只有认证了的用户才能创建、更新和删除代码段。
REST框架提供了一套权限类,可以让我们来限制可以访问View的用户。我们当前的案例中,我们是希望没认证的用户只能读取,认证的可以修改,所以应该使用IsAuthenticatedOrReadOnly
如何使用呢?
首先要引入permissions模块

from rest_framework import permissions

然后再SnippetList和SnippetDetail视图都加入以下语句:

permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

添加登录API

此时,如果你打开浏览器,访问Snippet的API,你会发现你现在已经创建不了新的代码段了。为了能够创建代码,我们必须登录。
我们可以添加一个登录视图,通过编辑URLconfig来添加。编辑项目最顶层的urls.py文件。

from django.conf.urls import include

在文件的结尾处添加一个pattern,用于登录和注销。

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

现在如果你现在再次打开浏览器,可以看到登录按钮,登陆后就可以创建或修改代码段了。

对象级权限控制

事实上,我们只是控制了认证的人拥有写权限,但是我们预期的是只有当前项的创建者才能修改或删除该值。
为了能够实现这种控制,我们需要创建一个自定义的权限。
在snippets app中,创建一个新的文件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

现在我们可以把自定义的权限添加到snippet视图中了,通过编辑SnippetDetail视图的permission_class属性:

from snippets.permissions import IsOwnerOrReadOnly

....

permission_classes = (permissions.IsAuthenticatedOrReadOnly,
                      IsOwnerOrReadOnly,)

现在,如果你重新打开浏览器,你会发现只有你创建的Snippet才会出现DELETE或者PUT的动作。

使用API进行验证

目前,我们没有设置任何的Authentication类,所以默认情况下是使用SessionAuthenticationBasicAuthentication
当我们通过浏览器访问API,我们可以通过登录页面登录,并且浏览器的session会保存认证所必须的数据。
如果我们使用命令行交互,则必须为每一个请求都显式指定验证数据。
如果我们项要创建一个Snippet,但是又没有登录,我们会得到以下错误:

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

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

如果带上了验证信息,那么请求就能够执行成功了。

http -a tom:password123 POST http://127.0.0.1:8000/snippets/ code="print 789"

{
    "id": 5,
    "owner": "tom",
    "title": "foo",
    "code": "print 789",
    "linenos": false,
    "language": "python",
    "style": "friendly"
}

总结

现在我们已经为我们的Web API提供了相当细粒度的权限控制了。
在下一章中,我们讲学习如何将一切联系在一起,创造出高亮代码段的HTML endpoint,并利用超链接关系提高我们API之间的关联。

你可能感兴趣的:(瞎翻译,python)