微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何使用 DRF djangorestframework-simplejwt 包将 JWT 令牌存储在 HttpOnly cookie 中?

如何解决如何使用 DRF djangorestframework-simplejwt 包将 JWT 令牌存储在 HttpOnly cookie 中?

我已经使用 djangorestframework-simplejwt 一段时间了,现在我想将 JWT 存储在 cookie(而不是本地存储或前端状态)中,以便客户端发出的每个请求都包含令牌。

因此对其进行了一些研究,我发现最相关的结果是 this stackoverflow question,其中作者使用的是 djangorestframework-jwt 包,该包具有名为 JWT_AUTH_COOKIE 的预配置 cookie 设置.所以想切换到那个包,但最终发现了 the package is pretty much dead

尽管推荐使用 djangorestframework-jwtfork,但我想知道是否可以使用 djagnorestframework_simplejwt 本身在 HttpOnly cookie 中设置 JWT?

>

解决方法

使用 httponly cookie 标志和 CSRF 保护遵循此代码。

在移动应用和网络应用中都非常有用..

urls.py:

...
path('login/',LoginView.as_view(),name = "login"),...

view.py:

from rest_framework_simplejwt.tokens import RefreshToken
from django.middleware import csrf

def get_tokens_for_user(user):
    refresh = RefreshToken.for_user(user)
        
    return {
        'refresh': str(refresh),'access': str(refresh.access_token),}

class LoginView(APIView):
    def post(self,request,format=None):
        data = request.data
        response = Response()        
        username = data.get('username',None)
        password = data.get('password',None)
        user = authenticate(username=username,password=password)
        if user is not None:
            if user.is_active:
                data = get_tokens_for_user(user)
                response.set_cookie(
                                    key = settings.SIMPLE_JWT['AUTH_COOKIE'],value = data["access"],expires = settings.SIMPLE_JWT['ACCESS_TOKEN_LIFETIME'],secure = settings.SIMPLE_JWT['AUTH_COOKIE_SECURE'],httponly = settings.SIMPLE_JWT['AUTH_COOKIE_HTTP_ONLY'],samesite = settings.SIMPLE_JWT['AUTH_COOKIE_SAMESITE']
                                        )
                csrf.get_token(request)
                email_template = render_to_string('login_success.html',{"username":user.username})    
                login = EmailMultiAlternatives(
                    "Successfully Login","Successfully Login",settings.EMAIL_HOST_USER,[user.email],)
                login.attach_alternative(email_template,'text/html')
                login.send()
                response.data = {"Success" : "Login successfully","data":data}
                
                return response
            else:
                return Response({"No active" : "This account is not active!!"},status=status.HTTP_404_NOT_FOUND)
        else:
            return Response({"Invalid" : "Invalid username or password!!"},status=status.HTTP_404_NOT_FOUND)

authenticate.py:

from rest_framework_simplejwt.authentication import JWTAuthentication
from django.conf import settings

from rest_framework.authentication import CSRFCheck
from rest_framework import exceptions

def enforce_csrf(request):
    """
    Enforce CSRF validation.
    """
    check = CSRFCheck()
    # populates request.META['CSRF_COOKIE'],which is used in process_view()
    check.process_request(request)
    reason = check.process_view(request,None,(),{})
    if reason:
        # CSRF failed,bail with explicit error message
        raise exceptions.PermissionDenied('CSRF Failed: %s' % reason)

class CustomAuthentication(JWTAuthentication):
    
    def authenticate(self,request):
        header = self.get_header(request)
        
        if header is None:
            raw_token = request.COOKIES.get(settings.SIMPLE_JWT['AUTH_COOKIE']) or None
        else:
            raw_token = self.get_raw_token(header)
        if raw_token is None:
            return None

        validated_token = self.get_validated_token(raw_token)
        enforce_csrf(request)
        return self.get_user(validated_token),validated_token

settings.py:

....
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'authentication.authenticate.CustomAuthentication',),}

SIMPLE_JWT = {
.....
'AUTH_COOKIE': 'access_token',# Cookie name. Enables cookies if value is set.
'AUTH_COOKIE_DOMAIN': None,# A string like "example.com",or None for standard domain cookie.
'AUTH_COOKIE_SECURE': False,# Whether the auth cookies should be secure (https:// only).
'AUTH_COOKIE_HTTP_ONLY' : True,# Http only cookie flag.It's not fetch by javascript.
'AUTH_COOKIE_PATH': '/',# The path of the auth cookie.
'AUTH_COOKIE_SAMESITE': 'Lax',# Whether to set the flag restricting cookie leaks on cross-site requests.
                                # This can be 'Lax','Strict',or None to disable the flag.
}

--------- 或 ------------

通过使用 middleware.py:

How to authenticate by using middleware

必须:

withCredentials 对双方都是 True..

有任何疑问请评论..

,

您可以执行以下操作以在 httpOnly cookie 中存储刷新令牌:

将此添加到 views.py:

# views.py
from rest_framework_simplejwt.views import TokenRefreshView,TokenObtainPairView
from rest_framework_simplejwt.serializers import TokenRefreshSerializer
from rest_framework_simplejwt.exceptions import InvalidToken

class CookieTokenRefreshSerializer(TokenRefreshSerializer):
    refresh = None
    def validate(self,attrs):
        attrs['refresh'] = self.context['request'].COOKIES.get('refresh_token')
        if attrs['refresh']:
            return super().validate(attrs)
        else:
            raise InvalidToken('No valid token found in cookie \'refresh_token\'')

class CookieTokenObtainPairView(TokenObtainPairView):
  def finalize_response(self,response,*args,**kwargs):
    if response.data.get('refresh'):
        cookie_max_age = 3600 * 24 * 14 # 14 days
        response.set_cookie('refresh_token',response.data['refresh'],max_age=cookie_max_age,httponly=True )
        del response.data['refresh']
    return super().finalize_response(request,**kwargs)

class CookieTokenRefreshView(TokenRefreshView):
    def finalize_response(self,**kwargs):
        if response.data.get('refresh'):
            cookie_max_age = 3600 * 24 * 14 # 14 days
            response.set_cookie('refresh_token',httponly=True )
            del response.data['refresh']
        return super().finalize_response(request,**kwargs)
    serializer_class = CookieTokenRefreshSerializer

更改 url.py 中的 url 以使用这些视图来获取和刷新令牌:

# url.py
from .views import CookieTokenRefreshView,CookieTokenObtainPairView # Import the above views
# [...]
urlpatterns = [
    path('auth/token/',CookieTokenObtainPairView.as_view(),name='token_obtain_pair'),path('auth/token/refresh/',CookieTokenRefreshView.as_view(),name='token_refresh'),# [...]
]

如果没有按预期工作,请检查您的 CORS 设置:也许您必须在 set_cookie 中设置 sameSite 和 secure

工作流程 - 使用凭据获取令牌对

  1. 使用有效凭据POST /auth/token
  2. 在响应正文中,您会注意到仅设置了“访问”键
  3. “refresh”键已移至名为“refresh_token”的 httpOnly cookie

工作流程 - 使用刷新令牌获取访问(和可选的刷新)令牌

  1. POST /auth/token/refresh 使用之前设置的 cookie 工作流,正文可以为空

  2. 在响应正文中,您会注意到仅设置了“访问”键

  3. 如果您设置了 ROTATE_REFRESH_TOKENS,httpOnly cookie 'refresh_token' 包含一个新的刷新令牌

参考:https://github.com/jazzband/djangorestframework-simplejwt/issues/71#issuecomment-762927394

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。