Do not require authentication for OPTIONS requests

2019-07-09 07:28发布

问题:

My settings.py

REST_FRAMEWORK = {
    'UNICODE_JSON': True,
    'NON_FIELD_ERRORS_KEY': '__all__',
    'DEFAULT_AUTHENTICATION_CLASSES': (
        # TODO(dmu) HIGH: Support OAuth or alike authentication
        'rest_framework.authentication.TokenAuthentication',
    ),
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    ),
    'DEFAULT_RENDERER_CLASSES': (
        'rest_framework.renderers.JSONRenderer',
    ),
    'ALLOWED_VERSIONS': ['v1'],
    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.NamespaceVersioning',
    'TEST_REQUEST_DEFAULT_FORMAT': 'json',
    'TEST_REQUEST_RENDERER_CLASSES': (
        'rest_framework.renderers.JSONRenderer',
    )
}

When I do this I get authentication error:

curl -X OPTIONS http://127.0.0.1:8000/api/passenger/v1/order/ | python -m json.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    58    0    58    0     0    469      0 --:--:-- --:--:-- --:--:--   475
{
    "detail": "Authentication credentials were not provided."
}

I'd like my server respond with "schema" description and not required authentication. At the same time I want it to require authentication for GET, POST, PUT, PATCH and DELETE requests as usual.

How can I achieve that?

MY SOLUTION

Thank you, Alasdair, for the idea. I personally used this solution:

from rest_framework.permissions import DjangoObjectPermissions

OPTIONS_METHOD = 'OPTIONS'

class DjangoObjectPermissionsOrOptions(DjangoObjectPermissions):
    def has_permission(self, request, view):
        if request.method == OPTIONS_METHOD:
            return True
        else:
            return super(DjangoObjectPermissions, self).has_permission(request, view)

回答1:

Django rest framework comes with a permissions class IsAuthenticatedOrReadOnly, which allows authenticated users to perform any request, and unauthorised users to make GET, HEAD or OPTIONS requests.

Your use case is pretty similar, so you could try the following (untested):

class IsAuthenticatedOrOptions(BasePermission):
    """
    The request is authenticated as a user, or an OPTIONS request.
    """

    def has_permission(self, request, view):
        return (
            request.method == 'OPTIONS' or
            request.user and
            request.user.is_authenticated()
        )

'DEFAULT_PERMISSION_CLASSES': (
    'path.to.IsAuthenticatedOrOptions',
),