web-dev-qa-db-ja.com

スタンドアロンのSpring OAuth2 JWT認証サーバー+ CORS

だから私は この例 Dave Syerから凝縮された次の認可サーバーを持っています

_@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を追加する必要があることはすでにわかっていましたが、さらに この投稿 で、最初のアンサーにスニペットを使用して、認証情報なしで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"}
_

簡単にするために、MyWebSecurityクラスのconfigureメソッドにhttp.csrf().disable()を追加しました。これはOPTIONリクエストで問題を解決しますが、POSTリクエストはもう機能しておらず、_There is no client authentication. Try adding an appropriate authentication filter._を取得します(curlとも)。

MyWebSecurityクラスとAuthServerを何らかの方法で接続する必要があるかどうかを調べましたが、運はありませんでした。元の例(冒頭のリンク)はauthenticationManagerも挿入しますが、これは私にとって何も変わりませんでした。

24
Michael K.

私の問題の理由を見つけました!

OPTIONSリクエストがCorsFilterによって処理された場合、フィルターチェーンを終了し、すぐに結果を返す必要がありました!

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クラスでブロックコメントを無視できます。

90
Michael K.

質問の解決策を使用して解決策を見つけました。しかし、ソリューションを説明する別の方法があります:

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

私は次を使用して同様の問題に遭遇しました

  • バックエンド_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/token 401()

そして

XMLHttpRequestはロードできません http:// localhost:8080/springboot/oauth/token 。プリフライトの応答に無効なHTTPステータスコード401が含まれています

少し読んだ後、次のようにOPTIONS実装クラスのconfigureをオーバーライドすることにより、WebSecurityConfigurerAdapterリクエストを無視するように_Spring OAuth_に指示できることがわかりました。

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

上記の追加は助けましたが、その後、CORS特定のエラーに遭遇しました

オプション http:// localhost:8080/springboot/oauth/token 403()

そして

XMLHttpRequestはロードできません http:// localhost:8080/springboot/oauth/token 。プリフライトリクエストへの応答がアクセスコントロールチェックに合格しません。リクエストされたリソースに「Access-Control-Allow-Origin」ヘッダーがありません。したがって、オリジン ' http:// localhost:80 'はアクセスを許可されません。応答には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);

上記のCors構成の_best practices_も同様です。今のところ_*_は仕事をしますが、実稼働環境では安全ではありません。

Cyrilの答えはpartiallyを助けてくれました。そして、この Github の問題でCorsConfigのアイデアに出会いました。

11
Raf

まあ、あなたは正しいです!それは解決策であり、私にとってもうまくいきました(私は同じ問題を抱えていました)

しかし、JavaのよりスマートなCORSフィルター実装を使用するように頑張ってみましょう: http://software.dzhuvinov.com/cors-filter.html

これはJavaアプリケーションの非常に完全なソリューションです。

実際には、 here ポイントの解決方法を見ることができます。

3
Gervasio Amy

ここで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...
}