Spring SecurityでCors設定をし、ReactからRestAPIを叩くときの対処

##Spring Bootアプリケーションで作成したRest APIをReactから叩いたときのCorsエラー

サーバーサイドをSpring Boot、クライアントサイドをReactで作成しました。サーバーサイドでホストしているRest APIをReactからリクエストするだけの簡単なアプリケーションだったのですが、ベーシック認証を入れたとたんCORSエラーで引っかかりました。

```
Access to XMLHttpRequest at 'http://localhost:8080/api/v1/memo/all' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
```

ただ、解決するまでにかなり時間がかかったので、解決できた際の設定をメモがてら残しておこうと思います。

##Spring Boot側の設定
Spring Security用のConfigクラスを宣言して、そこへCors設定用のクラスを用意します。

```SecurityConfig.java
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	 @Bean
	    public PasswordEncoder passwordEncoder() {
	        return new BCryptPasswordEncoder();
	    }

	 @Bean
	 CorsConfigurationSource corsConfigurationSource() {
	        CorsConfiguration configuration = new CorsConfiguration();

	        configuration.setAllowedOrigins(Collections.singletonList("http://localhost:3000"));

	        configuration.setAllowedHeaders(List.of("*"));
	        configuration.setAllowedMethods(Arrays.asList("GET","POST", "OPTIONS"));
	        configuration.setAllowCredentials(true);

	        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
	        source.registerCorsConfiguration("/**", configuration);

	        return source;
	    }



	 @Autowired
	    private DataSource dataSource;

	    private static final String USER_SQL = "SELECT文";
	    private static final String ROLE_SQL = "SELECT文";

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

			http.cors().and()
             .csrf().disable()
             .authorizeRequests()
             .antMatchers(HttpMethod.OPTIONS).permitAll()
             .antMatchers(HttpMethod.GET, "/api/*").hasAnyRole("ADMIN")
             .anyRequest().authenticated()
             .and()
             .httpBasic();
	    }

	    @Override
	    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

	        auth.jdbcAuthentication()
	            .dataSource(dataSource)
	            .usersByUsernameQuery(USER_SQL)
	            .authoritiesByUsernameQuery(ROLE_SQL)
	            .passwordEncoder(passwordEncoder());

	    }
}

```

`corsConfigurationSource`メソッドで、アクセス許可するオリジンURLやメソッドの種類を設定するだけです。

```App.js
useEffect(() => {
  const getData = () => {
    var xhr = new XMLHttpRequest();

    xhr.onload = function() {
        console.log('Success: ' + xhr.responseText);
    };
    
    xhr.onerror = function() {
        console.log('Error: '  + xhr.responseText);
    };
    
    xhr.open('GET', 'http://localhost:8080/api/v1/memo/all', true);
    xhr.withCredentials = true;
    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
    xhr.setRequestHeader('Authorization',  'Basic '+ btoa('system:password'));
    xhr.send();
  };
     getData();
    
  }, []);
```

また、クライアントサイドから送られてきたAuth情報をコンソールなどで見たい場合は、Controllerからリクエストヘッダーで設定できます。

```Controller.java
@GetMapping("/api/v1/memo/all")
	public List<Memo> getAllMemos(@RequestHeader("Authorization") String authorization) {...}
```