SpringBoot 2.7中CSRF Token配置异常:POST请求返回403求助
SpringBoot 2.7 + Spring Security 5.8 CSRF Token配置问题:POST请求返回403 Forbidden
我们正在构建基于SpringBoot 2.7的应用,部署在AWS ECS集群中,搭配Spring Security 5.8。目前遇到CSRF Token配置问题:用户POST操作返回403 Forbidden,仅禁用CSRF时操作才能正常执行。
补充排查:调试发现文件上传的POST请求中没有XSRF Token响应头。
SpringBoot依赖
implementation("org.springframework.boot:spring-boot-starter-web:2.7.12") implementation("org.springframework.boot:spring-boot-starter-security:2.7.12") implementation("io.jsonwebtoken:jjwt:0.9.1") implementation("com.jcraft:jsch:0.1.54") implementation("software.amazon.awssdk:secretsmanager:2.20.79") implementation("org.springframework.security:spring-security-web:5.8.0") implementation("org.springframework.security:spring-security-config:5.8.0") implementation("org.springframework.security:spring-security-core:5.8.0") implementation("com.sun.xml.bind:jaxb-core:2.3.0.1") implementation("javax.xml.bind:jaxb-api:2.3.1") implementation("com.sun.xml.bind:jaxb-impl:2.3.1")
JWT过滤器
package com.place.sftp.portal.security; import com.place.sftp.portal.constant.Path; import com.place.sftp.portal.model.SecretHolder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.csrf.CsrfToken; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Component public class JwtFilter extends OncePerRequestFilter { private static final String AUTHORIZATION = "Authorization"; private static final String BEARER = "Bearer "; private static final String OPTIONS = "OPTIONS"; @Autowired private SecretHolder secretHolder; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException { if( Path.AUTHENTICATE_FULL.equals(request.getRequestURI()) || Path.IS_CONNECTED_FULL.equals(request.getRequestURI()) || OPTIONS.equals(request.getMethod())) { logger.info("start: doFilterInternal"); CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName()); response.setHeader(csrfToken.getHeaderName(), csrfToken.getToken()); filterChain.doFilter(request, response); } else { logger.info("start: processJWT"); processJWT(request, response, filterChain); } } private void processJWT(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException { logger.info("Processing JWT"); String authorizationHeader = request.getHeader(AUTHORIZATION); if(validAuthHeader(authorizationHeader)) { String token = authorizationHeader.substring(7); try { validateJWT(token, filterChain, request, response); logger.info("JWT validated successfully"); } catch (Exception e) { logger.error("error in if ",e); response.sendError(HttpServletResponse.SC_UNAUTHORIZED); } } else { logger.info("Error in else"); response.sendError(HttpServletResponse.SC_UNAUTHORIZED); } } private boolean validAuthHeader(String authorizationHeader) { return authorizationHeader != null && authorizationHeader.startsWith(BEARER); } private void validateJWT(String token, FilterChain filterChain, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { logger.info("starting: JWT Validation"); logger.info("token is"); logger.info(token); logger.info("secret is"); //logger.info(secretHolder.getSecret()); UsernamePasswordAuthenticationToken auth = SECRET_VALUE_HERE logger.info("got auth"); SecurityContextHolder.getContext().setAuthentication(auth); logger.info("set security context successfully"); CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName()); logger.info("ending: JWT Validation"); response.setHeader(csrfToken.getHeaderName(), csrfToken.getToken()); filterChain.doFilter(request, response); } }
安全配置(Security Config)
package com.place.sftp.portal.security; import com.place.sftp.portal.constant.Path; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import org.springframework.security.web.csrf.CookieCsrfTokenRepository; import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler; import org.springframework.security.web.savedrequest.HttpSessionRequestCache; import org.springframework.security.web.savedrequest.RequestCache; import org.springframework.security.web.savedrequest.SimpleSavedRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Configuration @EnableWebSecurity public class SecurityConfig { private final Logger logger = LogManager.getLogger(SecurityConfig.class); @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { logger.info("Configure CSRF Token."); http .authorizeHttpRequests((authz) -> authz.requestMatchers("/", "/index.html", "/static/**", "/*.ico", "/*.json", "/*.png", "/api/isconnected", "/api/listchildnodes","/api/authenticate","/api/upload").permitAll() .anyRequest().authenticated() ) .csrf((csrf) -> csrf .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) .csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler()) ) .addFilterAfter(new JwtFilter(), BasicAuthenticationFilter.class); logger.info("Authorize CSRF Token."); return http.build(); } @Bean public RequestCache refererRequestCache() { return new HttpSessionRequestCache() { @Override public void saveRequest(HttpServletRequest request, HttpServletResponse response) { String referrer = request.getHeader("referer"); if (referrer == null) { referrer = request.getRequestURL().toString(); } request.getSession().setAttribute("SPRING_SECURITY_SAVED_REQUEST", new SimpleSavedRequest(referrer)); } }; } }
前端POST请求代码
const UploadModal = ({ open, handleClose }: UploadModalProps) => { const [fileData, setFileData] = useState<File | null>(null); const folderPath = ""; const [cookies] = useCookies(["XSRF-TOKEN"]); const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => { if (event.target.files) { setFileData(event.target.files[0]); } }; const handleFileUpload = async () => { if (fileData) { const formData: FormData = new FormData(); formData.append("file", fileData); formData.append("folderPath", folderPath); try { const response = await axios.post( `${process.env.API_URL}/api/upload`, formData, { withCredentials: true, headers: { "X-XSRF-TOKEN": cookies["XSRF-TOKEN"] } } ); if (response) { toast.success( "File Upload Successfully", successToastOptions ); setFileData(null); } else { toast.error("File Upload Failed", errorToastOptions); } } catch (error) { toast.error("File Upload Failed", errorToastOptions); } } };
内容的提问来源于stack exchange,提问作者Markus
相关产品推荐
相关产品推荐

