Django(DRF)&React-禁止(CSRF cookie not set)

7

有数十个问题与我所提问的本质相同。然而,它们的答案似乎都不能解决我的问题。

我有一个React前端,在其中使用axios向后端发送请求。例如:
const request = await axios.post('${BASE_URL}/logout/')

大多数Django Rest Framework端点都是使用ViewSets创建的。然而,我有一些自定义端点,主要用于身份验证。

path('createaccount/', views.create_account),
path('me/', views.current_user),
path('logout/', views.logout),
path('login/', views.login),
path('resetpassword', views.reset_password),

为了开发这个项目,我在这些视图上加入了@csrf_exempt,因为当时我不想处理它。现在我接近部署的时间,是时候解决这个问题了。
一些答案说我需要从Django获取一个在cookies中保存的CSRF令牌,并将其传递到每个请求的头部。还有一些答案说我只需要配置axios就可以了。
axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
axios.defaults.xsrfCookieName = "XCSRF-TOKEN";

它将“只是工作”。我尝试调整我的CSRF_COOKIE_NAME的各种值,以使其工作。

一些答案甚至说要保留@csrf_exempt,但这听起来像一个非常糟糕的主意。

我是否真的需要生成/获取CSRF cookie?我是否在每个请求中都包含它?还是它只是axios的配置?


你使用了什么身份验证机制?如果是会话,则需要在每个 (POST, PUT, PATCH, DELETE) 中包含 CSRF。如果是这样,请尝试从 cookie 中获取 https://github.com/js-cookie/js-cookie/ 令牌,然后发出请求。 - Raja Simon
我正在使用JWT令牌身份验证。 - Programmingjoe
1
你有没有找到这个问题的答案?看起来我遇到了一个非常类似的问题。 - djo
2个回答

0
为使CSRF保护生效,您需要从Django发送CSRF cookie作为响应到React,以回应某些请求(如登录或其他)。它将使用前端的Set-Cookie设置cookie。因此,请确保您在Django侧拥有执行此操作的视图。如果没有,请创建一个视图,作为响应生成该令牌。
Django(4.04) CSRF验证工作方式(简化,基于middleware/csrf.py):
  1. 从 cookie 中获取 CSRF token(因此前端需要在另一个请求中将其重新发送回来)- 可能还会从 session 中获取,但在 React 中我不会使用它

    def _get_token(self, request):
        ....
        try:
            cookie_token = request.COOKIES[settings.CSRF_COOKIE_NAME]
        except KeyError:
            return None
    
  2. 将 cookie 的 CSRF token 与请求中的非cookie token 进行比较:

    def _check_token(self, request):
        # Access csrf_token via self._get_token() as rotate_token() may have
        # been called by an authentication middleware during the
        # process_request() phase.
        try:
            csrf_token = self._get_token(request)
        except InvalidTokenFormat as exc:
            raise RejectRequest(f"CSRF cookie {exc.reason}.")
    
        if csrf_token is None:
            # No CSRF cookie. For POST requests, we insist on a CSRF cookie,
            # and in this way we can avoid all CSRF attacks, including login
            # CSRF.
            raise RejectRequest(REASON_NO_CSRF_COOKIE)
    
        # Check non-cookie token for match.
        request_csrf_token = ""
        if request.method == "POST":
            try:
                request_csrf_token = request.POST.get("csrfmiddlewaretoken", "")
            except UnreadablePostError:
                # Handle a broken connection before we've completed reading the
                # POST data. process_view shouldn't raise any exceptions, so
                # we'll ignore and serve the user a 403 (assuming they're still
                # listening, which they probably aren't because of the error).
                pass
    
        if request_csrf_token == "":
            # Fall back to X-CSRFToken, to make things easier for AJAX, and
            # possible for PUT/DELETE.
            try:
                request_csrf_token = request.META[settings.CSRF_HEADER_NAME]
            except KeyError:
                raise RejectRequest(REASON_CSRF_TOKEN_MISSING)
            token_source = settings.CSRF_HEADER_NAME
        else:
            token_source = "POST"
    
        try:
            request_csrf_token = _sanitize_token(request_csrf_token)
        except InvalidTokenFormat as exc:
            reason = self._bad_token_message(exc.reason, token_source)
            raise RejectRequest(reason)
    
        if not _does_token_match(request_csrf_token, csrf_token):
            reason = self._bad_token_message("incorrect", token_source)
            raise RejectRequest(reason)
    

正如您所见,您需要在POST请求中包含csrfmiddlewaretoken或在头中包含它,并使用键:settings.CSRF_HEADER_NAME和从前端读取的值。

因此,例如,您可以设置 withCredentials: true(以包含初始cookie),在React中读取该初始CSRF cookie并将其添加到axios请求的特定键的标头中。

如果有问题,我建议您在Django代码中的middleware/csrf.py中设置断点来调试请求,以便您可以追踪缺少什么以及为什么CSRF验证失败。


-1

我曾经遇到过这个问题,当时我正在使用令牌认证。这是我解决它的方法。但不确定是否是最好的想法。我只在此视图中使用了csrf_exempt,而所有其他视图都是视图集。

@csrf_exempt
def get_current_user(request, *args, **kwargs):
    if request.method == 'GET':
        user = request.user
        serializer = UserDataSerializer(user)
        return JsonResponse(serializer.data, safe=False)

我的中间件在settings.py文件中

MIDDLEWARE = [
   'django.middleware.security.SecurityMiddleware',
   'django.contrib.sessions.middleware.SessionMiddleware',
   'corsheaders.middleware.CorsMiddleware',
   # 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
   'django.middleware.common.CommonMiddleware',
   'django.middleware.csrf.CsrfViewMiddleware',
   'django.contrib.auth.middleware.AuthenticationMiddleware',
   'django.middleware.locale.LocaleMiddleware',
   'oauth2_provider.middleware.OAuth2TokenMiddleware',
   'django.contrib.messages.middleware.MessageMiddleware',
   'django.middleware.clickjacking.XFrameOptionsMiddleware',
   'auditlog.middleware.AuditlogMiddleware',
]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接