【问题标题】:django-rest-framework using HttpOnly Cookiedjango-rest-framework 使用 HttpOnly Cookie
【发布时间】:2019-10-28 11:20:27
【问题描述】:

在以不安全的方式使用 djangorestframework-jwt 一年多之后,我终于决定让它以更安全的方式工作。

我读到过在本地客户端(例如本地存储)中保存 JWT 令牌不好的地方,最好的解决方案是改用 HttpOnly cookie。

我知道 HttpOnly cookie 确实是一个 cookie,可以保存但不能被浏览器读取。所以我认为它可以像下面这样使用:

  • get_token:客户端通过发送用户名和密码向服务器请求授权令牌:如果用户名和密码有效,则服务器以 httpOnly cookie 响应,客户端可以存储但不能读取该cookie。
  • 客户端从现在开始执行的每个请求都经过授权,因为在 HttpOnly cookie 中有一个有效的授权令牌。
  • refresh_token:一旦客户端需要刷新token,它只需要请求一个refresh_token:如果发送的cookie包含一个有效的token,服务器将响应一个更新的HttpOnly cookie,带有新的token。

我现在正在尝试通过使用 HttpOnly cookie 来使用 djangorestframework-jwt,而 JWT_AUTH_COOKIE 配置似乎是最合适的配置:

如果您想使用 http cookie 以及 Authorization 标头作为令牌的有效传输,您可以将 JWT_AUTH_COOKIE 设置为字符串。您在此处设置的字符串将用作请求令牌时将在响应标头中设置的 cookie 名称。如果已设置,令牌验证过程还将查看此 cookie。如果请求中同时存在标头和 cookie,则“授权”标头优先。

默认为 None,创建令牌时不设置 cookie,验证令牌时也不接受。

在给 JWT_AUTH_COOKIE 一个字符串值之后,我收到了一个 httpOnly cookie,正如预期的那样。

问题:

当我调用 refreshToken 时,我得到以下响应:

{"token":["This field is required."]}

没错,我没有在请求的 HEADER 中发送任何令牌,这就是我想要的,因为客户端不应该将它保存在任何地方。

这就是我感到困惑的地方:

如果我从现在开始对客户端向服务器发出的每个请求都没有错,那么应该将 cookie 添加到请求中。

服务器在看到Header中没有传递令牌后,不应该检查cookie吗?如果不是这样,它应该如何工作?

如果有人想为改进做出贡献,还在这里发布了一个 Github 问题:https://github.com/jpadilla/django-rest-framework-jwt/issues/482

【问题讨论】:

  • 这听起来像是前端问题。如果您使用 axios,您将设置 axios.defaults.withCredentials = true 并在收到 cookie 后,您必须设置标头 axios.defaults.headers.common['Authorization'] = 'JWT <token>'
  • 这有什么更新吗?
  • @Francesco Meli - 这对你有用吗? djangrestframework-jwt 的行为不符合预期。我正在使用TokenAuthentication 作为httponly cookie,但我在处理已撤销的令牌客户端时遇到了麻烦,因为如果您将过期令牌与请求一起发送,Django 会返回 401。
  • @zerohedge 不幸的是我还没有时间测试它。不过,我需要尽快对其进行测试。请,如果你找到一个现实世界的工作解决方案,让我们都知道!
  • 有什么方法可以连接吗?也许我们可以一起寻找解决方案。我已经为此工作了好几天,但无济于事。

标签: django-rest-framework jwt


【解决方案1】:

您观察到的问题是正确的,因为尚未使用 cookie 实现刷新令牌 api。

这可能是代码本身的错误。但是没有什么能限制你解决这个问题。

您也可以修补视图以处理基于 cookie 的身份验证。将下面的代码添加到您的 urls.py 的顶部,它会处理同样的事情

from rest_framework_jwt.settings import api_settings

if api_settings.JWT_AUTH_COOKIE:
    from rest_framework_jwt.authentication import JSONWebTokenAuthentication
    from rest_framework_jwt.serializers import RefreshJSONWebTokenSerializer
    from rest_framework_jwt.views import RefreshJSONWebToken

    RefreshJSONWebTokenSerializer._declared_fields.pop('token')

    class RefreshJSONWebTokenSerializerCookieBased(RefreshJSONWebTokenSerializer):
        def validate(self, attrs):
            if 'token' not in attrs:
                if api_settings.JWT_AUTH_COOKIE:
                    attrs['token'] = JSONWebTokenAuthentication().get_jwt_value(self.context['request'])
            return super(RefreshJSONWebTokenSerializerCookieBased, self).validate(attrs)

    RefreshJSONWebToken.serializer_class = RefreshJSONWebTokenSerializerCookieBased

【讨论】:

  • 把那个repo的钥匙给这个人............
【解决方案2】:

我已将此中间件添加到我的 Django (3.1) 中:

class YankTokenRefreshFromHeaderIntoTheBody(MiddlewareMixin):
    """
    for Django Rest Framework JWT's POST "/token-refresh" endpoint --- check for a 'token' in the request.COOKIES
    and if, add it to the body payload.
    """

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        return response

    def process_view(self, request, view_func, *view_args, **view_kwargs):
        if request.path == '/v1/token-refresh' and 'token' in request.COOKIES:
            data = json.loads(request.body)
            data['token'] = request.COOKIES['token']
            request._body = json.dumps(data).encode('utf-8')
        return None

然后我将它添加到我的settings

MIDDLEWARE = [
    'myproj.utils.middleware.YankTokenRefreshFromHeaderIntoTheBody',
    ...
    ...
]

就是这样。 Django REST framework JWT 的令牌刷新端点现在可以工作了,因为它在那里找到“令牌”键/值。

注意事项:

  1. 我选择'token'作为持有tte JWT令牌的cookie的名称。当然,您的情况可能会有所不同。
  2. 我将端点名称更改为 /v1/token-refresh -- 如果您使用的是原始命名端点,您也需要更改它。

【讨论】:

    猜你喜欢
    • 2012-06-07
    • 2012-11-27
    • 2017-12-06
    • 2018-03-03
    • 2017-06-27
    • 2019-12-19
    • 1970-01-01
    • 1970-01-01
    • 2017-05-25
    相关资源
    最近更新 更多