【问题标题】:Unable to access API despite entering the token尽管输入了令牌,但无法访问 API
【发布时间】:2020-04-14 19:20:38
【问题描述】:

我有一个只能通过传递 JWT 令牌访问的 API。我在 Django 中使用 Simple-JWT 库。此外,我已将此令牌存储在我的 localStorage 中,并通过 Ajax 在我的视图模块中获取它。 我使用 GET 方法创建了一个 API,它应该从模型中检索某些数据。但是,当通过 Python 中的 Requests 模块访问 API URL 时,它仍然说没有提供身份验证凭据。请参阅下面的代码:

models.py:

class Mymodel(models.Model):
    emp= models.ForeignKey(User, on_delete = models.CASCADE)
    entry_time = models.DateTimeField()

views.py:

def get_time(request):
    if (request.method == 'GET'):
        tk = request.GET.get('token') #from AJAX
        headers = {'Authorization': 'JWT {}'.format(tk), 'content-type': "application/json",}
        url = 'http://127.0.0.1:8000/secret_api/'
        response = requests.get(url, headers = headers)
        if response:
            print("success")
            return HttpResponse(response)

class MyAPI(generics.ListAPIView):
    permission_classes = [IsAuthenticated] 
    def get(self, request, format = None):
        user_id = User.objects.get(username=request.GET.get('username')).pk
        user = User.objects.get(id = user_id)
        if not Mymodel.objects.filter(emp=user).exists():
            current_time = timezone.now()
            time = Mymodel.objects.create(emp=user, entry_time=current_time)
            data = time.entry_time
        return data #returns time 

AJAX 调用:

 $.ajax({
     cache: false,  
     url: {% url 'get_time' %}, #points to the get_time function
     type: 'GET',
     data: {username: username, token: token},
     success: function(data) {
         $("#time").html(data);
     }

但是,在执行 get_time 函数时,我收到此错误:

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

当我从以下位置更改标题时:

headers = {'Authorization': 'Bearer {}"'.format(tk)}

我收到此错误:

{"detail":"Given token not valid for any token type","code":"token_not_valid","messages":[{"token_class":"AccessToken","token_type":"access","message":"Token is invalid or expired"}]}

我 100% 肯定从 AJAX 传递的令牌是正确的。我哪里错了?

【问题讨论】:

  • 你有一个双引号字符被附加到标题{}",这应该在那里吗?
  • 不,不是。可能是我在解决问题时打错了,对不起。
  • 删除它可以解决问题吗?
  • 我现在收到此错误:ValueError:视图 myapp.views.get_time 未返回 HttpResponse 对象。它返回 None 。我应该收到回一个时间。但我什么也没收到。
  • 尝试在调用 API 后添加response.raise_for_status() 以查看错误

标签: django django-rest-framework jwt


【解决方案1】:

解决方案: 1. 您的令牌已过期。我建议您自己访问 Simple-JWT 模型,而不是创建自己的模型,因为您没有过期机制。 2. DRF 专为非网络浏览器解决方案而设计,在我看来。 将整个 get_time 视图更改为通用视图集。

快速说明:我强烈建议您不要将 JWT(访问/刷新)机制用于网站,仅用于移动应用程序。


只是一个意见(我建议您首先尝试解决方案 1):

我对您的运行方式感到非常困惑。 Django REST 框架专为访问您的 Web 服务器的非 Web 浏览器而设计。如果我没记错的话,AJAX 调用似乎更适合在网页中使用。

使用 JSON 网络令牌是为移动应用程序设计的。当您进行 AJAX 调用时,您还发送了用户的会话 ID,因此您可能只是使用 request.user。 (如果有的话,您应该访问 Simple-JWT .token 方法(不是模型),而不是自己存储令牌,因为这些令牌也会在过期后被处理掉。)

所以你的 get_time 视图应该是某种通用视图集。


编辑1:我认为您对MyAPI 的错误是它应该是authentication_classes = [IsAuthenticated],而不是permission_classes。确保您还在 DRF 设置中添加了 simplejwt。

当我开始学习 Django 时,我正在开发一个需要大量抽象第三方包的 Web 应用程序。我建议你了解一点 Python 和一点 Django,这样你就可以阅读这些包的源代码并理解它们。

对于 simplejwt,我忘记了令牌存储在缓存中是为了提高效率,而不是存储在数据库中。如果您在此处查看源代码:https://github.com/davesque/django-rest-framework-simplejwt/blob/master/rest_framework_simplejwt/authentication.py,您将了解身份验证的工作原理,然后如果您转到此处:https://github.com/davesque/django-rest-framework-simplejwt/blob/master/rest_framework_simplejwt/serializers.py,您将了解如何手动验证访问令牌(我强烈不推荐,因为如果您查看第一个链接,那就太复杂了)。

通常,您不必研究源代码,但这是我的建议。例如,要理解为什么我必须使用函数retrieve,是因为DRF works

如果您正在开发一个移动应用程序或只是一个简单的 API 端点,那么您可以制作一个类似于您的 MyAPI ListAPIView 的视图集。

views.py

class get_time(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    authentication_classes = [IsAuthenticated]
    def retrieve(request):  # DRF has default functions that you simply need to override.
        # we know that the user is authenticated already
        current_time = timezone.now()
        if not Mymodel.objects.filter(emp=request.user).exists():
            Mymodel.objects.create(emp=request.user, entry_time=current_time)  # Note, I can't remember if it's request.user or request.user.pk (or request.user.id, same difference there) since I haven't developed with Django in awhile
        return current_time

我认为您的 AJAX 调用没有任何问题,但我不建议您使用 Django-rest-framework(和 drf-simple-jwt),因为您似乎正在使用 Django rest 框架一般的网络浏览器使用,而不仅仅是一个 API 端点。您只需使用if request.user.is_authenticated 来检查用户是否通过普通的Django 视图进行了身份验证。然后记住,它是 authentivation_classes。不是permission_classes,如源代码中的自述文件所述的DRF设置中所述。

再次,我只建议使用 DRF 提供的 API 端点来使用移动应用程序,而不是一般用途的网络浏览。

【讨论】:

  • 您能否演示一下如何将通用视图集与我的 get_time 函数一起使用?我是 Django 新手,可以使用您的建议。非常感谢。
  • @danny_boy 我想我想先了解一下上下文。您正在开发 iOS/Android 应用程序吗?对我来说,它看起来不像,所以如果你只是在开发一个网站,你应该完全从头开始 JWT 和 Django-rest-framework。如果您只是开发一个网站,使用 Django 的会话身份验证说 if request.is_authenticated == true== true 不是必需的)就足够了(它也更安全)。令牌实际上存储在缓存中,而不是模型中,我的错。尽管如此,如果只做网站,请不要使用 JWT 和 DRF。
  • @danny_boy 如果您正在开发移动应用程序,或者我想只是一个 API 端点,那么请查看编辑。
  • 知道了,感谢您花时间解释。我正在做一个网站,所以我会完全避免使用 DRF 和 JWT。
  • @danny_boy 我觉得我在实际使用什么方面把你抛在了后面。尝试使用 django-channels 或最新的 django 3.0 进行异步加载。最好这样做,而不是在 api 端点上设置全新的身份验证系统和新的蓝色安全措施。
【解决方案2】:

您是否检查了您的 settings.py。确保输入正确的令牌到期时间。

在你settings.py

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': datetime.timedelta(days=15),
    'REFRESH_TOKEN_LIFETIME': datetime.timedelta(days=15),
}

请确保您点击python manage.py migrate 并在更改后重新启动 django 服务器。

【讨论】:

  • 感谢我的解决方案是增加 access_token_lifetime,我也注销并登录它才能工作,
猜你喜欢
  • 2020-03-29
  • 2019-12-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-13
  • 2011-02-07
  • 2016-02-09
相关资源
最近更新 更多