【问题标题】:Spring security, cors error when enable Oauth2Spring security,启用Oauth2时出现cors错误
【发布时间】:2017-11-21 08:53:17
【问题描述】:

我在查询我的 oauth/token 端点时遇到错误。

我已经为我的资源配置了 cors enable / 还尝试允许所有资源但没有任何效果。

XMLHttpRequest 无法加载 http://localhost:8080/oauth/token。回复 预检请求未通过访问控制检查:否 请求中存在“Access-Control-Allow-Origin”标头 资源。因此不允许使用原点“http://localhost:1111” 使用权。响应的 HTTP 状态代码为 401。

vendor.js:1837 ERROR SyntaxError: Unexpected token u in JSON at position 0
    at JSON.parse (<anonymous>)
    at CatchSubscriber.selector (app.js:7000)
    at CatchSubscriber.error (vendor.js:36672)
    at MapSubscriber.Subscriber._error (vendor.js:282)
    at MapSubscriber.Subscriber.error (vendor.js:256)
    at XMLHttpRequest.onError (vendor.js:25571)
    at ZoneDelegate.invokeTask (polyfills.js:15307)
    at Object.onInvokeTask (vendor.js:4893)
    at ZoneDelegate.invokeTask (polyfills.js:15306)
    at Zone.runTask (polyfills.js:15074)
defaultErrorLogger @ vendor.js:1837
ErrorHandler.handleError @ vendor.js:1897
next @ vendor.js:5531
schedulerFn @ vendor.js:4604
SafeSubscriber.__tryOrUnsub @ vendor.js:392
SafeSubscriber.next @ vendor.js:339
Subscriber._next @ vendor.js:279
Subscriber.next @ vendor.js:243
Subject.next @ vendor.js:14989
EventEmitter.emit @ vendor.js:4590
NgZone.triggerError @ vendor.js:4962
onHandleError @ vendor.js:4923
ZoneDelegate.handleError @ polyfills.js:15278
Zone.runTask @ polyfills.js:15077
ZoneTask.invoke @ polyfills.js:15369

有了 Postman,一切都完美无缺。

我的 cors 安全配置:

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedHeaders("*")
                .allowedMethods("*")
                .allowCredentials(true);
    }
}

还尝试在允许的来源中添加http://localhost:1111

Postman 中的代码:

require 'uri'
require 'net/http'

url = URI("http://localhost:8080/oauth/token")

http = Net::HTTP.new(url.host, url.port)

request = Net::HTTP::Post.new(url)
request["content-type"] = 'application/x-www-form-urlencoded'
request["authorization"] = 'Basic Y2hhdHRpbzpzZWNyZXRzZWNyZXQ='
request["cache-control"] = 'no-cache'
request["postman-token"] = 'daf213da-e231-a074-02dc-795a149a3bb2'
request.body = "grant_type=password&username=yevhen%40gmail.com&password=qwerty"

response = http.request(request)
puts response.read_body

【问题讨论】:

    标签: spring spring-mvc cors preflight


    【解决方案1】:

    经过很多努力,我已经覆盖了类 WebSecurityConfigurerAdapter 的方法 configure(WebSecurity web) 因为授权服务器自己配置它,我只是还没有找到另一个解决方案。您还需要 permitAll "/oauth/token" Http.Options 方法。我的方法:

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers(HttpMethod.OPTIONS, "/oauth/token");
    }
    

    在此之后我们需要添加 cors 过滤器以将 Http 状态设置为 OK。我们现在可以拦截 Http.Options 方法了。

    @Component
    @Order(Ordered.HIGHEST_PRECEDENCE)
    @WebFilter("/*")
    public class CorsFilter implements Filter {
    
        public CorsFilter() {
        }
    
        @Override
        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
            final HttpServletResponse response = (HttpServletResponse) res;
            response.setHeader("Access-Control-Allow-Origin", "*");
            response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
            response.setHeader("Access-Control-Allow-Headers", "x-requested-with, authorization");
            response.setHeader("Access-Control-Max-Age", "3600");
            if ("OPTIONS".equalsIgnoreCase(((HttpServletRequest) req).getMethod())) {
                response.setStatus(HttpServletResponse.SC_OK);
            } else {
                chain.doFilter(req, res);
            }
        }
    
        @Override
        public void destroy() {
        }
    
        @Override
        public void init(FilterConfig config) throws ServletException {
        }
    }
    

    【讨论】:

    • /oauth/token 本身使用基本身份验证进行保护。客户端 ID 和机密用于此目的。为什么要删除该身份验证?这破坏了 OAuth2 提供的安全性
    • 谢谢你!它拯救了我的一天。
    • 这是唯一能让我在同一个项目中使用 OAuth2 身份验证服务器和资源服务器设置 CORS 的配置。
    【解决方案2】:

    我找到了一种方法来修复 Spring Security 5 和 Spring Security OAuth 2.3.5 上的 401 错误,而无需关闭令牌端点上所有 OPTIONS 请求的安全性。 我意识到您可以通过AuthorizationServerSecurityConfigurer 向令牌端点添加安全过滤器。我尝试添加 CorsFilter 并且它有效。这种方法的唯一问题是我无法利用 Spring MVC 的CorsRegistry。如果有人知道如何使用 CorsRegistry,请告诉我。

    我在下面为我的解决方案复制了一个示例配置:

    import org.springframework.context.annotation.Configuration;
    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.AuthorizationServerSecurityConfigurer;
    import org.springframework.web.cors.CorsConfiguration;
    import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
    import org.springframework.web.filter.CorsFilter;
    
    @Configuration
    @EnableAuthorizationServer
    public static class AuthServerConfiguration extends AuthorizationServerConfigurerAdapter {
        //... other config
    
        @Override
        public void configure(AuthorizationServerSecurityConfigurer security) {
            //... other config
    
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
            CorsConfiguration config = new CorsConfiguration();
            config.applyPermitDefaultValues();
    
            // Maybe there's a way to use config from AuthorizationServerEndpointsConfigurer endpoints?
            source.registerCorsConfiguration("/oauth/token", config);
            CorsFilter filter = new CorsFilter(source);
            security.addTokenEndpointAuthenticationFilter(filter);
        }
    }
    
    

    【讨论】:

    • 太棒了!这是我过去 4 小时一直在寻找的解决方案!
    【解决方案3】:

    这对我有用

    @Configuration
    @EnableAuthorizationServer
    public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    
        @Override
        public void configure(AuthorizationServerSecurityConfigurer security) throws Exception 
          {
            security.tokenKeyAccess("permitAll()")
            .checkTokenAccess("isAuthenticated()");
    
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
            CorsConfiguration config = new CorsConfiguration();
            config.applyPermitDefaultValues();
    
            // add allow-origin to the headers
            config.addAllowedHeader("access-control-allow-origin");
    
            source.registerCorsConfiguration("/oauth/token", config);
            CorsFilter filter = new CorsFilter(source);
            security.addTokenEndpointAuthenticationFilter(filter);
    
        }
    
    }
    

    【讨论】:

      【解决方案4】:

      您可以扩展 AuthorizationServerSecurityConfiguration 并覆盖 void configure(HttpSecurity http) 方法以实现自定义 cors 配置,而其余部分保持不变。

      这是一个例子:

      import org.springframework.security.config.annotation.web.builders.HttpSecurity;
      import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerSecurityConfiguration;
      import org.springframework.web.cors.CorsConfiguration;
      
      public class MyAuthorizationServerSecurityConfiguration extends AuthorizationServerSecurityConfiguration {
      
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              super.configure(http);
      
              http.cors(httpSecurityCorsConfigurer -> httpSecurityCorsConfigurer.configurationSource(request -> {
                  CorsConfiguration configuration = new CorsConfiguration();
                  configuration.addAllowedMethod("POST");
                  configuration.addAllowedHeader("Content-Type");
      
                  return configuration;
              }));
          }
      }
      

      然后,您可以自己导入相关的类,而不是使用默认的注解@EnableAuthorizationServer 来引入默认的配置类:

      @Import({AuthorizationServerEndpointsConfiguration.class, MyAuthorizationServerSecurityConfiguration.class})
      

      无需更改与 OPTIONS 方法和/或特定 oauth 路径相关的任何安全配置。

      【讨论】:

        【解决方案5】:

        我在使用 XMLHttpRequest 发送 POST /logout 请求时遇到了 CORS 错误(Keycloak 和 Spring Cloud OidcClientInitiatedServerLogoutSuccessHandler),所以我改用 HTML 表单:

         <form action="/logout" method="post">
            <button>Logout</button>
         </form>
        

        它可以正常工作,并且不需要 CORS 配置。

        【讨论】:

          猜你喜欢
          • 2021-05-09
          • 2016-04-20
          • 1970-01-01
          • 2017-11-09
          • 2021-06-29
          • 2015-04-04
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多