【Java】Spring SecurityにCORS設定を追加する

2024年1月13日

Spring SecurityにCORS設定を追加

以下の記事で作成したSpring Securityを使用しWeb APIのリクエストごとにトークンで認証する処理にCORS設定を追加します。

下記のコードの中でCORS設定に関連するのはコメントを入れた箇所です。

まず、configureメソッド内でcorsメソッドの呼び出しを追加します。このメソッドは引数無しで呼び出すと「corsConfigurationSource」という名前のBean定義からCORS設定を読み込みます。

次に、「corsConfigurationSource」という名前でCorsConfigurationSourceクラスのインスタンスのBean定義を作成します。CorsConfigurationクラスのインスタンスを作成し、CORSに関連するヘッダの値を設定します。

UrlBasedCorsConfigurationSourceクラスのインスタンスのregisterCorsConfigurationメソッドの第一引数にCOSR設定を行うパス、第二引数に作成したCORS設定指定します。

@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity()
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .addFilter(preAuthenticatedProcessingFilter())
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .csrf().disable()
                .cors();  // CORS設定をcorsConfigurationSource()から読み込む
    }

    @Bean
    public AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> authenticationUserDetailsService() {

        return new MyAuthenticationUserDetailService();
    }

    @Bean
    public PreAuthenticatedAuthenticationProvider preAuthenticatedAuthenticationProvider() {

        var preAuthenticatedAuthenticationProvider = new PreAuthenticatedAuthenticationProvider();
        preAuthenticatedAuthenticationProvider.setPreAuthenticatedUserDetailsService(authenticationUserDetailsService());
        preAuthenticatedAuthenticationProvider.setUserDetailsChecker(new AccountStatusUserDetailsChecker());

        return preAuthenticatedAuthenticationProvider;
    }

    @Bean
    public AbstractPreAuthenticatedProcessingFilter preAuthenticatedProcessingFilter() throws Exception {

        var preAuthenticatedProcessingFilter = new MyPreAuthenticatedProcessingFilter();
        preAuthenticatedProcessingFilter.setAuthenticationManager(authenticationManager());

        return preAuthenticatedProcessingFilter;
    }

    // CORS設定を行うBean定義
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        var configuration = new CorsConfiguration();

        // Access-Control-Allow-Origin
        configuration.setAllowedOrigins(List.of("*"));
 
        // Access-Control-Allow-Methods
        configuration.setAllowedMethods(List.of("*"));

        // Access-Control-Allow-Headers
        configuration.setAllowedHeaders(List.of("*"));

        // Access-Control-Allow-Credentials
        configuration.setAllowCredentials(true);

        var source = new UrlBasedCorsConfigurationSource();

       // COSR設定を行う範囲のパスを指定する。この例では全てのパスに対して設定が有効になる
        source.registerCorsConfiguration("/**", configuration);

        return source;
    }
}

参考サイト

動作検証

動作検証のためにボタンをクリックするとGETリクエストを投げるだけのHTMLファイルを作成しました。このファイルをhttp-serverでlocalhost上に立てたサーバに配置実しブラウザからアクセスします。

この記事の例ではhttp-serverは8080ポートで起動しています。

<html>
    <head>
        <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
        <script>
            async function sendGetRequest() {
                try {
                    const config = {
                        headers : {
                            Authorization: "key1"
                        }
                    };
                    const params = {};
                    const data = await axios.get("http://localhost:8080/items/1", config);
                    console.log(data);
                } catch (e) {
                    console.log(e);
                }
            }
        </script>
    </head>
    <body>
        <button onclick="sendGetRequest()">Send GET Request</button>
    </body>
</html>

CORSアクセスが成功する場合

上記のJavaコードでconfiguration.setAllowedOrigins(List.of(“*"));とした場合はAPIへのリクエストが成功しレスポンスを受け取ることができます。

今回API側はAuthorizationヘッダの値により認証を行っています。リクエストヘッダにAuthorizationヘッダをつけるため、プリフライトリクエストが送信されます。 プリフライトリクエストはGETなどの実際のリクエストを送信する前にOPTIONSメソッドによるリクエストを送信しリクエストの安全性を確かめます。

プリフライトリクエストの詳細はオリジン間リソース共有 (CORS)プリフライトリクエストを参照してください。

プリフライトリクエストの成功
プリフライトリクエストが成功する
GETリクエストの成功
GETリクエストが成功する

CORSアクセスが失敗する場合

上記のJavaコードでconfiguration.setAllowedOrigins(List.of(“http://localhost:9000"));とした場合はAPI側でアクセスを許可したオリジンとリクエスト元が異なるためAPIへのリクエストが失敗します。

プリフライトリクエストが403エラーとなります。

プリフライトリクエストが失敗する

プリフライトリクエストがエラーとなるため、GETリクエストは送信されません。

GETリクエストは送信されない