Angular6+Django SPA开发遇403错误:CSRF令牌缺失或不正确
解决Angular 6 + Django SPA的CSRF 403错误问题
嘿,我碰到过类似Angular和Django跨CSRF验证的坑,结合你的情况,咱们一步步来排查解决:
1. 先理清Angular请求模块的冲突
你同时导入了HttpClientModule和HttpModule,但Angular 6里HttpModule已经被弃用,推荐用HttpClient发起请求。这两个模块的XSRF处理逻辑不一样,可能导致自动携带CSRF头失效。
解决方案:
- 移除
HttpModule的导入,只保留HttpClientModule - 确保你的服务里用的是
HttpClient而非旧的Http来发请求
调整后的AppModule配置:
import { BrowserModule } from '@angular/platform-browser'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { NgModule } from '@angular/core'; import { HttpClientModule } from '@angular/common/http'; // 移除HttpModule的导入 import .... @NgModule({ declarations: [ AppComponent, RegisterComponent, LoginComponent, AlertComponent, ProfileComponent, RegisterinvoiceComponent, ], imports: [ BrowserModule, FormsModule, ReactiveFormsModule, AppRoutingModule, HttpClientModule // 只保留这个 ], providers: [ { provide: HttpClientXsrfModule.withOptions({ cookieName: 'csrftoken', headerName: 'X-CSRFToken', }) } ], bootstrap: [AppComponent] }) export class AppModule { }
2. 修正Django的CSRF Cookie分发逻辑
你把@method_decorator(ensure_csrf_cookie)加在了create(POST)方法上,但这个装饰器的作用是在响应里设置CSRF cookie,应该加在用户首先访问的GET接口上(比如登录页接口、专门的CSRF获取接口)。POST请求本身已经需要CSRF验证,这时候再设置cookie已经晚了。
解决方案:
- 创建一个专门的GET视图来分发CSRF cookie,或者在登录/首页的GET接口上加上这个装饰器:
from django.views.decorators.csrf import ensure_csrf_cookie from django.http import HttpResponse @ensure_csrf_cookie def get_csrf_token(request): return HttpResponse(status=204)
- 确保Angular在发起POST请求前,先调用这个GET接口拿到CSRF cookie
3. 检查Django的CORS和CSRF信任配置
如果你的前端(比如localhost:4200)和后端(localhost:8000)是跨域的,必须正确配置CORS和CSRF信任:
步骤1:安装并配置django-cors-headers
pip install django-cors-headers
在settings.py里添加:
INSTALLED_APPS = [ ... 'corsheaders', ... ] MIDDLEWARE = [ ... 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', ... ] # 允许的前端域名 CORS_ALLOWED_ORIGINS = [ "http://localhost:4200", ] # 允许携带凭证(Cookie) CORS_ALLOW_CREDENTIALS = True # 信任的CSRF来源 CSRF_TRUSTED_ORIGINS = [ "http://localhost:4200", ]
步骤2:调整CSRF Cookie的属性
本地开发时(HTTP环境),要确保CSRF Cookie的属性不会阻止浏览器发送:
# settings.py CSRF_COOKIE_SECURE = False # 本地HTTP环境设为False,生产HTTPS设为True CSRF_COOKIE_SAMESITE = 'Lax' # 跨域时可以设为'None'(但需要配合CSRF_COOKIE_SECURE=True)
4. 手动携带CSRF头作为兜底方案
如果自动携带还是不行,可以手动从Cookie中提取CSRF token,然后在请求头里设置:
首先创建一个服务来获取Cookie:
import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class CookieService { getCookie(name: string): string | null { const value = "; " + document.cookie; const parts = value.split("; " + name + "="); if (parts.length === 2) return parts.pop()?.split(";").shift() || null; return null; } }
然后在发起POST请求时手动加头:
import { HttpClient, HttpHeaders } from '@angular/common/http'; import { CookieService } from './cookie.service'; // ... constructor(private http: HttpClient, private cookieService: CookieService) {} submitInvoice(data: any) { const csrfToken = this.cookieService.getCookie('csrftoken'); const headers = new HttpHeaders({ 'X-CSRFToken': csrfToken || '', 'Content-Type': 'application/json' }); return this.http.post('/api/invoices/', data, { headers, withCredentials: true }); }
最后验证几个关键点
- 确认POST请求的请求头里确实包含
X-CSRFToken字段,值和Cookie里的csrftoken一致 - 确认Cookie的
Domain属性和请求的域名匹配,没有跨域问题 - 检查Django的
settings.py里CSRF_COOKIE_NAME是不是csrftoken(默认是,如果你没改的话没问题)
内容的提问来源于stack exchange,提问作者Marcus Grass
相关产品推荐
相关产品推荐

