启用CSRF时Spring Security登出请求报403错误及疑问
问题说明
根据Spring Security官方文档,登出POST请求需携带CSRF token,但启用CSRF后调用/account/logout接口时出现403错误。通过配置csrf().ignoreAntMatchers("/account/logout")解决了问题,但疑惑为何忽略该路径即可正常工作。
相关配置
SecurityFilterChain 配置
public SecurityFilterChain securityFilterChain( HttpSecurity http, OAuth2UserService<OAuth2UserRequest, OAuth2User> oAuth2UserService, CustomOAuth2SuccessHandler customOAuth2SuccessHandler ) throws Exception { return http .authorizeHttpRequests(auth -> auth .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll() .mvcMatchers( ... "/account/logout" ).permitAll() .mvcMatchers( "/account/login", ... ).hasRole("ANONYMOUS") ... ) .csrf() .ignoringAntMatchers("/account/logout") .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) ... .and() .formLogin().disable() .logout(logout -> logout .logoutUrl("/account/logout") ) .oauth2Login(oAuth -> oAuth .loginPage("/account/login") .userInfoEndpoint(userInfo -> userInfo .userService(oAuth2UserService)) .successHandler(customOAuth2SuccessHandler) ) // 使用JWT token .addFilterBefore( new JwtTokenFilter(userAccountService, jwtProperties), BasicAuthenticationFilter.class) ... }
自定义登出端点
SecurityContextLogoutHandler logoutHandler = new SecurityContextLogoutHandler(); @PostMapping("/account/logout") public String requestLogout(Authentication authentication, HttpServletRequest request, HttpServletResponse response) { this.logoutHandler.logout(request, response, authentication); return "redirect:/"; }
前端代码
<form id="logout" action="/account/logout" method="post"> <button type="submit" class="btn text-success p-0">LogOut</button> </form>
错误截图


问题原因分析
核心问题出在无状态会话配置和CSRF机制的依赖冲突:
- Spring Security的CSRF默认依赖
HttpSession存储token,当你配置SessionCreationPolicy.STATELESS(无状态模式)时,服务器不会创建或维护Session,导致CSRF token无法被存储和读取。 - 你的前端表单没有携带CSRF token(比如Spring Security默认的
_csrf隐藏域),但即使添加了这个隐藏域,后端因为没有Session,也无法验证token的有效性,仍然会返回403。 - 当配置
csrf().ignoreAntMatchers("/account/logout")后,Spring Security会跳过对该接口的CSRF校验,所以请求能正常通过。
补充说明
在使用JWT的无状态应用中,CSRF的常规机制并不适用:
- JWT本身是存储在客户端的凭证,CSRF攻击的前提是攻击者利用用户的服务器端会话凭证,而无状态模式下没有服务器端会话,CSRF风险大大降低。
- 对于登出接口这类风险较低的操作,直接忽略CSRF校验是合理的做法;如果需要严格防护,可以考虑将CSRF token嵌入JWT中,但会增加复杂度,一般没必要。
内容的提问来源于stack exchange,提问作者jadekim
相关产品推荐
相关产品推荐

