我的前端(React)和后端(Django)是解耦的,分别运行在
localhost:3000
和 127.0.0.1:8000
上。
考虑以下前端请求:
async function test() {
let token = await getCsrfToken() // fetched from other endpoint
let url = 'http://localhost:8000/test'
let response = await fetch(url, {
method: 'POST',
headers: {
'X-CSRFToken': token,
},
credentials: 'include',
})
let response_text = await response.text()
return response_text
}
test()
到以下端点:
def test(request):
return HttpResponse('OK')
效果很好。但如果我改变:
let url = 'http://localhost:8000/test'
至:
let url = 'http://127.0.0.1:8000/test'
它将失败:
禁止(未设置 CSRF cookie。):/test
根据我的理解,
localhost
和127.0.0.1
应该是同义词。为什么在这种情况下不是这样?
更让我困惑的是,Django 的开发服务器显式运行在
127.0.0.1:8000
上。
注意:我正在使用
django-cors-headers
中间件和以下 CORS/CSRF 设置:
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_WHITELIST = ['http://localhost:3000']
CSRF_TRUSTED_ORIGINS = ['localhost:3000']
问题是 127.0.0.1 和 localhost 被认为是不同的域,至少在我的情况下是这样。因此,当CSRF token从后端发送到前端时,需要进行额外的配置。 在我的
@GetMapping("/csrf")
public CsrfToken csrfToken(CsrfToken token, HttpServletResponse response) {
response.setHeader(HttpHeaders.SET_COOKIE, "XSRF-TOKEN=" + (token == null ? "" : token.getToken()) + "; Path=/; SameSite=Lax; Secure");
return token;
}
添加的重要部分是
SameSite=Lax; Secure
这会起作用,但如果不行的话,请查看同一个站点的无、宽松、严格的文档,看看您真正需要什么
我现在使用samesite=lax,只是因为127.0.0.1和localhost是http而不是https,所以我不能使用samesite=none