• django restframwork 教程之authentication权限


    当前我们的API在编辑或者删除的时候没有任何限制,我们不希望有些人有高级的行为,确保:

    • 代码段始终与创建者相关联
    • 只允许授权的用户可以创建代码段
    • 只允许代码段创建者可以更新和删除
    • 没有认证的请求应该有一个完整的只读权限列表

    添加用户信息在我们的models中

    我们将对snippet models 进行一些更改,首先,让我们添加一对fields,其中一个跟我们的用户表关联,剩下一个字段用作存储高亮的html,还需要导入pygments库来高亮code,最后需要重新定义save方法,添加一些我们自定义配置

    修改后的models如下

    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()
    
        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)
    
    
        class Meta:
            ordering = ('created',)
    

    接下来我们需要将新字段在数据库中添加,为了中间不必要的麻烦,现在我们直接删除原来的库和migration目录,重新生成

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

    最后我们需要创建一个超级用户来测试API,使用createsuperuser来快速创建

    python manage.py createsuperuser
    

    为我们user models添加serializers

    我们需要在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')
    

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

    我们还会向views.py添加几个视图。我们只想对用户表示使用只读视图,因此我们将使用ListAPIView和RetrieveAPIView通用类的视图。

    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
    

    最后我们需要为刚才的user 视图添加url配置,修改urls.py

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

    将代码段与用户关联

    现在,如果我们创建了代码段,就无法将创建代码段的用户与代码段实例相关联。用户不作为序列化表示的一部分发送,而是传入请求的属性。
    我们处理它的方式是通过在我们的代码片段视图上覆盖一个.perform_create()方法,允许我们修改实例保存的方式,并处理在传入请求或请求的URL中隐含的任何信息。
    在我们SnippetList 视图类中,添加下面的方法

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

    当我们的serializer里create()方法被调用时,将自动添加owner字段和验证合法的请求数据

    更新我们的seralizer

    现在sippets创建的时候已经和我们的用户关联起来,让我们更新我们的SnippetSerializer,添加以下定义的字段:

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

    source参数用于控制那个属性被用来填充字段,并且可以指向序列化实例上的任何属性。 它也可以采用上面所示的虚线符号,在这种情况下,它将遍历给定的属性,与使用Django的模板语言类似的方式。
    我们添加的字段是无类型的ReadOnlyField类,与其他类型的字段,例如CharField,BooleanField等相反。无类型的ReadOnlyField总是只读的,并且将用于序列化表示,但不会 用于在反序列化时更新模型实例。 我们也可以在这里使用CharField(read_only = True)。
    同时需要在meta class中添加owner字段

    class Meta:
            model = Snippet
            fields = ('id', 'title', 'code', 'linenos', 'language', 'style','owner')
    

    在views中添加请求权限

    现在snippets 已经和我们的用户关联上了,我们需要确保仅仅通过验证的用户能够增删改

    REST framework 包含有一些权限类,我们可以用来限制谁可以访问给定的视图,在这种情况下,首先我们查找IsAuthenticatedOrReadOnl,这将确保已验证的请求获得读写访问权限,并且未验证的请求获得只读访问权限。

    首先在module中导入permissions,

    from rest_framework import permissions
    

    然后在SnippetListSnippetDetail view中添加下面这个属性

    permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
    

    添加登陆WEB API

    如果您打开浏览器并转到可浏览的API,您会发现您无法再创建新的代码段。为此,我们需要能够以用户身份登录。

    我们可以通过编辑项目级urls.py文件中的URLconf来添加一个登录视图,以便与可浏览的API一起使用
    编辑urls.py,添加如下内容

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

    r'^api-auth/' 这个可以自定义,但是后面的namespace必须使用rest_framework作命名空间,但是在django1.9+版本,自动设置,不需要添加
    现在,如果您再次打开浏览器并刷新页面,您将在页面右上角看到一个“登录”链接。如果您以之前创建的用户之一登录,则可以再次创建代码段
    创建几个代码段后,转到“/ users /”端点,并注意该表示中包含每个用户的“代码段”字段中与每个用户相关联的代码段ID列表。

    对象级别的权限

    我们希望所有的代码片段对任何人都可见,但也确保只有创建代码片段的用户能够更新或删除它。为了实现这个,我们需要创建一个自定义权限
    在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
    

    现在我们在SnippetDetail view中通过编辑permission_classes属性为snippet实例中添加自定义的权限

    permission_classes = (permissions.IsAuthenticatedOrReadOnly,
                          IsOwnerOrReadOnly,)
    

    同时确保导入了自定的权限类

    from snippets.permissions import IsOwnerOrReadOnly
    

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

    使用api进行身份验证

    因为我们现在有一组API的权限,如果我们想要编辑任何snippet,我们需要使用SessionAuthenticationBasicAuthentication验证我们的请求
    当我们通过Web浏览器与API交互时,我们可以登录,然后浏览器会话将为请求提供所需的身份验证。
    如果我们以编程方式与API交互,我们需要在每个请求中显式提供身份验证凭据。
    如果我们尝试创建一个未验证的代码段,我们会收到一条错误:

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

    我们在请求的时候加入验证,我们将可以创建:

    curl  -X POST  -s -u test:1234.com -H "Content-Type:application/json"  -d  '{"title": "sec test","code": "python asdasdas manasdnasd", "linenos": false,"language": "python","style": "vim","owner": "fuzengjie"}' http://127.0.0.1:8000/snippets/ | jq .
    {
      "id": 2,
      "title": "sec test",
      "code": "python asdasdas manasdnasd",
      "linenos": false,
      "language": "python",
      "style": "vim",
      "owner": "test"
    }
    
    
  • 相关阅读:
    mysql慢日志设置
    CURL模拟登陆
    违法图片检测
    PHP取二进制文件头快速判断文件类型
    重写session
    mysql处理高并发,防止库存超卖
    mysql中使用update select
    PHP中使用Luhn算法校验信用卡及借记卡卡号
    红包算法
    DIV当textarea使用,在聚焦的时候将光标移动到内容的末尾
  • 原文地址:https://www.cnblogs.com/pycode/p/6525456.html
Copyright © 2020-2023  润新知