Django Rest Framework - 使用会话和令牌身份验证

9
我试图让这个工作起来,但不确定是否可能。应该像这样做。
我使用 Django + Rest-Framework + jQuery 开发了一个 Web 应用程序,并希望有一个外部应用程序来消耗同样的 REST API,使用 JWT Tokens 进行身份验证。
我的当前配置如下。
settings.py
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ],
    'DEFAULT_RENDERER_CLASS': [
        'rest_framework.renderers.JSONRenderer',
    ]
}

SIMPLE_JWT = {
    'AUTH_HEADER_TYPES': ('Bearer',),
    }

views.py

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

    def get(self, request, *args, **kwargs):
        user = request.user

        if user:

            obj = Foo.objects.filter(owner=user)
            serializer = FooSerializer(obj, many=True)
            data = serializer.data

            return Response(data, status=status.HTTP_200_OK)

        return Response({'detail': 'You have no access to files.'}, status=status.HTTP_400_BAD_REQUEST)

棘手的部分是当我使用以下语句时:

permission_classes = (IsAuthenticated,)

我可以使用有效的 JWT 令牌从外部应用程序执行 ajax 调用,但在应用程序内(已认证用户)使用 jQuery 调用时会失败并出现以下错误:

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

另一方面,如果我使用autentication_classes而不是permission_classes

authentication_classes = (SessionAuthentication, BasicAuthentication)

我可以使用jQuery在Web应用程序中执行ajax调用,但是外部调用会出现相同的403错误。
我尝试使用以下两种方式:
class ListFileView(APIView):
    authentication_classes = (SessionAuthentication, BasicAuthentication)
    permission_classes = (IsAuthenticated,)

    def get(self, request, *args, **kwargs):
        ...

但是外部调用也被拒绝了。

这两种Auth是否可以在同一个class视图中一起使用,还是应该将其分为两个端点?

编辑

来自应用程序内部使用jQuery的示例调用:

<script type="text/javascript">

function loadTblDocs() {
  $("#tbl-docs > tbody").empty();

  $.ajaxSetup({
      headers: { "X-CSRFToken": '{{csrf_token}}' }
    });

  $.ajax({
    type: 'GET',
    contentType: "application/json; charset=utf-8",
    url: "/api/list/",
    dataType: "json",
    success: function (data) {
                console.log(data);
                }
    });
};

</script>

同时,也可以通过VBA代码的方式来进行外部操作:

Set web = CreateObject("WinHttp.WinHttpRequest.5.1")
web.Open "GET", "/api/list/", False
web.SetRequestHeader "Authorization", "Bearer <JWT_TOKEN_HERE>"
web.Send

你能展示一下你是如何调用API的吗? - JPG
使用jQuery内部还是外部的@JPG? - drec4s
是的,我正在使用标准的Django登录。 - drec4s
请澄清一下,您的初始情况是(在settings.py中定义了身份验证类,视图中定义了权限类),外部请求成功而内部ajax请求失败;在第二种情况下(在视图中定义身份验证类,未在视图中定义权限类),外部请求失败而内部ajax请求成功;最后一种情况是(在视图中定义身份验证类和权限类),外部请求失败而内部ajax请求成功。这样正确吗? - Ozgur Akcali
@OzgurAkcali 是的,没错。 - drec4s
显示剩余2条评论
1个回答

13

我无法准确理解您的情况,因为您分享的 3 种行为似乎不一致。但是,我将简要解释 DRF 中如何确定身份验证和授权,这可能有助于您解决问题。

在 DRF 中,您应该可以使用两个身份验证类而没有问题。身份验证的工作方式如下:

每次请求时,DRF 按照定义的顺序遍历提供的身份验证类。对于每个类,有以下 3 种情况:

  1. 如果当前类可以验证请求,则 DRF 设置 request.user。从此时起,此请求已经得到了身份验证。
  2. 如果没有提供身份验证凭据,则 DRF 跳过该类。
  3. 如果提供了身份验证凭据但无效(比如在 Authorization 头中提供了无效的 JWT 令牌),则 DRF 会引发异常并返回 403 响应。

DRF 视图通常使用设置文件中定义的 DEFAULT_AUTHENTICATION_CLASSES 变量,但是如果您在视图中提供了它们,则会覆盖设置。

授权在您向视图添加 permission_classes 时发挥作用。授权控制您视图的访问权限,因此当您将 IsAuthenticated 添加到视图的权限类中时,如果您尝试在未经身份验证的情况下访问该视图,则 DRF 将拒绝该请求并返回 403 响应。

在您最初的情况下,内部AJAX请求失败表明您的请求会话中没有经过身份验证的用户数据。我不知道是什么原因导致了这种情况。也许您在登录视图中没有更新请求会话(Django的登录方法应自动执行此操作)。

在您的第二种情况下,您从视图中删除了permission_classes,因此该视图将为请求提供服务,无论请求中是否有已验证的用户。因此,在此处预计您的内部AJAX请求成功,但我不知道为什么您的外部请求在此情况下失败。

在您的第三种情况下,从您的内部AJAX请求的角度来看,情景似乎与第一种情况相同,因此我不知道为什么在此情况下您的内部AJAX请求成功,但在第一种情况下却不成功。在此处添加了IsAuthenticated权限类别到视图中是可以预期的,但是您在authentication_classes中并没有包括JWTAuthentication,因此您的请求不能通过JWT令牌进行身份验证。


1
你说得对,非常感谢你提供的详细解释。事实证明,在生产环境中我实际上使用了错误的settings.py文件,其中缺少DRF DEFAULT_AUTHENTICATION_CLASSES中的rest_framework.authentication.SessionAuthentication这一行,现在已经正常工作了。 - drec4s

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接