Spring Security+OAuth2 REST API预检请求跨域校验失败问题排查
解决Spring Security OAuth2 4.x版本AJAX跨域CORS问题
兄弟,我之前在使用Spring Security OAuth2 2.0.x版本的时候也踩过这个CORS的坑——明明配置了Spring MVC的CORS,但AJAX调用/oauth/token或者带token的API还是报跨域错误。问题出在OAuth2的核心端点(比如/oauth/token)并不受普通Spring MVC的CORS规则管控,而且请求会先经过Spring Security的过滤器链,普通CORS过滤器如果顺序不对,根本没机会添加响应头。
下面给你针对这个版本的具体解决方案:
1. 自定义CORS过滤器并调整执行顺序
首先要写一个全局的CORS过滤器,并且让它在Spring Security的过滤器之前执行,这样预检请求(OPTIONS)和实际请求都能先拿到CORS响应头。
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; public class CorsFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { response.setHeader("Access-Control-Allow-Origin", "*"); // 生产环境建议指定具体域名 response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type, Accept, X-Requested-With, remember-me"); // 处理预检OPTIONS请求,直接返回200 if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { response.setStatus(HttpServletResponse.SC_OK); } else { filterChain.doFilter(request, response); } } }
然后在Spring配置里注册这个过滤器,并且设置优先级高于Spring Security:
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; @Configuration public class CorsConfig { @Bean public FilterRegistrationBean<CorsFilter> corsFilter() { FilterRegistrationBean<CorsFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new CorsFilter()); registrationBean.addUrlPatterns("/*"); // 对所有URL生效 registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE); // 优先级最高,在Security过滤器之前执行 return registrationBean; } }
2. 调整Spring Security配置,允许CORS和OPTIONS请求
在你的WebSecurityConfigurerAdapter配置里,要明确允许OPTIONS请求通过,并且开启CORS支持:
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.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.web.header.writers.StaticHeadersWriter; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() // 如果是纯API服务,可关闭CSRF .authorizeRequests() .antMatchers("/oauth/token", "/oauth/authorize").permitAll() // 放开OAuth2端点 .anyRequest().authenticated() .and() .cors() // 开启Spring Security的CORS支持 .and() .headers() .addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-Origin", "*")); // 双重保障,也可以在这里加 } }
3. 配置AuthorizationServer允许OPTIONS请求
在你的AuthorizationServerConfigurerAdapter里,要确保/oauth/token端点接受OPTIONS请求,因为预检请求会先发送OPTIONS到这个端点:
import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; @Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security .tokenKeyAccess("permitAll()") .checkTokenAccess("isAuthenticated()") .allowFormAuthenticationForClients(); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { // 这里配置你的客户端信息,比如clientId、secret、授权类型等 clients.inMemory() .withClient("your-client-id") .secret("your-client-secret") .authorizedGrantTypes("password", "refresh_token") .scopes("read", "write"); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { // 确保端点支持OPTIONS请求 endpoints.getFrameworkEndpointHandlerMapping().setAllowOptions(true); } }
最后检查点
- 确保你的AJAX请求里没有带自定义的未被允许的请求头,如果有,要在
CorsFilter的Access-Control-Allow-Headers里加上。 - 生产环境不要用
*作为Access-Control-Allow-Origin,要指定具体的前端域名,比如http://localhost:3000。 - 清除浏览器缓存,有时候旧的CORS缓存会导致问题。
按照上面的配置调整后,AJAX请求应该就能正常获取token并调用API了。
内容的提问来源于stack exchange,提问作者Rama
相关产品推荐
相关产品推荐

