Django + DRF: 403禁止访问:缺少或不正确的CSRF令牌

10

我有一个安卓客户端应用程序,试图通过Django + DRF后端进行身份验证。但是,当我尝试登录时,我收到以下响应:

403: CSRF Failed: CSRF token missing or incorrect.

该请求被发送到 http://localhost/rest-auth/google/ 并带有以下内容:

access_token: <the OAuth token from Google>

可能是什么原因导致这个问题?由于POST身份验证是客户端和服务器之间发生的第一件事,因此客户端没有CSRF令牌。我查看了很多遇到相同问题的旧问题,但是找不到任何解决方案。

Django端的相关设置如下:

AUTHENTICATION_BACKENDS = (
    "django.contrib.auth.backends.ModelBackend",
    "allauth.account.auth_backends.AuthenticationBackend"
)

TEMPLATE_CONTEXT_PROCESSORS = (
    "django.core.context_processors.request",
    "django.contrib.auth.context_processors.auth",
    "allauth.account.context_processors.account",
    "allauth.socialaccount.context_processors.socialaccount"
)

MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
)

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app',
    'allauth',
    'allauth.account',
    'allauth.socialaccount',
    'allauth.socialaccount.providers.facebook',
    'allauth.socialaccount.providers.google',

    'django.contrib.admin',

    # REST framework
    'rest_framework',
    'rest_framework.authtoken',
    'rest_auth',
    'rest_auth.registration',
)

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': ( 
        'rest_framework.permissions.IsAuthenticated'
    ),
}
4个回答

12

为什么会出现这个错误?

由于您在设置中未定义AUTHENTICATION_CLASSES,因此DRF将使用以下默认身份验证类。

'DEFAULT_AUTHENTICATION_CLASSES': (
    'rest_framework.authentication.SessionAuthentication',
    'rest_framework.authentication.BasicAuthentication'
)
现在,SessionAuthentication 要求使用 CSRF Token。如果您未传递有效的 CSRF token,将会出现 403 错误。

如果您正在使用带有 SessionAuthentication 的 AJAX 样式 API,则需要确保为任何 "不安全" 的 HTTP 方法调用(例如 PUTPATCHPOSTDELETE 请求)包含有效的 CSRF token。

那么你需要做什么呢?

由于您正在使用 TokenAuthentication,因此需要在 DRF 设置的 AUTHENTICATION_CLASSES 中明确定义它。这应该解决您的 CSRF token 问题。


你说我们必须传递有效的CSRF令牌。我在哪里可以找到这个令牌?在我的情况下,当我尝试使用rest-auth/login/端点登录时出现问题。但这是客户端和服务器之间的第一次联系,所以客户端不知道令牌。 - Ben
1
请查看Django文档链接以获取用于Ajax请求的CSRF令牌。 - Rahul Gupta
谢谢@rahul-gupta,但我不在Web环境中。我在C#环境(Xamarin)中。但无论如何,在rest<>client应用程序中,如果服务器没有告诉客户端csrf,客户端将无法知道它。 - Ben
然后,你应该使用不同的身份验证类,因为SessionAuthentication需要发送CSRF令牌。你需要在设置或特定视图中明确定义该特定的身份验证类。 - Rahul Gupta

9
傻乎乎的我,竟然错过了REST设置中的TokenAuthentication框架:

settings.py

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.TokenAuthentication',
    )
}

现在它按照预期工作。

1

在发送请求时,您需要传递csrf令牌,请查看给定的代码:

function getCookie(name) {
 var cookieValue = null;
 if (document.cookie && document.cookie !== '') {
     var cookies = document.cookie.split(';');
     for (var i = 0; i < cookies.length; i++) {
         var cookie = cookies[i].trim();
         // Does this cookie string begin with the name we want?
         if (cookie.substring(0, name.length + 1) === (name + '=')) {
             cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
             break;
         }
     }
 }
 return cookieValue;
}

const check = () => {

 const endPoint = '/api/check/'
 const csrftoken = getCookie('csrftoken'); // getting the cookie to pass as csrf token

 return fetch(baseUrl + endPoint,{
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'X-CSRFToken': csrftoken
        },
        
    })
    .then(response => {
        if (response.ok) {
            return response
        }
        else { 
            if (response.status === 401 && response.statusText === 'Unauthorized'){ // if it is un authorized then dispatch the logout
                dispatch(logout());
            }
            var error = new Error('Error: '+ response.status + ': ' + response.statusText); 
            error.response = response;  
            throw error; 
        }

    },               
    error => {
        var errmess = new Error(error.message); 
        throw errmess;
    })
    .then( data => data.json())
    .then(data => console.log(data))
    .catch(error => console.log(error.message)); 
   }

各位,无论你发送 GET PUT 还是 POST 请求,都需要发送 csrf token 以确保安全。

希望我的回答能对你有所帮助。


0

这个错误可能是由于urlpatterns的不正确实现,包括静态URL路径。要进行调试,

  1. 检查您是否正确设置了身份验证类。

    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': (
            <your auth classes>
        )
    }
    
  2. 检查类视图的路径是否正确定义。

    urlpatterns = [
        '<your URL path>', <your class-based view>.as_view(), name='<path name>')
    ]
    
  3. 检查您是否为静态和媒体文件定义了urlpatterns,并且在setting.py中正确定义了它们。

    settings.py中:

    STATIC_URL = '/static/'
    STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
    # STATIC_ROOT = '/assets'
    
    STATICFILES_DIRS = [
        os.path.join(BASE_DIR, 'static'),
    ]
    
    MEDIA_URL = '/media/'
    MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
    

    urls.py中:

    urlpatterns = [
        ...
    ]
    urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
    

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