如何让 Spring Security 使用 OAUTH2 响应 Pre-Flight CORS 请求
Posted
技术标签:
【中文标题】如何让 Spring Security 使用 OAUTH2 响应 Pre-Flight CORS 请求【英文标题】:How to get Spring Security to respond to Pre-Flight CORS request with OAUTH2 【发布时间】:2016-12-24 10:17:59 【问题描述】:我不是专业的 Java 程序员。我正在为我们的实验室构建一个应用程序。它最初是一个带有 JSP 文件的 Spring MVC 应用程序。它现在已迁移到使用 OAUTH2 作为独立身份验证和授权服务器的 Spring REST API。我正在使用 mysql 数据库来存储我的数据和用户。如果我使用 Postman 访问服务器,我可以成功获得一个 access_token。但是,如果我使用我设置的 Angular 2 客户端,我无法通过客户端发送到服务器的飞行前 OPTIONS 请求。我已经尝试了几种不同的方法来配置 CORS 过滤器,但我无法实现该过滤器,并且我总是得到一个为飞行前请求返回的 401 代码。我已尝试在 Spring 博客 https://spring.io/blog/2015/06/08/cors-support-in-spring-framework 中实施建议的解决方案,但尚未奏效。
在我的 pom.xml 文件中,我使用的是这些版本的 Spring
Spring Framework - 4.3.2.RELEASE
Spring Security - 4.1.2.RELEASE
Spring Security OAUTH2 - 2.0.10.RELEASE
我在尝试使用 FilterRegistrationBean 时还包含 Spring Boot 1.4.0
这是我现在的代码。
WebConfig Class:
@EnableWebMvc
@Configuration
@ComponentScan(basePackages="eng.lab")
@Import(ApplicationContext.class)
@PropertySource("classpath:application.properties")
public class WebConfig extends WebMvcConfigurerAdapter
/* @Override
public void addCorsMappings(CorsRegistry registry)
registry.addMapping("/**");
*/
SecurityConfig 类:
@Configuration
@EnableWebSecurity
@PropertySource("classpath:application.properties")
public class SecurityConfig extends WebSecurityConfigurerAdapter
@Autowired
DataSource dataSource;
@Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery(
"select username,password, enabled from user where username=?")
.authoritiesByUsernameQuery(
"select u.username, r.role from User u, Role r where r.id=u.roleId and u.userName=?");
@Autowired
private ClientDetailsService clientDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception
http
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/oauth/token").permitAll()
//.requestMatchers(CorsUtils::isCorsRequest).permitAll()
.antMatchers("/oauth/token").permitAll();
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception
return super.authenticationManagerBean();
@Bean
public TokenStore tokenStore()
return new InMemoryTokenStore();
@Bean
@Autowired
public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore)
TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
handler.setTokenStore(tokenStore);
handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
handler.setClientDetailsService(clientDetailsService);
return handler;
@Bean
@Autowired
public ApprovalStore approvalStore(TokenStore tokenStore) throws Exception
TokenApprovalStore store = new TokenApprovalStore();
store.setTokenStore(tokenStore);
return store;
MethodSecurityConfig 类:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration
@SuppressWarnings("unused")
@Autowired
private SecurityConfig securityConfig;
@Override
protected MethodSecurityExpressionHandler createExpressionHandler()
return new OAuth2MethodSecurityExpressionHandler();
WebAppInitializer 类:
public class WebAppInitializer implements WebApplicationInitializer
@Override
public void onStartup(ServletContext container) throws ServletException
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(WebConfig.class);
container.addListener(new ContextLoaderListener(rootContext));
DelegatingFilterProxy filter = new DelegatingFilterProxy("springSecurityFilterChain");
DispatcherServlet dispatcherServlet = new DispatcherServlet(rootContext);
ServletRegistration.Dynamic registration = container.addServlet("dispatcherServlet", dispatcherServlet);
container.addFilter("springSecurityFilterChain", filter).addMappingForUrlPatterns(null, false, "/*");
registration.setLoadOnStartup(1);
registration.addMapping("/");
SimpleCorsFilter 类:
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SimpleCorsFilter implements Filter
public SimpleCorsFilter()
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with, authorization");
if ("OPTIONS".equalsIgnoreCase(request.getMethod()))
response.setStatus(HttpServletResponse.SC_OK);
else
chain.doFilter(req, res);
@Override
public void init(FilterConfig filterConfig)
@Override
public void destroy()
如果我包含第二个 WebSecurityConfig 类,我会得到一个 403 代码。
MyWebSecurity 类:
@Configuration
@EnableWebSecurity
@Order(-1)
public class MyWebSecurity extends WebSecurityConfigurerAdapter
@Override
protected void configure(HttpSecurity http) throws Exception
http
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/oauth/token").permitAll();
除了等待修复 DATAREST-573 错误之外,关于如何克服飞行前问题的任何建议?
更新:我尝试了 Benjamin 的建议,但不确定我是否正确实施了解决方案。我已经按如下方式编辑了我的 WebConfig 类,但飞行前仍然失败。过滤器仍未应用。
package eng.lab.config;
import javax.servlet.Filter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@EnableWebMvc
@Configuration
@ComponentScan(basePackages="eng.lab")
@Import(ApplicationContext.class)
@PropertySource("classpath:application.properties")
public class WebConfig extends WebMvcConfigurerAdapter
//more custome rule beans
@Bean
public FilterRegistrationBean simpleCORSFilterRegistration()
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new SimpleCorsFilter());
registration.addUrlPatterns("/*");
//registration.addInitParameter("paramName", "paramValue");
registration.setName("simpleCorsFilter");
registration.setOrder(0);
return registration;
@Bean(name = "simpleCorsFilter")
public Filter simpleCorsFilter()
return new SimpleCorsFilter();
/* @Override
public void addCorsMappings(CorsRegistry registry)
registry.addMapping("/**");
*/
【问题讨论】:
你的 SimpleCorsFilter 是 javax.servlet.Filter 吗? 是的。我使用了这个 import import javax.servlet.Filter; 我认为这个类中的代码没有被执行。你能确认一下吗? 【参考方案1】:您的 CORS 过滤器未注册。由于这是一个标准的 javax.servlet.Filter
实例,您需要针对 FilterRegistrationBean 注册它或在您的 web.xml 文件中声明它。
您可以参考这个 SO 问题了解更多详情:How to add a filter class in Spring Boot?
【讨论】:
以上是关于如何让 Spring Security 使用 OAUTH2 响应 Pre-Flight CORS 请求的主要内容,如果未能解决你的问题,请参考以下文章
如何让 Spring Security 使用 OAUTH2 响应 Pre-Flight CORS 请求
Spring Security:如何让用户打开任何页面的监听器?