【问题标题】:Standalone Spring OAuth2 JWT Authorization Server + CORS独立 Spring OAuth2 JWT 授权服务器 + CORS
【发布时间】:2015-08-18 09:03:48
【问题描述】:

所以我从 Dave Syer 的 this example 浓缩了以下授权服务器

@SpringBootApplication
public class AuthserverApplication {

    public static void main(String[] args) {
            SpringApplication.run(AuthserverApplication.class, args);
    }

    /* added later
    @Configuration
    @Order(Ordered.HIGHEST_PRECEDENCE)
    protected static class MyWebSecurity extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http //.csrf().disable() 
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS, "/oauth/token").permitAll();
       }
    }*/

    @Configuration
    @EnableAuthorizationServer
    protected static class OAuth2AuthorizationConfig extends
                    AuthorizationServerConfigurerAdapter {

            @Autowired
            private AuthenticationManager authenticationManager;

            @Bean
            public JwtAccessTokenConverter jwtAccessTokenConverter() {
                    JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
                    KeyPair keyPair = new KeyStoreKeyFactory(
                                    new ClassPathResource("keystore.jks"), "foobar".toCharArray())
                                    .getKeyPair("test");
                    converter.setKeyPair(keyPair);
                    return converter;
            }

            @Override
            public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
                    clients.inMemory()
                                    .withClient("acme")
                                    //.secret("acmesecret")
                                    .authorizedGrantTypes(//"authorization_code", "refresh_token",
                                                    "password").scopes("openid");
            }

            @Override
            public void configure(AuthorizationServerEndpointsConfigurer endpoints)
                            throws Exception {
                    endpoints.authenticationManager(authenticationManager).accessTokenConverter(
                                    jwtAccessTokenConverter());
            }

            @Override
            public void configure(AuthorizationServerSecurityConfigurer oauthServer)
                            throws Exception {
                    oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess(
                                    "isAuthenticated()");
            }
    }
}

当我运行它并用 curl 测试它时

curl acme@localhost:8110/oauth/token -d grant_type=password -d client_id=acme -d username=user -d password=password

我得到一个 JWT 作为响应,但是当我尝试从我的前端(不同端口上的 Angular JS)访问 AuthServer 时,我得到了 CORS 错误。不是因为缺少标头,而是因为 OPTION 请求被拒绝并且缺少凭据。

Request URL:http://localhost:8110/oauth/token
Request Method:OPTIONS
Status Code:401 Unauthorized
WWW-Authenticate:Bearer realm="oauth", error="unauthorized", error_description="Full authentication is required to access this resource"

我已经知道我必须添加一个 CorsFilter 并另外找到 this post ,我在第一个答案中使用了 sn-p 以让 OPTIONS 请求访问 /oauth/token 而无需凭据:

@Order(-1)
public class MyWebSecurity extends WebSecurityConfigurerAdapter {
   @Override
   protected void configure(HttpSecurity http) throws Exception {
       http
          .authorizeRequests()
          .antMatchers(HttpMethod.OPTIONS, "/oauth/token").permitAll();
   }
}

之后我用 curl 得到以下错误:

{"timestamp":1433370068120,"status":403,"error":"Forbidden","message":"Expected CSRF token not found. Has your session expired?","path":"/oauth/token"}

所以为了简单起见,我只是将 http.csrf().disable() 添加到 MyWebSecurity 类的 configure 方法中,这解决了 OPTION 请求的问题,但因此 POST 请求不再工作,我得到 There is no client authentication. Try adding an appropriate authentication filter. (也可以卷曲)。

我试图找出是否必须以某种方式连接 MyWebSecurity 类和 AuthServer,但没有任何运气。原始示例(开头的链接)也注入了 authenticationManager,但这对我来说没有任何改变。

【问题讨论】:

    标签: spring-security cors jwt spring-security-oauth2


    【解决方案1】:

    找到我的问题的原因!

    如果 CorsFilter 处理了 OPTIONS 请求,我只需要结束过滤器链并立即返回结果!

    SimpleCorsFilter.java

    @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() {
        }
    }
    

    之后我可以忽略 AuthServer =D 中的 OPTIONS 预检请求

    所以服务器的工作方式与上面的片段一样,您可以忽略开头带有 MyWebSecurity 类的块注释。

    【讨论】:

    • 作为替代方案,您可以使用 Spring Framework 4.2+ 和 Spring Boot 1.3+ 提供的CorsFilter,更多详细信息请参阅我的answer here
    • @SébastienDeleuze Spring 的 CorsFilter 似乎由于某种原因在 FilterChain 之后执行,但仍然导致未经授权:(
    • 您是否尝试按照here 所述更改过滤器的顺序?
    • 这个春季文档帮助了我:Extend spring web CORSFilter
    • @SébastienDeleuze 我真的很想使用@CrossOrigin 注释,但可以使用 Spring CorsFilter 作为备份。 @CrossOrigin 与我的@ResoruceService 完美搭配,但两个选项都不适用于我的@EnableAuthorizationServer。我总是收到对 Chrome preflight OPTIONS 请求的 401 响应。此答案中描述的自定义过滤器确实有效。我们如何获得 @CrossOrigin 注释和/或 Spring CorsFilter 以实际使用 @EnableAuthorizationServer。我已经尝试了您在此处和其他地方的回复中的所有建议,但均无济于事。
    【解决方案2】:

    我使用该问题的解决方案找到了解决方案。但我有另一种方式来描述解决方案:

    @Configuration
    public class WebSecurityGlobalConfig extends WebSecurityConfigurerAdapter {
          ....
          @Override
          public void configure(WebSecurity web) throws Exception {
            web.ignoring()
              .antMatchers(HttpMethod.OPTIONS);
          }
          ...
    }
    

    【讨论】:

      【解决方案3】:

      我使用以下方法遇到了类似的问题

      • 后台Spring Boot 1.5.8.RELEASE
      • Spring OAuth2 Spring OAuth 2.2.0.RELEASE w
      • Vuejs 应用使用axios ajax 请求库

      postman 一切正常!当我开始从Vuejs 应用程序发出请求时,我收到以下错误

      选项http://localhost:8080/springboot/oauth/token401 ()

      XMLHttpRequest 无法加载 http://localhost:8080/springboot/oauth/token。预检响应包含无效的 HTTP 状态代码 401

      读了一点之后,我发现我可以通过在我的WebSecurityConfigurerAdapter 实现类中覆盖configure 来指示我的Spring OAuth 忽略OPTIONS 请求,如下所示

      @Override
      public void configure(WebSecurity web) throws Exception {
         web.ignoring().antMatchers(HttpMethod.OPTIONS);
      }
      

      添加上述内容有所帮助,但后来我遇到了 CORS 特定错误

      选项http://localhost:8080/springboot/oauth/token403 ()

      XMLHttpRequest 无法加载 http://localhost:8080/springboot/oauth/token。对预检请求的响应未通过访问控制检查:请求的资源上不存在“Access-Control-Allow-Origin”标头。因此,Origin 'http://localhost:8000' 不允许访问。响应的 HTTP 状态代码为 403。

      并在CorsConfig 的帮助下解决了上述问题,如下所示

      @Configuration
      public class CorsConfig {
          @Bean
          public FilterRegistrationBean corsFilterRegistrationBean() {
              UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
              CorsConfiguration config = new CorsConfiguration();
              config.applyPermitDefaultValues();
              config.setAllowCredentials(true);
              config.setAllowedOrigins(Arrays.asList("*"));
              config.setAllowedHeaders(Arrays.asList("*"));
              config.setAllowedMethods(Arrays.asList("*"));
              config.setExposedHeaders(Arrays.asList("content-length"));
              config.setMaxAge(3600L);
              source.registerCorsConfiguration("/**", config);
              FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
              bean.setOrder(0);
              return bean;
          }
      }
      

      添加上述类后,它按预期工作。在我去prod 之前我会研究consequences 的使用

      web.ignoring().antMatchers(HttpMethod.OPTIONS);

      以及best practices 用于上述Cors 配置。现在* 完成了这项工作,但对于生产来说绝对不安全。

      Cyril 的回答帮助了我 partially,然后我在这个 Github 问题中遇到了 CorsConfig 的想法。

      【讨论】:

        【解决方案4】:

        嗯,你是对的!这是一个解决方案,它也对我有用(我有同样的问题)

        但是让我怀疑为 Java 使用更智能的 CORS 过滤器实现: http://software.dzhuvinov.com/cors-filter.html

        这是一个非常完整的 Java 应用解决方案。

        其实你可以看到here你的点是怎么解决的。

        【讨论】:

        • Spring Framework 4.2+ native CorsFilter 可能是更好的选择,see this answer 了解更多详情。
        【解决方案5】:

        在这里使用 Spring Boot 2。

        我必须在我的 AuthorizationServerConfigurerAdapter 中这样做

        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        
            Map<String, CorsConfiguration> corsConfigMap = new HashMap<>();
            CorsConfiguration config = new CorsConfiguration();
            config.setAllowCredentials(true);
            //TODO: Make configurable
            config.setAllowedOrigins(Collections.singletonList("*"));
            config.setAllowedMethods(Collections.singletonList("*"));
            config.setAllowedHeaders(Collections.singletonList("*"));
            corsConfigMap.put("/oauth/token", config);
            endpoints.getFrameworkEndpointHandlerMapping()
                    .setCorsConfigurations(corsConfigMap);
        
            //additional settings...
        }
        

        【讨论】:

          【解决方案6】:
          我尝试了不同的方法来解决这个问题。我会说下面是在我这边解决了这个问题(使用 Spring Boot 2) 1-将以下方法添加到扩展 WebSecurityConfigurerAdapter 的以下方法类中: // CORS 设置 @覆盖 公共无效配置(WebSecurity web)抛出异常{ web.ignoring() .antMatchers(HttpMethod.OPTIONS); } 2-将以下内容添加到我的扩展 AuthorizationServerConfigurerAdapter 的类中
          @Override
          public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
              // enable cors for "/oauth/token"
              Map<String, CorsConfiguration> corsConfigMap = new HashMap<>();
              CorsConfiguration config = new CorsConfiguration();
              config.setAllowCredentials(true);
             
              config.setAllowedOrigins(Collections.singletonList("*"));
              config.setAllowedMethods(Collections.singletonList("*"));
              config.setAllowedHeaders(Collections.singletonList("*"));
              corsConfigMap.put("/oauth/token", config);
              endpoints.getFrameworkEndpointHandlerMapping()
                      .setCorsConfigurations(corsConfigMap);
              // add the other configuration
          }
          

          【讨论】:

            猜你喜欢
            • 2015-03-31
            • 2021-10-29
            • 2015-05-22
            • 1970-01-01
            • 2016-05-21
            • 2014-07-09
            • 2017-08-26
            • 2016-01-18
            相关资源
            最近更新 更多