Spring 3 -- 无法刷新 csrf 令牌

问题描述 投票:0回答:2

我有一个生产应用程序,它将像这样刷新 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

spring spring-boot spring-security csrf spring-3
2个回答
1
投票

查看文档更新的 CSRF 章节中的图表,了解

CsrfFilter
所做工作的概述。您绕过了过滤器,因此无法从它执行的工作中受益。

几个 JavaScript 示例,但没有一个与您的自定义完全匹配。我的建议是根据此示例此示例重新处理您的支持。

具体来说,请注意,您应该将

CsrfToken csrfToken
注入到控制器方法中,而不是直接与
CsrfTokenRepository
交互。例如,您可以这样做:

    @GetMapping(value = "/refresh_csrf_token")
    public ResponseEntity<String> refreshCsrfToken(CsrfToken csrfToken) {
        return ResponseEntity.ok(csrfToken.getToken());
    }

0
投票

(无法在评论中发布代码)

我们最终做了类似的事情


    @GetMapping(value = "/refresh_csrf_token")
    public ResponseEntity<String> refreshCsrfToken(HttpServletRequest rest) {
         if (request.getAttribute("_csrf") instanceof CsrfToken token) {
            return ResponseEntity.ok(token.getToken());
        }
    }

我猜这是等效的,你的建议只是完成从请求中解析出属性的工作?

© www.soinside.com 2019 - 2024. All rights reserved.