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) {...}