我有一个生产应用程序,它将像这样刷新 csrf 令牌
private static final String CSRF_TOKEN_SETTER = "window.import.meta.env.CSRF_TOKEN=\"%s\";";
// sets initial token from request
@GetMapping(value = "/csrf.js", produces = "application/javascript; charset=utf-8")
public String csrf(HttpServletRequest request) {
if (request.getAttribute("_csrf") instanceof CsrfToken token) {
return String.format(CSRF_TOKEN_SETTER, token.getToken());
}
return "ERROR";
}
// used to work, but no longer does after Spring update
@GetMapping(value = "/refresh_csrf_token")
public ResponseEntity<String> refreshCsrfToken(HttpServletRequest request, HttpServletResponse response) {
CsrfToken newToken = csrfTokenRepository.generateToken(request);
csrfTokenRepository.saveToken(newToken, request, response);
return ResponseEntity.ok(newToken.getToken());
}
FE 基本上是这样做的:
if (statusCodeIs403) {
refreshCsrfTokenAndAppendToHeadersOfFutureRequests();
}
^ 这是因为我们不想在 csrf 令牌过期时出现错误或中断会话。我们只是想刷新它并继续前进。
这在 Spring 2 上有效。我们最近升级到 Spring 3,现在我们的实现似乎出现了问题。我们最初可以设置 csrf 令牌(使用 javascript 文件),但是一旦 csrf 令牌过期并且我们刷新令牌,我们仍然会返回 403。 Spring 2 和 3 之间肯定发生了根本性的变化,但目前还不清楚为什么刷新的 csrf 令牌无法验证。
这是我们的实现
CsrfTokenRepository
@Bean
@Lazy
public CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository tokenRepository = new HttpSessionCsrfTokenRepository();
tokenRepository.setHeaderName("X-CSRF-TOKEN");
return tokenRepository;
}
我们是否缺少保存令牌的步骤?我还注意到初始的有效令牌与刷新的令牌的格式完全不同:
有效代币:
sBkVwbeM-hgCeILgJFU6zADrNZkMpcnkG3i0vzqSMIR6MWis1Xgk9Y-6mCkvT-DWE3gO9WPeGPs4xvrJKUqCjQ7wCLEZVF6Z
刷新代币:
3c78e076-9431-4f70-97a8-0aa8760037a7
查看文档更新的 CSRF 章节中的图表,了解
CsrfFilter
所做工作的概述。您绕过了过滤器,因此无法从它执行的工作中受益。
有 几个 JavaScript 示例,但没有一个与您的自定义完全匹配。我的建议是根据此示例或此示例重新处理您的支持。
具体来说,请注意,您应该将
CsrfToken csrfToken
注入到控制器方法中,而不是直接与 CsrfTokenRepository
交互。例如,您可以这样做:
@GetMapping(value = "/refresh_csrf_token")
public ResponseEntity<String> refreshCsrfToken(CsrfToken csrfToken) {
return ResponseEntity.ok(csrfToken.getToken());
}
(无法在评论中发布代码)
我们最终做了类似的事情
@GetMapping(value = "/refresh_csrf_token")
public ResponseEntity<String> refreshCsrfToken(HttpServletRequest rest) {
if (request.getAttribute("_csrf") instanceof CsrfToken token) {
return ResponseEntity.ok(token.getToken());
}
}
我猜这是等效的,你的建议只是完成从请求中解析出属性的工作?