Jal*_*lal 10 django django-rest-framework django-rest-framework-jwt django-rest-framework-simplejwt
我已经使用了djangorestframework-simplejwt一段时间,现在我想将 JWT 存储在 cookie 中(而不是本地存储或前端状态),以便客户端发出的每个请求都包含令牌。
因此,我对此进行了一些研究,我发现的最相关的结果是这个 stackoverflow 问题,其中作者使用的djangorestframework-jwt包有一个名为 cookie 的预配置设置JWT_AUTH_COOKIE。因此想切换到该软件包,但最终发现该软件包几乎已经死了。
虽然有一个建议使用的分叉djangorestframework-jwt,但我想知道是否可以用其djagnorestframework_simplejwt本身来设置 HttpOnly cookie 中的 JWT?
Pra*_*dip 15
使用 httponly cookie 标志和 CSRF 保护,请遵循此代码。
双方在移动应用程序和网络应用程序中都非常有用。
网址.py:
...
path('login/',LoginView.as_view(),name = "login"),
...
Run Code Online (Sandbox Code Playgroud)
视图.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)
Run Code Online (Sandbox Code Playgroud)
验证.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
Run Code Online (Sandbox Code Playgroud)
设置.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.
}
Run Code Online (Sandbox Code Playgroud)
通过使用 middleware.py:
必须 :
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, request, 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, response, *args, **kwargs)
class CookieTokenRefreshView(TokenRefreshView):
def finalize_response(self, request, 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, response, *args, **kwargs)
serializer_class = CookieTokenRefreshSerializer
Run Code Online (Sandbox Code Playgroud)
更改 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'),
# [...]
]
Run Code Online (Sandbox Code Playgroud)
如果它没有按预期工作,请检查您的 CORS 设置:也许您必须在 set_cookie 中设置 SameSite 和 secure
工作流程 - 使用凭据获取令牌对
工作流程 - 使用刷新令牌获取访问(和可选刷新)令牌
使用之前工作流程中设置的 cookie POST /auth/token/refresh,正文可以为空
在响应正文中,您会注意到仅设置了“访问”键
如果您设置了 ROTATE_REFRESH_TOKENS,则 httpOnly cookie 'refresh_token' 包含新的刷新令牌
参考: https: //github.com/jazzband/djangorestframework-simplejwt/issues/71#issuecomment-762927394
小智 5
我到处找过,这就是我发现的。我将尝试解释从设置 cookie 到检索 cookie 的整个过程。我知道我迟到了,但是和我一样,一定还有其他人在努力寻找答案。如果我做错了什么,请纠正我。
设置 Cookie
在项目配置下的django 文档中,您将找到他们用来TokenObtainPairView.as_view()获取令牌的 。TokenObtainPairView我们将在单独的视图文件中修改,调用它MyTokenObtainPairView并将其导入。请参阅下面的代码。
# urls.py
from django.urls import path
from .views import MyTokenObtainPairView
urlpatterns = [
path("token/", MyTokenObtainPairView.as_view(), name="token_obtain_pair"),
]
Run Code Online (Sandbox Code Playgroud)
# views.py
from rest_framework_simplejwt.views import TokenObtainPairView
class MyTokenObtainPairView(TokenObtainPairView):
def post(self, request, *args, **kwargs):
response = super().post(request, *args, **kwargs)
token = response.data["access"]
response.set_cookie("pick_a_name_you_like_for_the_cookie", token, httponly=True)
return response
Run Code Online (Sandbox Code Playgroud)
您可以在邮递员中测试这一点。假设您从根目录执行此操作并使用 localhost,则端点应该类似于:http://localhost:8000/token/。您应该使用登录凭据(通常是用户名和密码)运行 POST 请求。现在,如果您使用邮递员,您应该看到有一个名为pick_a_name_you_like_for_the_cookie
-> 阅读更多内容(文档) - 这是用于自定义令牌声明的更多内容。
检索令牌
默认情况下,simplejwt 将在标头中查找访问令牌。因此,您将无法通过使用user = request.user或 添加权限permission_classes = [IsAuthenticated]来检索用户@permission_classes([IsAuthenticated])。请参阅文档。
这是因为默认身份验证类别设置为:
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": (
"rest_framework_simplejwt.authentication.JWTAuthentication",
)
}
Run Code Online (Sandbox Code Playgroud)
我们需要复制并修改 JWTAuthentication 类。在根目录中创建一个文件,名称任意。在这种情况下custom_auth.py。复制所有内容rest_framework_simplejwt/authentication.py。进入虚拟环境或者直接进入pip安装的rest_framework_simplejwt的地方就可以找到这个文件。
现在,假设您已复制所有内容,请更改以下代码custom_auth.py:
from .exceptions import AuthenticationFailed, InvalidToken, TokenError
from .settings import api_settings
Run Code Online (Sandbox Code Playgroud)
到
from rest_framework_simplejwt.exceptions import AuthenticationFailed, InvalidToken, TokenError
from rest_framework_simplejwt.settings import api_settings
Run Code Online (Sandbox Code Playgroud)
现在,在调用的类中,JWTAuthentication您需要将身份验证函数更改为:
class JWTAuthentication(authentication.BaseAuthentication):
"""
An authentication plugin that authenticates requests through a JSON web
token provided in a request header.
"""
www_authenticate_realm = "api"
media_type = "application/json"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.user_model = get_user_model()
def authenticate(self, request):
cookie = request.COOKIES.get("pick_a_name_you_like_for_the_cookie")
raw_token = cookie.encode(HTTP_HEADER_ENCODING)
validated_token = self.get_validated_token(raw_token)
return self.get_user(validated_token), validated_token
...
Run Code Online (Sandbox Code Playgroud)
您应该添加一些验证等,以了解如果没有 cookie 等应该做什么。可能类似于:
if cookie is None:
return None
Run Code Online (Sandbox Code Playgroud)
根据需要自定义错误处理。最后回到settings.py并替换:
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": (
"rest_framework_simplejwt.authentication.JWTAuthentication",
)
}
Run Code Online (Sandbox Code Playgroud)
和:
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": (
"custom_auth.JWTAuthentication",
)
}
Run Code Online (Sandbox Code Playgroud)
如果我留下任何漏洞或者我是否可以做一些不同的事情,请告诉我!:)
| 归档时间: |
|
| 查看次数: |
13685 次 |
| 最近记录: |