我有一个 React 前端应用程序,它可以对 Spring boot 应用程序进行 API 调用。
两者都位于单独的 docker 容器中。当它在本地运行时,一切都很好,但是在部署后,当进行调用时,我收到 CORS 错误:
Access to fetch at 'http://xx.xx.xxx.xx:8080/simulations/1/ratings/1' from origin 'http://xx.xx.xxx.xx:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
我不知道为什么会这样。在我看来,我已经编写了所有内容以允许 CORS 访问(正如我所说,它在本地工作,所以我不明白为什么它在部署后失败。我已经阅读了一些有关 docker 和 localhost 的内容,但它看起来很混乱此刻对我来说。
这是我的 Java 安全代码
WebSecurityConfigurerAdapter
:
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://xx.xx.xxx.xx:3000"));
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
docker-compose.yml
如下:
version: '3'
services:
application-server:
restart: always
image: 'myhub/app-server:cors'
expose:
- '8080'
ports:
- '8080:8080'
networks:
- local
application-client:
restart: always
image: 'myhub/app-client:cors'
ports:
- '3000:80'
stdin_open: true
depends_on:
- application-server
networks:
- local
networks:
local:
driver: bridge
最后是
nginx.conf
:
http
{
server_tokens off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
upstream backend
{
server xx.xx.xxx.xx:8080;
}
server
{
listen 80;
server_name localhost;
access_log /var/log/nginx/host.access.log;
error_log /var/log/nginx/host.error.log;
root /var/www;
index index.html index.htm;
location ~* \.(?:manifest|appcache|html?|xml|json)$
{
expires -1;
}
location ~* \.(?:css|js)$
{
try_files $uri =404;
expires 1y;
access_log off;
add_header Cache-Control "public";
}
# Any route containing a file extension (e.g. /devicesfile.js)
location ~ ^.+\..+$
{
try_files $uri =404;
}
location /
{
root /var/www;
index index.html;
autoindex on;
set $fallback_file /index.html;
if ($http_accept !~ text/html)
{
set $fallback_file /null;
}
if ($uri ~ /$)
{
set $fallback_file /null;
}
try_files $uri $fallback_file;
if ($request_method = 'OPTIONS')
{
add_header 'Access-Control-Allow-Origin: $http_origin');
add_header 'Access-Control-Allow-Origin: GET, POST, DELETE, PUT, PATCH, OPTIONS');
add_header 'Access-Control-Allow-Credentials: true');
add_header 'Vary: Origin');
}
add_header 'Access-Control-Allow-Origin' "$http_origin" always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With' always;
}
}
}
我需要对容器做什么才能允许 CORS 访问?
首先:要从 CORS 中受益,您不需要关注 http 客户端配置。
只有回答以下问题的后端才允许授权:该主机、端口、请求是否可接受?
与 CSRF 相反,CSRF 需要配置客户端和服务器。
我提到,因为你暴露了你的客户配置。
无论如何,在 Spring Boot(当然是后端)中,我们通过
WebSecurityConfigurerAdapter.configure(HttpSecurity http)
方法以声明方式启用 CORS,如下所示:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors()...
}
cors()
javadoc 指出:
添加要使用的CorsFilter。如果名称为 corsFilter 的 bean 是 前提是使用 CorsFilter。否则如果 corsConfigurationSource 是 定义,然后使用 CorsConfiguration。
首先,检查一下。
那么你有三个选择:
– 不做任何额外的事情。在这种情况下,Spring security 将使用 Spring security 提供的默认类以及关联的默认 CorsConfigurationSource bean。
– 声明一个名为
corsFilter
的 bean。在这种情况下,Spring security 将使用它而不是默认值。corsConfigurationSource
的 bean。在这种情况下,Spring 将使用默认的 CorsFilter 和应用程序声明的自定义 corsConfigurationSource
。
你用的是最后一种方式。
如果这还不够,您的
WebSecurity
配置中可能有某些内容会阻止 CORS 过滤器在正确的时间处理。logging.level.org.springframework.security=DEBUG
我几个月前写了那篇文章。也许它可以帮助你。
我能够通过在我的 WebSecurityConfig 类中执行以下操作来解决相同的问题:
(请注意,为了简单起见,我删除了部分代码,并且我的 Spring 应用程序仅作为 REST API 使用。)
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
@DependsOn("authManager")
SecurityFilterChain filterChain(HttpSecurity http,
@Qualifier("authManager") AuthenticationManager authManager) throws Exception {
http.cors(cors -> cors.configurationSource(corsConfigurationSource()));
// code to config authorizeHttpRequests
http.httpBasic(AbstractHttpConfigurer::disable)
.formLogin(AbstractHttpConfigurer::disable)
.csrf(AbstractHttpConfigurer::disable);
// code to set sessionManagement
return http.build();
}
CorsConfigurationSource corsConfigurationSource() {
var configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("<YOUR_URL>"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE"));
configuration.setAllowedHeaders(Arrays.asList("*"));
var source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
希望有帮助。