{"id":445136,"date":"2025-01-15T21:00:09","date_gmt":"2025-01-15T21:00:09","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=445136"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=445136","title":{"rendered":"<span>\u041f\u0435\u0440\u0435\u043d\u043e\u0441 JWT-\u0442\u043e\u043a\u0435\u043d\u043e\u0432 \u0432 \u043a\u0443\u043a\u0438: Django REST + React<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u0421\u0442\u0430\u0442\u044c\u044f \u0432 \u043f\u0435\u0440\u0432\u0443\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u0431\u044b\u043b\u0430 \u043f\u0440\u0435\u0436\u0434\u0435 \u0432\u0441\u0435\u0433\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0430 \u0434\u043b\u044f \u0441\u0430\u043c\u043e\u0433\u043e \u0441\u0435\u0431\u044f \u0441 \u0446\u0435\u043b\u044c\u044e \u0437\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u043d\u0438\u044f \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0433\u043e \u043e\u043f\u044b\u0442\u0430 \u043f\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0445 \u043a\u043e\u0441\u0442\u044b\u043b\u0435\u0439 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e JWT-\u0442\u043e\u043a\u0435\u043d\u043e\u0432, \u043d\u0430\u0445\u043e\u0434\u044f\u0449\u0438\u0445\u0441\u044f \u0432 \u043a\u0443\u043a\u0438.<\/p>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0431\u0435\u043a\u0435\u043d\u0434\u0430 \u0431\u044b\u043b \u0432\u044b\u0431\u0440\u0430\u043d \u0433\u043e\u0440\u044f\u0447\u043e \u043b\u044e\u0431\u0438\u043c\u044b\u0439 Django Rest Framework, \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0444\u0440\u043e\u043d\u0442\u043e\u0432\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u0432 \u043c\u043e\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0441\u044f React. \u041d\u0430\u0447\u043d\u0443 \u0441 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u043e\u0439 \u0441\u0442\u043e\u0440\u043e\u043d\u044b. \u042f \u043f\u0440\u043e\u043f\u0443\u0449\u0443 \u0448\u0430\u0433\u0438 \u043f\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 Django REST Framework \u0432 \u0441\u0432\u044f\u0437\u043a\u0435 \u0441 React. \u0412 Django \u0432 \u043c\u043e\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0431\u044b\u043b\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 <em>user.<\/em><\/p>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0431\u0430\u0437\u044b JWT-\u0442\u043e\u043a\u0435\u043d\u043e\u0432 \u0432\u0437\u044f\u043b \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 <a href=\"https:\/\/django-rest-framework-simplejwt.readthedocs.io\/en\/latest\/\" rel=\"noopener noreferrer nofollow\">Simple JWT<\/a>. <\/p>\n<p>\u041c\u043e\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438:<\/p>\n<pre><code class=\"python\">SIMPLE_JWT = {     'ROTATE_REFRESH_TOKENS': True,  # \u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 refresh \u0442\u043e\u043a\u0435\u043d\u0430 \u043f\u0440\u0438 \u0437\u0430\u043c\u0435\u043d\u0435 access \u0442\u043e\u043a\u0435\u043d\u0430     'BLACKLIST_AFTER_ROTATION': True,      'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),      'REFRESH_TOKEN_LIFETIME': timedelta(days=1),      'REFRESH_COOKIE': 'refresh_token',  # \u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043a\u043b\u044e\u0447\u0430 \u0432 \u043a\u0443\u043a\u0438, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f refresh \u0442\u043e\u043a\u0435\u043d     'AUTH_COOKIE': 'access_token',  # \u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043a\u043b\u044e\u0447\u0430 \u0432 \u043a\u0443\u043a\u0438, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f access \u0442\u043e\u043a\u0435\u043d     'AUTH_COOKIE_SECURE': False,  # \u041a\u0443\u043a\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e HTTPS (True \u0434\u043b\u044f production)     'AUTH_COOKIE_HTTP_ONLY': True,  # \u0417\u0430\u043f\u0440\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u043a\u0443\u043a\u0438 \u0447\u0435\u0440\u0435\u0437 JavaScript     'AUTH_COOKIE_SAMESITE': 'Strict',  # \u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u043a\u0443\u043a\u0438 \u043f\u0440\u0438 \u043a\u0440\u043e\u0441\u0441-\u0441\u0430\u0439\u0442\u043e\u0432\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u0445. }<\/code><\/pre>\n<p>\u041f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0440\u0430\u0437\u043c\u0435\u0442\u0438\u043b \u0441\u0430\u043c\u0438 API \u043f\u0443\u0442\u0438:<\/p>\n<pre><code class=\"python\">from django.urls import path from .views import CookieTokenObtainPairView, CookieTokenRefreshView, get_csrf  urlpatterns = [    path('token\/', CookieTokenObtainPairView.as_view(), name='token_obtain_pair'),    path('token\/refresh\/', CookieTokenRefreshView.as_view(), name='token_refresh'),    path('csrf\/', get_csrf, name='get_csrf'), ]<\/code><\/pre>\n<h2>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043b\u043e\u0433\u0438\u043a\u0438 \u0432\u0445\u043e\u0434\u0430 <\/h2>\n<p>\u0414\u0430\u043b\u0435\u0435 \u043d\u0430\u0447\u043d\u0435\u043c \u0441 \u043a\u043b\u0430\u0441\u0441\u0430 <code>CookieTokenObtainPairView<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u043b\u043e\u0433\u0438\u043a\u0443 \u0432\u0445\u043e\u0434\u0430 \u0438 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044e \u043f\u0435\u0440\u0432\u0438\u0447\u043d\u043e\u0439 \u043f\u0430\u0440\u044b jwt \u0442\u043e\u043a\u0435\u043d\u043e\u0432:<\/p>\n<pre><code class=\"python\">from django.conf import settings from django.http import JsonResponse from django.middleware.csrf import get_token from django.utils.decorators import method_decorator  from rest_framework.response import Response from rest_framework.permissions import IsAuthenticated from rest_framework.request import Request from rest_framework import status from rest_framework.permissions import AllowAny  from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView from rest_framework_simplejwt.authentication import JWTAuthentication from rest_framework_simplejwt.exceptions import InvalidToken, TokenError  from .utils import set_jwt_cookies, enforce_csrf from .serializer import CookieTokenObtainPairSerializer  class CookieTokenObtainPairView(TokenObtainPairView):      \"\"\"     \u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f JWT-\u0442\u043e\u043a\u0435\u043d\u043e\u0432 (access \u0438 refresh) \u0438 \u0438\u0445 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0432 \u043a\u0443\u043a\u0438.      \u042d\u0442\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0435\u0442 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 `TokenObtainPairView` \u0438\u0437 Django REST Framework Simple JWT.     \u041f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 access \u0438 refresh \u0442\u043e\u043a\u0435\u043d\u044b \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u044e\u0442\u0441\u044f \u0432 HTTP-only \u043a\u0443\u043a\u0438 \u0438 \u0443\u0434\u0430\u043b\u044f\u044e\u0442\u0441\u044f     \u0438\u0437 \u0442\u0435\u043b\u0430 \u043e\u0442\u0432\u0435\u0442\u0430.      \u041f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u0435:     - \u0414\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043a\u0443\u043a\u0430\u043c\u0438 \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c CORS \u0441 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u043e\u0439 credentials.     \"\"\"      serializer_class = CookieTokenObtainPairSerializer     authentication_classes = ()     permission_classes = (AllowAny,)      @method_decorator(enforce_csrf)     def post(self, request: Request, *args, **kwargs) -&gt; Response:         response = super().post(request, *args, **kwargs)          if response.status_code == 200:             access_token = response.data.get('access')             refresh_token = response.data.get('refresh')              if access_token and refresh_token:                 response = set_jwt_cookies(response, access_token, refresh_token)                                  del response.data['access']                 del response.data['refresh']          return response    <\/code><\/pre>\n<p>\u041f\u0440\u043e\u0431\u0435\u0433\u0443\u0441\u044c \u043f\u043e \u043a\u043e\u0434\u0443:<\/p>\n<p><code>CookieTokenObtainPairSerializer<\/code> &#8212; \u043d\u0430\u043f\u0438\u0441\u0430\u043b \u0441\u0432\u043e\u0439 \u0441\u0435\u0440\u0438\u0430\u043b\u0430\u0439\u0437\u0435\u0440, \u0442\u0430\u043a \u043a\u0430\u043a \u043b\u0438\u0447\u043d\u043e \u043c\u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u043f\u043e\u043c\u0438\u043c\u043e \u0442\u043e\u043a\u0435\u043d\u043e\u0432 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043b\u0441\u044f \u0438 \u0441\u0442\u0430\u0442\u0443\u0441-\u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0443<\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from rest_framework_simplejwt.serializers import TokenObtainPairSerializer  class CookieTokenObtainPairSerializer(TokenObtainPairSerializer):          def validate(self, attrs):         data = super().validate(attrs)         data['user'] = str(self.user)         data['user_status'] = \"active\"         return data<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0422\u0430\u043a\u0436\u0435 \u0440\u0435\u0448\u0438\u043b \u043f\u0435\u0440\u0435\u0441\u0442\u0440\u0430\u0445\u043e\u0432\u0430\u0442\u044c \u0441\u0432\u043e\u0438 \u0444\u043e\u0431\u0438\u0438 \u0431\u044b\u0442\u044c \u0432\u0437\u043b\u043e\u043c\u0430\u043d\u043d\u044b\u043c \u043f\u0435\u0440\u0443\u0430\u043d\u0441\u043a\u0438\u043c\u0438 \u0445\u0430\u043a\u0435\u0440\u0430\u043c\u0438 \u0438 \u0432\u043d\u0435\u0434\u0440\u0438\u043b \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 csrf-\u0442\u043e\u043a\u0435\u043d\u0430. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0431\u044b\u043b \u0441\u043e\u0437\u0434\u0430\u043d \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043c\u043e\u0434\u0443\u043b\u044c <em>utils.py<\/em> \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440 <em>enforce_csrf<\/em><\/p>\n<pre><code class=\"python\">from functools import wraps from rest_framework.authentication import CSRFCheck from rest_framework import exceptions, request, response  def enforce_csrf(func):     \"\"\"     \u0414\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440 \u0434\u043b\u044f \u043f\u0440\u0438\u043d\u0443\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 CSRF.     \"\"\"     @wraps(func)     def wrapped_view(request, *args, **kwargs):         check = CSRFCheck(dummy_get_response)         check.process_request(request)         reason = check.process_view(request, None, (), {})         if reason:             raise exceptions.PermissionDenied('CSRF Failed: %s' % reason)          return func(request, *args, **kwargs)     return wrapped_view<\/code><\/pre>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<p>\u0422\u0430\u043a\u0436\u0435 \u0432 \u0444\u0430\u0439\u043b \u0441 \u043d\u0430\u0448\u0438\u043c <code>CookieTokenObtainPairView<\/code>  \u0434\u043e\u0431\u0430\u0432\u0438\u043b API \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 csrf-\u0442\u043e\u043a\u0435\u043d\u0430<\/p>\n<pre><code class=\"python\">def get_csrf(request: Request) -&gt; Response:     response = JsonResponse({'detail': 'CSRF cookie set'})     response['X-CSRFToken'] = get_token(request)     return response<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0442\u043e\u043a\u0435\u043d\u043e\u0432 &#8212; \u0443\u0434\u0430\u043b\u044f\u044e \u0438\u0445 \u0438\u0437 \u0442\u0435\u043b\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044e \u0432 \u043a\u0443\u043a\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>set_jwt_cookies<\/code><\/p>\n<pre><code class=\"python\">def set_jwt_cookies(response: response.Response, access_token: str, refresh_token: str) -&gt; response.Response:     response.set_cookie(         'access_token',         access_token,         max_age=5 * 60,  # 4 \u043c\u0438\u043d\u0443\u0442\u044b         httponly=True,    # \u0417\u0430\u0449\u0438\u0442\u0430 \u043e\u0442 XSS         # secure=True,      # \u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0434\u043b\u044f \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u043d \u0440\u0435\u0436\u0438\u043c\u0430         samesite='Strict' # \u0417\u0430\u0449\u0438\u0442\u0430 \u043e\u0442 CSRF     )     response.set_cookie(         'refresh_token',         refresh_token,         max_age=24 * 60 * 60,  # 1 \u0434\u0435\u043d\u044c         httponly=True,         # secure=True,         samesite='Strict'     )     return response <\/code><\/pre>\n<h2>\u041f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043d\u0430 React<\/h2>\n<p>\u041d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 React \u043d\u0430\u043f\u0438\u0441\u0430\u043b \u043f\u0440\u043e\u0441\u0442\u0435\u043d\u044c\u043a\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0441 \u043b\u043e\u0433\u0438\u043d\u043e\u043c: <\/p>\n<pre><code class=\"javascript\">import { useState, useEffect } from \"react\"; import { useNavigate } from \"react-router-dom\";  \/\/\u041f\u0440\u0438\u043d\u0443\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 crsf-\u0442\u043e\u043a\u0435\u043d\u0430 \u0434\u043b\u044f \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0435\u0433\u043e \u0432 \u043a\u0443\u043a\u0438 \/\/\u0438 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 POST-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 async function getCSRF() {   return axios.get('\/api\/user\/csrf\/', { withCredentials: true })       .then((res) =&gt; {           return res.headers['x-csrftoken'];       })       .catch((err) =&gt; {           console.error('\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 CSRF-\u0442\u043e\u043a\u0435\u043d\u0430:', err);           throw err;       }); }  \/\/\u041c\u043e\u0438 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0435 \u043f\u0440\u0438\u043a\u043e\u043b\u044b \u0441 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435\u043c \u0438\u043c\u0435\u043d\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u0435\u0433\u043e \u043f\u0441\u0435\u0432\u0434\u043e-\u0441\u0442\u0430\u0442\u0443\u0441\u0430 const [username, setUserName] = useState(() =&gt;     localStorage.getItem(\"username\")     ? JSON.parse(localStorage.getItem(\"username\"))     : null ); const [userStatus, setUserStatus] = useState(() =&gt;     localStorage.getItem(\"userStatus\")     ? JSON.parse(localStorage.getItem(\"userStatus\"))     : null );  const loginUser = async (e) =&gt; {     e.preventDefault();     let csrfToken = await getCSRF() \/\/\u041f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u043e\u0442 \u0434\u0436\u0430\u043d\u0433\u043e csrf \u0442\u043e\u043a\u0435\u043d \u0438 \u0432\u0441\u0442\u0430\u0432\u0438\u043b\u0438 \u0432 \u043a\u0443\u043a\u0438     const response = await fetch(\"\/api\/user\/token\/\", {         method: \"POST\",         credentials: 'include',         headers: {             \"Content-Type\": \"application\/json\",             'X-CSRFToken': csrfToken, \/\/ \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c CSRF-\u0442\u043e\u043a\u0435\u043d \u0432 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a          },         body: JSON.stringify({             username: e.target.username.value,             password: e.target.password.value,         }),     });      if (response.ok) {         const data = await response.json();         \/\/\u0414\u0435\u043b\u0430\u0435\u043c \u0442\u043e, \u0447\u0442\u043e \u043d\u0430\u043c \u043d\u0430\u0434\u043e \u043f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0433\u043e \u043b\u043e\u0433\u0438\u043d\u0430         setUserName(data[\"user\"])         setUserStatus(data[\"user_status\"])         localStorage.setItem(\"username\", JSON.stringify(data[\"user\"]));         localStorage.setItem(\"userStatus\", JSON.stringify(data[\"user_status\"]));         navigate(\"\/\");     } else {         alert(\"\u041d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439 \u043b\u043e\u0433\u0438\u043d \u0438\u043b\u0438 \u043f\u0430\u0440\u043e\u043b\u044c\");     } };<\/code><\/pre>\n<p>\u0424\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u0435\u0440\u0433\u0430\u0435\u0442 \u0434\u0436\u0430\u043d\u0433\u043e, \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u043a\u0443\u043a\u0438 \u0441 csrf-\u0442\u043e\u043a\u0435\u043d\u043e\u043c \u0438 \u0437\u0430\u0442\u0435\u043c \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u043d\u0430\u0448\u0435\u043c\u0443 <code>CookieTokenObtainPairView<\/code> . \u041f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0433\u043e \u0432\u0445\u043e\u0434\u0430 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044e \u0432 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u043d\u0443\u0436\u043d\u044b\u0435 \u043c\u043d\u0435 \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u0435\u0433\u043e \u0441\u0442\u0430\u0442\u0443\u0441-\u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0443<\/p>\n<p>\u0414\u0430\u043b\u0435\u0435 \u043f\u0440\u0438 API \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u0445 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0430\u043c \u043d\u0430\u0434\u043e \u0432\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u043a\u0443\u043a\u0438 \u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u044b, \u0432 React \u0441\u0434\u0435\u043b\u0430\u043b \u044d\u0442\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0443\u043a\u0430\u0437\u0430\u043d\u0438\u044f <code>withCredentials: true<\/code><\/p>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0441 GET \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c:<\/p>\n<pre><code class=\"javascript\">function refreshObjectDetail(setObjectDetail, apiPathDetail) {     axios         .get(`${apiPathDetail}`, {             headers: {                 'Content-Type': 'application\/json',                              },             withCredentials: true,         })         .then((res) =&gt; {             setObjectDetail(res.data);             if (res.data.name){                 document.title = res.data.name;             }         })         .catch((err) =&gt; console.log(err)); }<\/code><\/pre>\n<p>\u0418 \u0431\u043e\u043b\u0435\u0435 \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0432\u043e\u043b\u043d\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043e \u0442\u043e\u043c, \u0447\u0442\u043e \u043a\u0442\u043e-\u0442\u043e \u0432\u0430\u0448\u0438 \u0442\u043e\u043a\u0435\u043d\u044b \u043c\u043e\u0436\u0435\u0442 \u0443\u043a\u0440\u0430\u0441\u0442\u044c \u0438\u0437 \u043e\u0442\u043a\u0440\u044b\u0442\u043e\u0433\u043e \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430, \u043b\u044f\u043f\u043e\u0442\u0430!<\/p>\n<h2>\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e cookie <\/h2>\n<p>\u0414\u043b\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0442\u043e\u043a\u0435\u043d\u043e\u0432, \u0441\u043f\u0440\u044f\u0442\u0430\u043d\u043d\u044b\u0445 \u0432 \u043a\u0443\u043a\u0438 \u043f\u043e\u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043b\u043e\u0441\u044c \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441 <code>CookieAuthentication<\/code>  \u0438 \u0442\u0430\u043a\u0436\u0435 \u0441 \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u043e\u043c <code>enforce_csrf<\/code><\/p>\n<pre><code class=\"python\">from django.conf import settings from django.utils.decorators import method_decorator from rest_framework_simplejwt.authentication import JWTAuthentication from .utils import enforce_csrf  class CookieJWTAuthentication(JWTAuthentication):          @method_decorator(enforce_csrf)     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)         return self.get_user(validated_token), validated_token<\/code><\/pre>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043b \u044d\u0442\u043e\u0442 \u043a\u043b\u0430\u0441\u0441 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043a\u0430\u043a \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0438 \u043d\u0435\u043f\u043e\u0432\u0442\u043e\u0440\u0438\u043c\u044b\u0439 \u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0430\u0445 REST_FRAMEWORK <\/p>\n<pre><code class=\"python\">REST_FRAMEWORK = {     \"DEFAULT_PERMISSION_CLASSES\": [         \"rest_framework.permissions.IsAuthenticated\",     ],     'DEFAULT_AUTHENTICATION_CLASSES': [         'user.authenticate.CookieJWTAuthentication',     ],   }<\/code><\/pre>\n<p>\u041d\u043e \u043c\u044b \u0436\u0435 \u043f\u043e\u043c\u043d\u0438\u043c, \u0447\u0442\u043e \u0441\u0440\u043e\u043a \u043d\u0430\u0448\u0435\u0433\u043e access-\u0442\u043e\u043a\u0435\u043d\u0430 \u0432\u0441\u0435\u0433\u043e-\u0442\u043e 5 \u043c\u0438\u043d\u0443\u0442, \u0442\u0430\u043a \u0447\u0442\u043e \u043f\u043e\u0440\u0430 \u0431\u044b \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u0442\u044c \u043a <s>\u043e\u0431\u043d\u0443\u043b\u0435\u043d\u0438\u044e<\/s> \u043a \u0441\u0442\u0430\u0434\u0438\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f.<\/p>\n<h2>\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 JWT-\u0442\u043e\u043a\u0435\u043d\u043e\u0432<\/h2>\n<p>\u0412\u0441\u044f \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u0430\u044f \u043b\u043e\u0433\u0438\u043a\u0430 \u0441\u043f\u0440\u044f\u0442\u0430\u043b\u0430\u0441\u044c \u0432 \u043a\u043b\u0430\u0441\u0441\u0435 <code>CookieTokenRefreshView<\/code><\/p>\n<pre><code class=\"python\">class CookieTokenRefreshView(JWTAuthentication, TokenRefreshView):     \"\"\"     \u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f JWT-\u0442\u043e\u043a\u0435\u043d\u043e\u0432 (access \u0438 refresh) \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043a\u0443\u043a.      \u042d\u0442\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0435\u0442 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 `TokenObtainPairView` \u0438\u0437 Django REST Framework Simple JWT.     \u041f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0433\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0442\u043e\u043a\u0435\u043d\u043e\u0432 -  access \u0438 refresh \u0442\u043e\u043a\u0435\u043d\u044b \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u044e\u0442\u0441\u044f \u0432 HTTP-only \u043a\u0443\u043a\u0438 \u0438 \u0443\u0434\u0430\u043b\u044f\u044e\u0442\u0441\u044f     \u0438\u0437 \u0442\u0435\u043b\u0430 \u043e\u0442\u0432\u0435\u0442\u0430.      \u041f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u0435:     - \u0414\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043a\u0443\u043a\u0430\u043c\u0438 \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c CORS \u0441 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u043e\u0439 credentials.     - \u041a\u0443\u043a\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u044b \u0444\u043b\u0430\u0433\u0430\u043c\u0438 `HttpOnly`, `Secure` \u0438 `SameSite`.     \"\"\"     @method_decorator(enforce_csrf)     def post(self, request: Request, *args, **kwargs) -&gt; Response:         raw_refresh_token = request.COOKIES.get(settings.SIMPLE_JWT['REFRESH_COOKIE']) or None         raw_acces_token = request.COOKIES.get(settings.SIMPLE_JWT['AUTH_COOKIE']) or None         data = {'access': raw_acces_token, 'refresh': raw_refresh_token}          serializer = self.get_serializer(data=data)         try:             serializer.is_valid(raise_exception=True)         except TokenError as e:             raise InvalidToken(e.args[0])          response = Response(serializer.validated_data, status=status.HTTP_200_OK)                  access_token = response.data.get('access')         refresh_token = response.data.get('refresh')          if access_token and refresh_token:             response = set_jwt_cookies(response, access_token, refresh_token)                          del response.data['access']             del response.data['refresh']          return response<\/code><\/pre>\n<p>\u041f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u044e csrf-\u0442\u043e\u043a\u0435\u043d\u044b, \u0434\u0430\u043b\u0435\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u044e \u0438\u0437 \u043a\u0443\u043a\u0438 \u0441\u0432\u043e\u0438 \u0442\u043e\u043a\u0435\u043d\u044b. \u041f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0442\u043e\u043a\u0435\u043d\u043e\u0432 &#8212; \u0443\u0434\u0430\u043b\u044f\u044e \u0438\u0445 \u0438\u0437 \u0442\u0435\u043b\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044e \u0432 \u043a\u0443\u043a\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0440\u0430\u043d\u0435\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>set_jwt_cookies<\/code><\/p>\n<p>\u041d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 React \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0442\u043e\u043a\u0435\u043d\u043e\u0432 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<pre><code class=\"javascript\">const logoutUser = () =&gt; {     setUserName(null);     localStorage.removeItem(\"username\");     localStorage.removeItem(\"userStatus\");     navigate(\"\/login\"); };  const refreshToken = async () =&gt; {     let csrfToken = await getCSRF()     try {         await fetch(\"\/api\/user\/token\/refresh\/\", {             method: 'POST',             credentials: 'include', \/\/ \u0412\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u043a\u0443\u043a\u0438             headers: {                 \"Content-Type\": \"application\/json\",                 'X-CSRFToken': csrfToken, \/\/ \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c CSRF-\u0442\u043e\u043a\u0435\u043d \u0432 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a             },         });     } catch (error) {         console.error('Error refreshing token:', error);         logoutUser();     } };<\/code><\/pre>\n<p>\u0414\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0442\u043e\u043a\u0435\u043d \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u043b\u0441\u044f \u043a\u0430\u0436\u0434\u044b\u0435 5 \u043c\u0438\u043d\u0443\u0442 \u0438 React \u0434\u0435\u0440\u0433\u0430\u043b \u0431\u044b \u0434\u0436\u0430\u043d\u0433\u043e \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u044d\u0442\u0438\u0445 \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u043e\u0432 &#8212; \u0441\u043e\u0437\u0434\u0430\u043b \u0435\u0434\u0438\u043d\u044b\u0439 \u0444\u0430\u0439\u043b <em>AuthContext.js <\/em>\u043d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 \u0444\u0440\u043e\u043d\u0442\u0430 \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u043b \u0445\u0443\u043a useEffect \u0434\u043b\u044f \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430<\/p>\n<pre><code class=\"javascript\">import { useState, useEffect } from \"react\";  useEffect(()=&gt;{      const REFRESH_INTERVAL = 1000 * 60 * 4.9\/\/ \u041f\u043e\u0447\u0442\u0438 5 \u043c\u0438\u043d\u0443\u0442, \u043a\u0430\u043a \u0438 \u0432\u0440\u0435\u043c\u044f \u0436\u0438\u0437\u043d\u0438 access \u0442\u043e\u043a\u0435\u043d\u0430         let interval = setInterval(()=&gt;{             refreshToken()         }, REFRESH_INTERVAL)         return () =&gt; clearInterval(interval)      },[])<\/code><\/pre>\n<h2>\u0412\u044b\u0432\u043e\u0434\u044b<\/h2>\n<p>\u0412\u043e\u0442 \u0442\u0430\u043a\u0430\u044f \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0430\u0441\u044c \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0447\u0435\u0440\u0435\u0437 JWT \u0442\u043e\u043a\u0435\u043d\u044b, \u0441\u043f\u0440\u044f\u0442\u0430\u043d\u043d\u044b\u0445 \u0432 cookie. \u041d\u0430 \u043c\u043e\u0439 \u0432\u0437\u0433\u043b\u044f\u0434, \u0442\u0430\u043a\u043e\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u0433\u043e\u0440\u0430\u0437\u0434\u043e \u0431\u043e\u043b\u0435\u0435 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u044b\u0439, \u0447\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u044d\u0442\u0438 \u0442\u043e\u043a\u0435\u043d\u044b \u0432 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u043c \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u044f\u0432\u043d\u043e \u043d\u0435 \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u043e \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043a\u043e\u043d\u0444\u0435\u043d\u0434\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438<\/p>\n<p>\u0411\u0443\u0434\u0443 \u0440\u0430\u0434 \u043a\u0440\u0438\u0442\u0438\u043a\u0435 \u0438 \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u043c!<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/873918\/\"> https:\/\/habr.com\/ru\/articles\/873918\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u0421\u0442\u0430\u0442\u044c\u044f \u0432 \u043f\u0435\u0440\u0432\u0443\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u0431\u044b\u043b\u0430 \u043f\u0440\u0435\u0436\u0434\u0435 \u0432\u0441\u0435\u0433\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0430 \u0434\u043b\u044f \u0441\u0430\u043c\u043e\u0433\u043e \u0441\u0435\u0431\u044f \u0441 \u0446\u0435\u043b\u044c\u044e \u0437\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u043d\u0438\u044f \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0433\u043e \u043e\u043f\u044b\u0442\u0430 \u043f\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0445 \u043a\u043e\u0441\u0442\u044b\u043b\u0435\u0439 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e JWT-\u0442\u043e\u043a\u0435\u043d\u043e\u0432, \u043d\u0430\u0445\u043e\u0434\u044f\u0449\u0438\u0445\u0441\u044f \u0432 \u043a\u0443\u043a\u0438.<\/p>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0431\u0435\u043a\u0435\u043d\u0434\u0430 \u0431\u044b\u043b \u0432\u044b\u0431\u0440\u0430\u043d \u0433\u043e\u0440\u044f\u0447\u043e \u043b\u044e\u0431\u0438\u043c\u044b\u0439 Django Rest Framework, \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0444\u0440\u043e\u043d\u0442\u043e\u0432\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u0432 \u043c\u043e\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0441\u044f React. \u041d\u0430\u0447\u043d\u0443 \u0441 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u043e\u0439 \u0441\u0442\u043e\u0440\u043e\u043d\u044b. \u042f \u043f\u0440\u043e\u043f\u0443\u0449\u0443 \u0448\u0430\u0433\u0438 \u043f\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 Django REST Framework \u0432 \u0441\u0432\u044f\u0437\u043a\u0435 \u0441 React. \u0412 Django \u0432 \u043c\u043e\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0431\u044b\u043b\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 <em>user.<\/em><\/p>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0431\u0430\u0437\u044b JWT-\u0442\u043e\u043a\u0435\u043d\u043e\u0432 \u0432\u0437\u044f\u043b \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 <a href=\"https:\/\/django-rest-framework-simplejwt.readthedocs.io\/en\/latest\/\" rel=\"noopener noreferrer nofollow\">Simple JWT<\/a>. <\/p>\n<p>\u041c\u043e\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438:<\/p>\n<pre><code class=\"python\">SIMPLE_JWT = {     'ROTATE_REFRESH_TOKENS': True,  # \u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 refresh \u0442\u043e\u043a\u0435\u043d\u0430 \u043f\u0440\u0438 \u0437\u0430\u043c\u0435\u043d\u0435 access \u0442\u043e\u043a\u0435\u043d\u0430     'BLACKLIST_AFTER_ROTATION': True,      'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),      'REFRESH_TOKEN_LIFETIME': timedelta(days=1),      'REFRESH_COOKIE': 'refresh_token',  # \u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043a\u043b\u044e\u0447\u0430 \u0432 \u043a\u0443\u043a\u0438, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f refresh \u0442\u043e\u043a\u0435\u043d     'AUTH_COOKIE': 'access_token',  # \u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043a\u043b\u044e\u0447\u0430 \u0432 \u043a\u0443\u043a\u0438, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f access \u0442\u043e\u043a\u0435\u043d     'AUTH_COOKIE_SECURE': False,  # \u041a\u0443\u043a\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e HTTPS (True \u0434\u043b\u044f production)     'AUTH_COOKIE_HTTP_ONLY': True,  # \u0417\u0430\u043f\u0440\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u043a\u0443\u043a\u0438 \u0447\u0435\u0440\u0435\u0437 JavaScript     'AUTH_COOKIE_SAMESITE': 'Strict',  # \u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u043a\u0443\u043a\u0438 \u043f\u0440\u0438 \u043a\u0440\u043e\u0441\u0441-\u0441\u0430\u0439\u0442\u043e\u0432\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u0445. }<\/code><\/pre>\n<p>\u041f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0440\u0430\u0437\u043c\u0435\u0442\u0438\u043b \u0441\u0430\u043c\u0438 API \u043f\u0443\u0442\u0438:<\/p>\n<pre><code class=\"python\">from django.urls import path from .views import CookieTokenObtainPairView, CookieTokenRefreshView, get_csrf  urlpatterns = [    path('token\/', CookieTokenObtainPairView.as_view(), name='token_obtain_pair'),    path('token\/refresh\/', CookieTokenRefreshView.as_view(), name='token_refresh'),    path('csrf\/', get_csrf, name='get_csrf'), ]<\/code><\/pre>\n<h2>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043b\u043e\u0433\u0438\u043a\u0438 \u0432\u0445\u043e\u0434\u0430 <\/h2>\n<p>\u0414\u0430\u043b\u0435\u0435 \u043d\u0430\u0447\u043d\u0435\u043c \u0441 \u043a\u043b\u0430\u0441\u0441\u0430 <code>CookieTokenObtainPairView<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u043b\u043e\u0433\u0438\u043a\u0443 \u0432\u0445\u043e\u0434\u0430 \u0438 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044e \u043f\u0435\u0440\u0432\u0438\u0447\u043d\u043e\u0439 \u043f\u0430\u0440\u044b jwt \u0442\u043e\u043a\u0435\u043d\u043e\u0432:<\/p>\n<pre><code class=\"python\">from django.conf import settings from django.http import JsonResponse from django.middleware.csrf import get_token from django.utils.decorators import method_decorator  from rest_framework.response import Response from rest_framework.permissions import IsAuthenticated from rest_framework.request import Request from rest_framework import status from rest_framework.permissions import AllowAny  from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView from rest_framework_simplejwt.authentication import JWTAuthentication from rest_framework_simplejwt.exceptions import InvalidToken, TokenError  from .utils import set_jwt_cookies, enforce_csrf from .serializer import CookieTokenObtainPairSerializer  class CookieTokenObtainPairView(TokenObtainPairView):      \"\"\"     \u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f JWT-\u0442\u043e\u043a\u0435\u043d\u043e\u0432 (access \u0438 refresh) \u0438 \u0438\u0445 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0432 \u043a\u0443\u043a\u0438.      \u042d\u0442\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0435\u0442 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 `TokenObtainPairView` \u0438\u0437 Django REST Framework Simple JWT.     \u041f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 access \u0438 refresh \u0442\u043e\u043a\u0435\u043d\u044b \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u044e\u0442\u0441\u044f \u0432 HTTP-only \u043a\u0443\u043a\u0438 \u0438 \u0443\u0434\u0430\u043b\u044f\u044e\u0442\u0441\u044f     \u0438\u0437 \u0442\u0435\u043b\u0430 \u043e\u0442\u0432\u0435\u0442\u0430.      \u041f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u0435:     - \u0414\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043a\u0443\u043a\u0430\u043c\u0438 \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c CORS \u0441 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u043e\u0439 credentials.     \"\"\"      serializer_class = CookieTokenObtainPairSerializer     authentication_classes = ()     permission_classes = (AllowAny,)      @method_decorator(enforce_csrf)     def post(self, request: Request, *args, **kwargs) -&gt; Response:         response = super().post(request, *args, **kwargs)          if response.status_code == 200:             access_token = response.data.get('access')             refresh_token = response.data.get('refresh')              if access_token and refresh_token:                 response = set_jwt_cookies(response, access_token, refresh_token)                                  del response.data['access']                 del response.data['refresh']          return response    <\/code><\/pre>\n<p>\u041f\u0440\u043e\u0431\u0435\u0433\u0443\u0441\u044c \u043f\u043e \u043a\u043e\u0434\u0443:<\/p>\n<p><code>CookieTokenObtainPairSerializer<\/code> &#8212; \u043d\u0430\u043f\u0438\u0441\u0430\u043b \u0441\u0432\u043e\u0439 \u0441\u0435\u0440\u0438\u0430\u043b\u0430\u0439\u0437\u0435\u0440, \u0442\u0430\u043a \u043a\u0430\u043a \u043b\u0438\u0447\u043d\u043e \u043c\u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u043f\u043e\u043c\u0438\u043c\u043e \u0442\u043e\u043a\u0435\u043d\u043e\u0432 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043b\u0441\u044f \u0438 \u0441\u0442\u0430\u0442\u0443\u0441-\u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0443<\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from rest_framework_simplejwt.serializers import TokenObtainPairSerializer  class CookieTokenObtainPairSerializer(TokenObtainPairSerializer):          def validate(self, attrs):         data = super().validate(attrs)         data['user'] = str(self.user)         data['user_status'] = \"active\"         return data<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0422\u0430\u043a\u0436\u0435 \u0440\u0435\u0448\u0438\u043b \u043f\u0435\u0440\u0435\u0441\u0442\u0440\u0430\u0445\u043e\u0432\u0430\u0442\u044c \u0441\u0432\u043e\u0438 \u0444\u043e\u0431\u0438\u0438 \u0431\u044b\u0442\u044c \u0432\u0437\u043b\u043e\u043c\u0430\u043d\u043d\u044b\u043c \u043f\u0435\u0440\u0443\u0430\u043d\u0441\u043a\u0438\u043c\u0438 \u0445\u0430\u043a\u0435\u0440\u0430\u043c\u0438 \u0438 \u0432\u043d\u0435\u0434\u0440\u0438\u043b \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 csrf-\u0442\u043e\u043a\u0435\u043d\u0430. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0431\u044b\u043b \u0441\u043e\u0437\u0434\u0430\u043d \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043c\u043e\u0434\u0443\u043b\u044c <em>utils.py<\/em> \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440 <em>enforce_csrf<\/em><\/p>\n<pre><code class=\"python\">from functools import wraps from rest_framework.authentication import CSRFCheck from rest_framework import exceptions, request, response  def enforce_csrf(func):     \"\"\"     \u0414\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440 \u0434\u043b\u044f \u043f\u0440\u0438\u043d\u0443\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 CSRF.     \"\"\"     @wraps(func)     def wrapped_view(request, *args, **kwargs):         check = CSRFCheck(dummy_get_response)         check.process_request(request)         reason = check.process_view(request, None, (), {})         if reason:             raise exceptions.PermissionDenied('CSRF Failed: %s' % reason)          return func(request, *args, **kwargs)     return wrapped_view<\/code><\/pre>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<p>\u0422\u0430\u043a\u0436\u0435 \u0432 \u0444\u0430\u0439\u043b \u0441 \u043d\u0430\u0448\u0438\u043c <code>CookieTokenObtainPairView<\/code>  \u0434\u043e\u0431\u0430\u0432\u0438\u043b API \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 csrf-\u0442\u043e\u043a\u0435\u043d\u0430<\/p>\n<pre><code class=\"python\">def get_csrf(request: Request) -&gt; Response:     response = JsonResponse({'detail': 'CSRF cookie set'})     response['X-CSRFToken'] = get_token(request)     return response<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0442\u043e\u043a\u0435\u043d\u043e\u0432 &#8212; \u0443\u0434\u0430\u043b\u044f\u044e \u0438\u0445 \u0438\u0437 \u0442\u0435\u043b\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044e \u0432 \u043a\u0443\u043a\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>set_jwt_cookies<\/code><\/p>\n<pre><code class=\"python\">def set_jwt_cookies(response: response.Response, access_token: str, refresh_token: str) -&gt; response.Response:     response.set_cookie(         'access_token',         access_token,         max_age=5 * 60,  # 4 \u043c\u0438\u043d\u0443\u0442\u044b         httponly=True,    # \u0417\u0430\u0449\u0438\u0442\u0430 \u043e\u0442 XSS         # secure=True,      # \u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0434\u043b\u044f \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u043d \u0440\u0435\u0436\u0438\u043c\u0430         samesite='Strict' # \u0417\u0430\u0449\u0438\u0442\u0430 \u043e\u0442 CSRF     )     response.set_cookie(         'refresh_token',         refresh_token,         max_age=24 * 60 * 60,  # 1 \u0434\u0435\u043d\u044c         httponly=True,         # secure=True,         samesite='Strict'     )     return response <\/code><\/pre>\n<h2>\u041f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043d\u0430 React<\/h2>\n<p>\u041d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 React \u043d\u0430\u043f\u0438\u0441\u0430\u043b \u043f\u0440\u043e\u0441\u0442\u0435\u043d\u044c\u043a\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0441 \u043b\u043e\u0433\u0438\u043d\u043e\u043c: <\/p>\n<pre><code class=\"javascript\">import { useState, useEffect } from \"react\"; import { useNavigate } from \"react-router-dom\";  \/\/\u041f\u0440\u0438\u043d\u0443\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 crsf-\u0442\u043e\u043a\u0435\u043d\u0430 \u0434\u043b\u044f \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0435\u0433\u043e \u0432 \u043a\u0443\u043a\u0438 \/\/\u0438 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 POST-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 async function getCSRF() {   return axios.get('\/api\/user\/csrf\/', { withCredentials: true })       .then((res) =&gt; {           return res.headers['x-csrftoken'];       })       .catch((err) =&gt; {           console.error('\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 CSRF-\u0442\u043e\u043a\u0435\u043d\u0430:', err);           throw err;       }); }  \/\/\u041c\u043e\u0438 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0435 \u043f\u0440\u0438\u043a\u043e\u043b\u044b \u0441 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435\u043c \u0438\u043c\u0435\u043d\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u0435\u0433\u043e \u043f\u0441\u0435\u0432\u0434\u043e-\u0441\u0442\u0430\u0442\u0443\u0441\u0430 const [username, setUserName] = useState(() =&gt;     localStorage.getItem(\"username\")     ? JSON.parse(localStorage.getItem(\"username\"))     : null ); const [userStatus, setUserStatus] = useState(() =&gt;     localStorage.getItem(\"userStatus\")     ? JSON.parse(localStorage.getItem(\"userStatus\"))     : null );  const loginUser = async (e) =&gt; {     e.preventDefault();     let csrfToken = await getCSRF() \/\/\u041f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u043e\u0442 \u0434\u0436\u0430\u043d\u0433\u043e csrf \u0442\u043e\u043a\u0435\u043d \u0438 \u0432\u0441\u0442\u0430\u0432\u0438\u043b\u0438 \u0432 \u043a\u0443\u043a\u0438     const response = await fetch(\"\/api\/user\/token\/\", {         method: \"POST\",         credentials: 'include',         headers: {             \"Content-Type\": \"application\/json\",             'X-CSRFToken': csrfToken, \/\/ \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c CSRF-\u0442\u043e\u043a\u0435\u043d \u0432 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a          },         body: JSON.stringify({             username: e.target.username.value,             password: e.target.password.value,         }),     });      if (response.ok) {         const data = await response.json();         \/\/\u0414\u0435\u043b\u0430\u0435\u043c \u0442\u043e, \u0447\u0442\u043e \u043d\u0430\u043c \u043d\u0430\u0434\u043e \u043f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0433\u043e \u043b\u043e\u0433\u0438\u043d\u0430         setUserName(data[\"user\"])         setUserStatus(data[\"user_status\"])         localStorage.setItem(\"username\", JSON.stringify(data[\"user\"]));         localStorage.setItem(\"userStatus\", JSON.stringify(data[\"user_status\"]));         navigate(\"\/\");     } else {         alert(\"\u041d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439 \u043b\u043e\u0433\u0438\u043d \u0438\u043b\u0438 \u043f\u0430\u0440\u043e\u043b\u044c\");     } };<\/code><\/pre>\n<p>\u0424\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u0435\u0440\u0433\u0430\u0435\u0442 \u0434\u0436\u0430\u043d\u0433\u043e, \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u043a\u0443\u043a\u0438 \u0441 csrf-\u0442\u043e\u043a\u0435\u043d\u043e\u043c \u0438 \u0437\u0430\u0442\u0435\u043c \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u043d\u0430\u0448\u0435\u043c\u0443 <code>CookieTokenObtainPairView<\/code> . \u041f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0433\u043e \u0432\u0445\u043e\u0434\u0430 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044e \u0432 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u043d\u0443\u0436\u043d\u044b\u0435 \u043c\u043d\u0435 \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u0435\u0433\u043e \u0441\u0442\u0430\u0442\u0443\u0441-\u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0443<\/p>\n<p>\u0414\u0430\u043b\u0435\u0435 \u043f\u0440\u0438 API \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u0445 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0430\u043c \u043d\u0430\u0434\u043e \u0432\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u043a\u0443\u043a\u0438 \u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u044b, \u0432 React \u0441\u0434\u0435\u043b\u0430\u043b \u044d\u0442\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0443\u043a\u0430\u0437\u0430\u043d\u0438\u044f <code>withCredentials: true<\/code><\/p>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0441 GET \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c:<\/p>\n<pre><code class=\"javascript\">function refreshObjectDetail(setObjectDetail, apiPathDetail) {     axios         .get(`${apiPathDetail}`, {             headers: {                 'Content-Type': 'application\/json',                              },             withCredentials: true,         })         .then((res) =&gt; {             setObjectDetail(res.data);             if (res.data.name){                 document.title = res.data.name;             }         })         .catch((err) =&gt; console.log(err)); }<\/code><\/pre>\n<p>\u0418 \u0431\u043e\u043b\u0435\u0435 \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0432\u043e\u043b\u043d\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043e \u0442\u043e\u043c, \u0447\u0442\u043e \u043a\u0442\u043e-\u0442\u043e \u0432\u0430\u0448\u0438 \u0442\u043e\u043a\u0435\u043d\u044b \u043c\u043e\u0436\u0435\u0442 \u0443\u043a\u0440\u0430\u0441\u0442\u044c \u0438\u0437 \u043e\u0442\u043a\u0440\u044b\u0442\u043e\u0433\u043e \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430, \u043b\u044f\u043f\u043e\u0442\u0430!<\/p>\n<h2>\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e cookie <\/h2>\n<p>\u0414\u043b\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0442\u043e\u043a\u0435\u043d\u043e\u0432, \u0441\u043f\u0440\u044f\u0442\u0430\u043d\u043d\u044b\u0445 \u0432 \u043a\u0443\u043a\u0438 \u043f\u043e\u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043b\u043e\u0441\u044c \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441 <code>CookieAuthentication<\/code>  \u0438 \u0442\u0430\u043a\u0436\u0435 \u0441 \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u043e\u043c <code>enforce_csrf<\/code><\/p>\n<pre><code class=\"python\">from django.conf import settings from django.utils.decorators import method_decorator from rest_framework_simplejwt.authentication import JWTAuthentication from .utils import enforce_csrf  class CookieJWTAuthentication(JWTAuthentication):          @method_decorator(enforce_csrf)     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)         return self.get_user(validated_token), validated_token<\/code><\/pre>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043b \u044d\u0442\u043e\u0442<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-445136","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/445136","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=445136"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/445136\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=445136"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=445136"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=445136"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}