DRF-Authentication&Permissions

Authentication

身份验证是将传入Request与一组标识凭据 (如请求来自的用户或与之签名的令牌) 相关联的机制。然后, 权限和限制策略可以使用这些凭据来确定是否应允许该请求。

DRF提供了许多现成的身份验证方案, 还允许您实现自定义方案。

身份验证始终在视图一开始、在进行权限和限制检查之前以及在允许任何其他代码继续之前运行。

request.user属性通常会被设置成contrib.auth包内User类的实例。

request.auth属性用于任何身份验证信息, 例如, 它可用于表示与请求签名的身份验证令牌。

如何进行身份验证

首先,身份验证方案被定义为一个类的列表。列表里的每个类都是一种身份验证方式。DRF会尝试按照列表里的每个类进行身份验证。最终将第一个类的返回结果设置在request.userrequest.auth值班中。

如果,列表为空,也就是没有身份验证,request.user会被设置成django.contrib.auth.models.AnoymouseUser的一个实例,并且request.auth会被设置成None

未经验证请求的request.userrequest.auth的值可以通过UNAUTHENTICATED_USERUNAUTHENTICATED_TOKEN设置修改。

设置身份验证

全局设置

使用settings.py文件中的DEFAULT_AUTHENTICATION_CLASSES选项进行身份验证的全局配置:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    )
}

视图内配置

  1. 使用authentication_classes选项在APIView内配置:
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView

class ExampleView(APIView):
    authentication_classes = (SessionAuthentication, BasicAuthentication)
    permission_classes = (IsAuthenticated,)

    def get(self, request, format=None):
        content = {
            'user': unicode(request.user),  # `django.contrib.auth.User` instance.
            'auth': unicode(request.auth),  # None
        }
        return Response(content)
  1. 使用@authentication_classes装饰器在@api_view装饰器后设置:
    @api_view(['GET'])
    @authentication_classes((SessionAuthentication, BasicAuthentication))
    @permission_classes((IsAuthenticated,))
    def example_view(request, format=None):
     content = {
         'user': unicode(request.user),  # `django.contrib.auth.User` instance.
         'auth': unicode(request.auth),  # None
     }
     return Response(content)
    

response

当未经验证的请求没有权限访问时,可能会返回两种不同的错误码:

  • HTTP 401 Unauthorized
  • HTTP 403 Permission Denied

HTTP 401response通常会包含一个WWW-Authenticate 头部, 来介绍客户端应如何验证。HTTP 403response不包含WWW-Authenticate头部。

通常在视图中第一个验证的类会被用来决定response的类型。

当一个请求已经成功完成身份验证,但仍然没有权限执行请求。这个时候,就会返回403 Permission Denied response。

Apache mod_wsgi specific configuration

请注意, 如果使用 mod _ wsgi 部署到 Apache, 则默认情况下不会将授权header传递给WSGI应用程序, 因为假定身份验证将由Apache 处理, 而不是在应用程序级别处理。

如果要部署到Apache, 并使用任何非会话身份验证, 则需要显式配置 mod _ wsgi, 以便将所需的header传递给应用程序。这可以通过在适当的上下文中指定WSGIPassAuthorization指令并将其设置为 'On' 来实现。

# this can go in either server config, virtual host, directory or .htaccess
WSGIPassAuthorization On

认证类型

默认内置认证类型

第三方认证库

https://www.django-rest-framework.org/api-guide/authentication/#third-party-packages

比如:

JSON Web Token Authentication

自定义认证

若要实现自定义身份验证方案, 需要继承 BaseAuthentication类并重写.authenticate(self, request)方法。如果身份验证成功, 该方法应返回(user, auth), 否则返回None

在某些情况下, 您可能希望从.authenticate()抛出AuthenticationFailed异常, 而不是返回 None.

通常, 您应该采取的方法是:

  • 如果未尝试身份验证, 则返回 None。仍将检查正在使用的任何其他身份验证方案
  • 如果尝试了身份验证, 但失败, 则引发AuthenticationFailed 异常。无论是否进行任何权限检查, 都将立即返回错误响应, 而无需检查任何其他身份验证方案。

您也可以重写.authenticate_header(self, request)方法。如果实现, 它应该返回一个字符串, 该字符串将在HTTP 401 Unauthorized response中用作WWW-Authenticate头部的值。

Example

from django.contrib.auth.models import User
from rest_framework import authentication
from rest_framework import exceptions

class ExampleAuthentication(authentication.BaseAuthentication):
    def authenticate(self, request):
        username = request.META.get('X_USERNAME')
        if not username:
            return None

        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            raise exceptions.AuthenticationFailed('No such user')

        return (user, None)

Permissions

权限用于授予或拒绝不同类别的用户访问API不同部分。

最简单的权限是允许对任何经过身份验证的用户进行访问, 并拒绝对任何未经身份验证的用户进行访问。这对应于DRF 的IsAuthenticated类。

怎样确定权限

DRF中的Permissions始终被定义为一个权限类的列表。

在运行视图的主体之前, 将检查列表中的每个权限。如果任何权限检查失败, 则会引发exceptions.PermissionDeniedexceptions.NotAuthenticated异常,并且视图的主体将不会运行。

当权限检查失败时, 将根据以下规则返回 403 Forbidden401 Unauthorized响应:

  • 请求已成功通过身份验证, 但权限被拒绝。-将返回HTTP 403 Forbidden 响应。

  • 请求未成功通过身份验证, 并且最高优先级身份验证类不使用WWW-Authenticate header。-将返回HTTP 403 Forbidden 响应。

  • 请求未成功通过身份验证, 并且最高优先级身份验证类确实使用WWW-Authenticate header。-将返回具有适当的WWW-Authenticate header的HTTP 401 Unauthorized 响应。

Object level permissions

DRF还支持对象级权限控制。对象级别权限用于确定是否应允许用户对特定对象执行操作, 该对象通常是模型实例。

当调用.get_object() 时, 对象级别权限由DRF的泛型视图运行。与视图级别权限一样, 如果不允许用户对给定对象执行操作, 则将引发exceptions.PermissionDenied 异常。

如果您正在编写自己的视图并希望强制实施对象级别权限, 或者如果您在泛型视图上重写get_object 法, 则当您检索对象时,需要在视图上显式调用.check_object_permissions(request, obj)方法。

这将引发PermissionDeniedNotAuthenticated异常, 或者简单返回视图是否拥有合适的权限。

For example:

def get_object(self):
    obj = get_object_or_404(self.get_queryset(), pk=self.kwargs["pk"])
    self.check_object_permissions(self.request, obj)
    return obj

Setting the permission policy

全局设置

使用settings.py文件中的DEFAULT_PERMISSION_CLASSES选项进行设置:

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    )
}

当没有指定时,此设置默认为允许不受限制的访问:

'DEFAULT_PERMISSION_CLASSES': (
   'rest_framework.permissions.AllowAny',
)

视图内设置

  1. 使用permission_classesAPIView内设置: ```Python from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView

class ExampleView(APIView): permission_classes = (IsAuthenticated,)

def get(self, request, format=None):
    content = {
        'status': 'request was permitted'
    }
    return Response(content)

2. 使用`@permission_classes`在`@api_view`后使用:
```Python
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

@api_view(['GET'])
@permission_classes((IsAuthenticated, ))
def example_view(request, format=None):
    content = {
        'status': 'request was permitted'
    }
    return Response(content)

当使用类属性或者装饰设置权限类列表后,则自动忽略了全局配置中的权限列表类。

如果权限从rest_framework.permissions.BasePermission 继承, 则可以使用标准 python 按位运算符组成权限。例如, IsAuthenticatedOrReadOnly可以写成:

from rest_framework.permissions import BasePermission, IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView

class ReadOnly(BasePermission):
    def has_permission(self, request, view):
        return request.method in SAFE_METHODS

class ExampleView(APIView):
    permission_classes = (IsAuthenticated|ReadOnly)

    def get(self, request, format=None):
        content = {
            'status': 'request was permitted'
        }
        return Response(content)

仅支持 &与 和 |或。

权限类

内置权限类

第三方权限类库

Third party packages

自定义权限

若要实现自定义权限, 请重写BasePermission并实现以下方法中的一种或两种:

  • .has_permission(self, request, view)
  • .has_object_permission(self, request, view, obj)

如果授予请求访问权限, 则方法应返回True, 否则返回False

如果需要测试请求是读取操作还是写入操作, 则应对照常量SAFE_METHODS 检查请求方法, 后者是包含'GET''OPTIONS''HEAD'的元组。For example:

if request.method in permissions.SAFE_METHODS:
    # Check permissions for read-only request
else:
    # Check permissions for write request

只有视图级别的has_permission检查通过后,才会调用实例级别的has_object_permission。为了运行实例级别检查, 视图代码应显式调用为了运行实例级检查, 视图代码应显式调用. check _ object _ 权限 (请求、obj)。。如果您使用的是通用视图, 则默认情况下将为您处理此问题。(基于函数的视图需要显式检查对象权限, 并在失败时抛出PermissionDenied

如果测试失败, 自定义权限将引发PermissionDenied异常。若要更改与异常关联的错误消息, 请直接在自定义权限上实现message属性。否则, 将使用PermissionDenied中的default_detail 属性。

from rest_framework import permissions

class CustomerAccessPermission(permissions.BasePermission):
    message = 'Adding customers not allowed.'

    def has_permission(self, request, view):
         ...

Examples

下面是一个权限类的示例, 该示例根据黑名单检查传入请求的 ip 地址, 并在 ip 已被列入黑名单时拒绝该请求。

from rest_framework import permissions

class BlacklistPermission(permissions.BasePermission):
    """
    Global permission check for blacklisted IPs.
    """

    def has_permission(self, request, view):
        ip_addr = request.META['REMOTE_ADDR']
        blacklisted = Blacklist.objects.filter(ip_addr=ip_addr).exists()
        return not blacklisted

除了针对所有传入请求运行的全局权限外, 还可以创建对象级权限, 这些权限仅针对影响特定对象实例的操作运行。例如:

class IsOwnerOrReadOnly(permissions.BasePermission):
    """
    Object-level permission to only allow owners of an object to edit it.
    Assumes the model instance has an `owner` attribute.
    """

    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

        # Instance must have an attribute named `owner`.
        return obj.owner == request.user

请注意, 泛型视图将检查相应的对象级别权限, 但如果您编写自己的自定义视图, 则需要确保自己检查对象级别权限。您可以在拥有对象实例后,通过从视图中调用self.check_object_permissions(request, obj)来实现此目的。如果任何对象级别的权限检查失败, 此调用将引发适当的APIException , 否则将简单地返回

Copyright © itrunner.cn 2020 all right reserved,powered by Gitbook该文章修订时间: 2022-08-28 07:44:16

results matching ""

    No results matching ""