قرار دادن JWT token در قسمت Cookie در جنگو

شاید برای شماهم پیش اومده باشه برای ارتباط back با front و حفظ امنیت و جلوگیری از حملات XSS بخواین توکن احراز هویت jwt رو توی کوکی و بعنوان HttpOnly cookie نگهداری کنین، خب برای این‌کار راه‌های زیادی هست و من توی این نوشته سعی کردم براتون مسیری رو که رفتم توضیح بدم (خودم هم ازین منبع استفاده کردم).شما میتونین کدهای این پروژه رو اینجا ببینین.اول از همه ایجاد یه venv و شروع یه پروژه جنگویی:virtualenv .venv --python=python3source .venv/bin/activatepip install djangodjango-admin startproject projmv proj JwtInCookiecd JwtInCookiepython manage.py startapp jwt_cookieخب حالا وقتشه یه سری نیازمندی رو نصب کنیم:pip install djangorestframeworkpip install django-cors-headerspip install djangorestframework-simplejwtوقتی جنگو یه پروژه رو ایجاد میکنه توی مسیر اصلی برنامه که اینجا proj هست یه فایل به اسم settings.py میسازه که لازمه کمی ویرایشش کنیم و موارد زیر رو بهش اضافه کنیم:INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'jwt_cookie.apps.JwtCookieConfig', 'rest_framework', 'corsheaders', ] MIDDLEWARE = [ ... 'corsheaders.middleware.CorsMiddleware', ... ] CORS_ALLOW_CREDENTIALS = True REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'jwt_cookie.authenticate.JwtInCookieAuthentication', ), } SIMPLE_JWT = { 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=2), 'REFRESH_TOKEN_LIFETIME': timedelta(minutes=10), 'AUTH_COOKIE': 'access_token', # Cookie name. Enables cookies if value is set. 'AUTH_COOKIE_DOMAIN': None, # A string like &quotexample.com&quot, 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. }الآن اگه برنامه‌رو serve کنید احتمالا خطایی مبنی‌بر پیدا نشد JwtInCookieAuthentication میگیرید که مهم نیست چون در ادامه این کلاس رو اضافه می‌کنیم به برنامه. حالا طبق روال همیشه برای ساختن جداول ابتدایی برنامه جنگویی نیازه که مایگریت انجام بدیم و دستور زیر رو وارد می‌کنیم و یک کابر مدیر میسازیم:python manage.py migrate python manage.py createsuperuserحالا که همه‌چیز آمادهٔ توسعه شده میریم تا apiview مورد نظرمون رو بنویسیم و کم‌کم به برنامه شکل بدیم. برای این کار فایل views.py توی اپ jwt_cookie رو به‌صورت زیر ویرایش می‌کنیم:from rest_framework_simplejwt.tokens import RefreshToken from django.middleware import csrf from rest_framework.views import APIView from rest_framework.response import Response from django.contrib.auth import authenticate from django.conf import settings from rest_framework import status 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[&quotaccess&quot], 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) response.data = {&quotSuccess&quot: &quotLogin successfully&quot, &quotdata&quot: data} return response else: return Response({&quotNo active&quot: &quotThis account is not active!!&quot}, status=status.HTTP_404_NOT_FOUND) else: return Response({&quotInvalid&quot: &quotInvalid username or password!!&quot}, status=status.HTTP_404_NOT_FOUND)خب همونطور که توی کد بالا میبینین ما یه کلاس LoginView داریم که براش یه متد post نوشتیم، توی این متد اول از داخل request نام کاربری و رمزعبوری که کاربر برامون ارسال کرده رو میگیریم و بعد میریم سراغ authenticate کردنش که این کلاس رو در ادامه می‌سازیم، اگه user = None باشه یعنی کاربری با مشخصات وارد شده پیدا نشده، اما اگه پیدا شه باید شرط بعدی رو بررسی کنیم که اصلا این کاربر active هست یا نه، دقت کنید بسته به مدل CustomUser شما ممکنه نیاز داشته باشین موارد دیگه‌ای رو هم بررسی کنین، من ساده‌ترین حالت ممکن رو براش در نظر گرفتم، ممکنه شما مثلا یه سری permission رو هم بخواین بررسی کنین که میتونین همینجا اضافه کنین. اینجا ما وقتی مطمئن شدیم که کاربر اجازه login داره متد get_tokes_for_user رو صدا می‌زنیم که میره به کمک RefreshToken از کتابخانه simplejwt توکن‌های لازم رو برای این کاربر پیدا میکنه. حالا کافیه توی response کاربر کوکی رو مطابق با کد بالا ست کنیم تا موارد خواسته شده براش قابل دسترسی باشه، حالا بریم سراغ فایل بعدی به اسم authenticate.py که کنار همون فایل views.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): check = CSRFCheck() check.process_request(request) reason = check.process_view(request, None, (), {}) if reason: raise exceptions.PermissionDenied('CSRF Failed: %s' % reason) class JwtInCookieAuthentication(JWTAuthentication): def authenticate(self, request): header = self.get_header(request) if header is None: access_token = request.COOKIES.get(settings.SIMPLE_JWT['AUTH_COOKIE']) or None else: access_token = self.get_raw_token(header) if access_token is None: return None validated_token = self.get_validated_token(access_token) enforce_csrf(request) return self.get_user(validated_token), validated_tokenما نیاز داریم یک کلاس JwtInCookieAuthentication که از کلاس JWTAuthentication ارث برده بسازیم و توش متد authenticate رو بازنویسی کنیم. همچنین دقت کنید که برای بهبود امنیت از enforce_csrf هم استفاده کنیم تا validation اون فرم رو درنظر گرفته باشیم. خب بخش اصلی کار انجام شده حالا کافیه آدرس api مورد نظر رو به برنامه اضافه کنیم و بریم سراغ تست کردنش، برای این‌کار اول توی urls.py اصلی توی شاخه proj این موارد رو اضافه می‌کنیم:from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('user/', include('jwt_cookie.urls')), ]حالا توی مسیر jwt_cookie یه فایل urls.py می‌سازیم و موارد زیر رو بهش اضافه می‌کنیم (دقت کنید این‌ نام‌گذاری‌ها دلخواهه):from django.urls import path from .views import LoginView app_name = &quotjwt_cookie&quot urlpatterns = [ path('jwt/', LoginView.as_view(), name=&quotlogin&quot), ]حالا میتونیم با postman یا insomnia یه request به برنامه بزنیم و نتیجه رو ببینیم، قبل از همه python manage.py runserver رو فراموش نکنید.نمونه درخواست:و نمونه خروجی:اگر قسمت cookie رو هم بررسی کنید چیزی شبیه به این تصویر میبینید:امیدوارم به‌کارتون اومده باشه، مقاله اصلی رو از اینجا میتونین ببینین.