Spring Boot 和 OAuth2 CORS

Posted

技术标签:

【中文标题】Spring Boot 和 OAuth2 CORS【英文标题】:Spring Boot and OAuth2 CORS 【发布时间】:2017-05-13 13:13:12 【问题描述】:

当我们使用 axios 客户端从节点服务器访问 api 时,总是会遇到以下错误:

XMLHttpRequest cannot load http://localhost:8089/public/api. Redirect from 'http://localhost:8089/public/api/' to 'http://localhost:8089/' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3002' is therefore not allowed access.

我的配置如下:

import java.security.Principal;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.Filter;

import org.apache.catalina.filters.CorsFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties;
import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoTokenServices;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.oauth2.client.OAuth2ClientContext;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter;
import org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter;
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.cors.CorsUtils;
import org.springframework.web.filter.CompositeFilter;

@SpringBootApplication
@EnableOAuth2Client
@EnableAuthorizationServer
@Order(6)
public class MonitoringApiApplication extends WebSecurityConfigurerAdapter 

    @Autowired
    OAuth2ClientContext oauth2ClientContext;
    @Override
    protected void configure(HttpSecurity http) throws Exception 
        // @formatter:off
        http
                .antMatcher("/**").authorizeRequests().antMatchers("/","/user**", "/login**", "/webjars/**").permitAll().anyRequest()

                .authenticated().and().exceptionHandling()
                .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/")).and().logout()
                .logoutSuccessUrl("/").permitAll().and().csrf().disable()
                .addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class);

        // @formatter:on
    

    @Configuration
    @EnableResourceServer
    protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter 
        @Override
        public void configure(HttpSecurity http) throws Exception 
            // @formatter:off
            http.antMatcher("/me").authorizeRequests().anyRequest().authenticated();
            // @formatter:on
        
    

    public static void main(String[] args) 

        SpringApplication.run(MonitoringApiApplication.class, args);
    

    @Bean
    public FilterRegistrationBean oauth2ClientFilterRegistration(OAuth2ClientContextFilter filter) 
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(filter);
        registration.setOrder(-100);
        return registration;
    

    @Bean
    @ConfigurationProperties("github")
    public ClientResources github() 
        return new ClientResources();
    

    @Bean
    @ConfigurationProperties("facebook")
    public ClientResources facebook() 
        return new ClientResources();
    

    private Filter ssoFilter() 
        CompositeFilter filter = new CompositeFilter();
        List<Filter> filters = new ArrayList<>();
        filters.add(ssoFilter(facebook(), "/login/facebook"));
        filters.add(ssoFilter(github(), "/login/github"));
        filter.setFilters(filters);
        return filter;
    

    private Filter ssoFilter(ClientResources client, String path) 
        OAuth2ClientAuthenticationProcessingFilter oAuth2ClientAuthenticationFilter = new OAuth2ClientAuthenticationProcessingFilter(
                path);
        OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(client.getClient(), oauth2ClientContext);
        oAuth2ClientAuthenticationFilter.setRestTemplate(oAuth2RestTemplate);
        UserInfoTokenServices tokenServices = new UserInfoTokenServices(client.getResource().getUserInfoUri(),
                client.getClient().getClientId());
        tokenServices.setRestTemplate(oAuth2RestTemplate);
        oAuth2ClientAuthenticationFilter.setTokenServices(tokenServices);
        return oAuth2ClientAuthenticationFilter;
    



class ClientResources 

    @NestedConfigurationProperty
    private AuthorizationCodeResourceDetails client = new AuthorizationCodeResourceDetails();

    @NestedConfigurationProperty
    private ResourceServerProperties resource = new ResourceServerProperties();

    public AuthorizationCodeResourceDetails getClient() 
        return client;
    

    public ResourceServerProperties getResource() 
        return resource;
    

过滤器配置:

@Bean
public FilterRegistrationBean  corsFilter() 
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    CorsConfiguration config = new CorsConfiguration();
    config.setAllowCredentials(true);
    config.addAllowedOrigin("http://localhost:3002");
    config.addAllowedHeader("*");
    config.addAllowedMethod("*");
    source.registerCorsConfiguration("/**", config);
    FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
    bean.setOrder(0);
    return bean;

春季开机版本:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.4.0.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

如果我将 /public/** 添加到 antMatchers 它可以工作,这意味着端点不受 ssoFilter() 保护,我已经尝试过将虚假来源的凭据设置为 * 但没有任何工作

【问题讨论】:

可能是***.com/questions/35035055/spring-boot-and-cors?rq=1对你有帮助 【参考方案1】:

对我来说,唯一有效的解决方案来自此链接:

Spring security, cors error when enable Oauth2

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
@WebFilter("/*") //TODO Exclude API endpoints when possible
public class CorsFilter implements Filter 

    public CorsFilter() 
    

    @Override
    public void init(FilterConfig fc) 
    

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException 
        System.out.println("doFilter");
        HttpServletResponse response = (HttpServletResponse) resp;
        HttpServletRequest request = (HttpServletRequest) req;
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
        response.setHeader("Access-Control-Max-Age", "3600");
        response
                .setHeader("Access-Control-Allow-Headers", "Origin, origin, x-requested-with, authorization, " +
                        "Content-Type, Authorization, credential, X-XSRF-TOKEN");

        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) 
            response.setStatus(HttpServletResponse.SC_OK);
         else 
            chain.doFilter(req, resp);
        
    

    @Override
    public void destroy() 
    

我还需要从扩展 WebSecurityConfigurerAdapter 和 ResourceServerConfigurerAdapter 的类的配置方法中删除 http.cors()。

【讨论】:

【参考方案2】:

你需要添加这个http.cors().and().csrf().disable();以及 SecurityConfig 文件中的过滤器。

@Bean
CorsConfigurationSource corsConfigurationSource() 
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.setAllowedOrigins("*");
    configuration.addAllowedHeader("*");
    configuration.addAllowedMethod("*");
    configuration.setAllowCredentials(true);
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
    return source;

您需要最新版本的 spring security 之一才能正常工作。

【讨论】:

在完成所有文档、***、google 示例之后,这是为我启用 CORS 的唯一想法。

以上是关于Spring Boot 和 OAuth2 CORS的主要内容,如果未能解决你的问题,请参考以下文章

带有 Google 的 OAuth2 - CORS 错误(Angular + Spring boot)[重复]

Spring boot Oauth2 + Angular CORS问题

spring boot with react前端oauth2代理CORS问题

如何在 Spring Boot 2.x 中将 CORS 添加到 oauth2/resource 服务器?

如何使用密码授予在 Spring Boot Oauth2 资源服务器中处理 CORS

Spring Boot OAuth2 CORS 在版本 2.2.1 中不起作用