使用HttpOnly Cookie的django-rest-framework

时间:2019-06-13 19:51:30

标签: django-rest-framework jwt

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

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

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

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

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

  

如果除了Authorization标头之外,还想使用http cookie作为令牌的有效传输,则可以设置JWT_AUTH_COOKIE一个字符串。您在此处设置的字符串将用作在请求令牌时将在响应标头中设置的cookie名称。令牌验证过程还将调查此cookie(如果已设置)。如果请求中同时包含标头和cookie,则“授权”标头具有优先权。

     

默认值为“无”,创建令牌时不设置cookie,在验证令牌时不接受。

在将字符串值赋予JWT_AUTH_COOKIE之后,我收到了预期的httpOnly cookie。

问题:

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

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

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

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

如果从现在起我对客户端对服务器的每个请求都没错,则应该将cookie添加到该请求中。

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

3 个答案:

答案 0 :(得分:3)

您观察到的问题是正确的,因为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

Refresh working with cookies

答案 1 :(得分: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框架JWT的令牌刷新端点现在可以正常工作,因为它 可以在其中找到“令牌”键/值。

几件事要注意:

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

答案 2 :(得分:-1)

当调用getToken时,它应该返回另一个名为refreshToken的变量(发送该变量以刷新会话时间),这是您必须在以后发送的变量以刷新它,因为变量令牌已过期。我为英语道歉,但我会说西班牙语。如果您保存在cokkie中,那么您要做的就是刷新安全站点的令牌将是不安全的。