我有一个使用Thymeleaf模板和Spring Boot配置的应用程序。同时使用某些端点时,我遇到了无效的CSRF令牌问题。 App将CSRF令牌存储在cookie中。
在其中一个模板上,其中有注销按钮,当我在处不包含任何链接(样式表)和脚本(JS)时,一切运行良好。但是,当我包含any或header时,出现了“对于localhost:/ logout无效的CSRF令牌”的问题。
Spring安全配置:
@Configuration
@EnableWebSecurity
@Slf4j
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.authenticationProvider(authenticationProvider())
.userDetailsService(customUserDetailsService)
.passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf()
.csrfTokenRepository(new CookieCsrfTokenRepository())
// .disable()
.and()
.exceptionHandling()
.accessDeniedPage("/dashboard")
.and()
.httpBasic()
.disable()
.addFilterBefore(customFilter(), OAuth2LoginAuthenticationFilter.class)
.addFilterBefore(tokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/", "/home", "/webjars/**", "/static/**", "/css/**", "/js/**")
.permitAll()
.antMatchers("/",
"/home",
"/webjars/**",
"/error",
"/favicon.ico",
"/**/*.png",
"/**/*.gif",
"/**/*.svg",
"/**/*.jpg",
"/**/*.html",
"/**/*.css",
"/**/*.js")
.permitAll()
.antMatchers("/logon","/login/**", "/oauth2_register/**","/register/**")
.anonymous()
.anyRequest()
.authenticated()
.and()
.formLogin()
.disable()
.logout()
.deleteCookies("access_token")
.permitAll()
.and()
.oauth2Login()
.authorizationEndpoint()
.authorizationRequestRepository(cookieAuthorizationRequestRepository())
.authorizationRequestResolver(customAuthorizationRequestResolver())
.and()
.loginPage("/login")
.userInfoEndpoint()
.userService(customOAuth2UserService)
.and()
.successHandler(oAuth2AuthenticationSuccessHandler)
.failureHandler(oAuth2AuthenticationFailureHandler());
}
}
index.html,我在其中使用Spring Security提供的默认“ / logout”端点:
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title th:text="'Welcome '+${user.getUsername()}+'!'"></title>
<!-- When I include the part with Bootstrap, an Invalid CSRF token error is thrown at Spring -->
<!--Bootstrap-->
<!--<link rel="stylesheet" href="/webjars/bootstrap/css/bootstrap.min.css"/>-->
<!--<link rel="stylesheet" href="/webjars/bootstrap-social/bootstrap-social.css"/>-->
<!--<link rel="stylesheet" href="/webjars/font-awesome/css/all.css"/>-->
<!--<script src="/webjars/jquery/jquery.min.js"></script>-->
<!--<script src="/webjars/popper.js/popper.min.js"></script>-->
<!--<script src="/webjars/bootstrap/js/bootstrap.min.js"></script>-->
<!--<script th:src="@{js/fade.js}"></script>-->
<!--Bootstrap-->
</head>
<body>
<div class="container">
<div class="row justify-content-center">
<div class="alert alert-danger" role="alert" id="backend-validation-alert" th:if="${errorMessage}">
<span th:text="${errorMessage}"></span>
<script>fade();</script>
</div>
</div>
<h1 th:text="'Welcome '+${user.getUsername()}+'!'"></h1>
<form role="form" th:action="@{/logout}" method="POST">
<div><input type="submit" value="Log out"/></div>
</form>
<form role="form" th:action="@{/user/settings/delete}" method="POST">
<div><input type="submit" value="Delete account"/></div>
</form>
</div>
</body>
</html>
当我调试代码并检查CsrfFilter时,我发现在Thymeleaf模板上为POST表单创建了一个不同的_csrf隐藏参数,该参数与cookie上的CSRF不匹配,并引发了异常。过滤器使用正确的XSRF-TOKEN值作为要验证的令牌,但是在请求中作为参数存储的CSRF令牌是错误的并且不同。
有人遇到过类似的问题吗?什么时候应该生成一个新的CSRF令牌?因为在我的情况下,当我检查网络信息时,我发现登录后为每个单个资源(.js,.css,.html)生成了一个新的CSRF令牌,但是只有.html CSRF令牌存储为隐藏参数。在Thymeleaf,这会导致无效的CSRF令牌错误。
其他说明:我已经观察到,首先会加载初始html,并且会在HTML隐藏输入和cookie上设置正确的CSRF表单参数。但是随后,JS和CSS等文件进入CSRF身份验证,并导致cookie CSRF令牌更改。我想解决方案将以某种方式包括从身份验证中排除这些标头脚本和链接。