Java SpringBoot:CORS缺少允许原点错误,但CORS配置已实际定义

np8igboo  于 2022-10-23  发布在  Java
关注(0)|答案(1)|浏览(260)

我正在尝试使用Java SpringBoot设置一个简单的WebSocket服务器,虽然我已经定义了CORS配置(以及SockJS的STOP)以允许从本地主机:8080(以及之前的星号)进行起源,但我仍然看到标题中缺少‘Access-Control-Allow-Origin’,并且我得到了403状态代码错误。
在客户端,我有一个简单的SockJS设置,通常通过普通的HTML和JS连接到WebSocket。以下是我的Java代码(请注意,我曾多次尝试定义CORS允许起源)……
WebSocket Broker配置:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer {
  @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
      registry.enableSimpleBroker("/topic");
      registry.setApplicationDestinationPrefixes("/app");
    }

  @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
      registry.addEndpoint("/stomp")
        .setAllowedOrigins("http://localhost:8080")
        .setAllowedOriginPatterns("*")
        .withSockJS();
    }
}

和另一个类,我在其中定义了SpringBoot的CORS配置和安全规则(由于目前没有更好的名称,我将这个类称为WebSocketSecurity):

@Configuration
@EnableWebSecurity
public class WebSocketSecurity {
  private CorsConfiguration corsConfiguration() {
    CorsConfiguration corsConfig = new CorsConfiguration();
    corsConfig.addAllowedOrigin("http://localhost:8080");
    corsConfig.addAllowedHeader("*");
    corsConfig.addAllowedMethod(HttpMethod.GET);
    corsConfig.addAllowedMethod(HttpMethod.POST);
    corsConfig.applyPermitDefaultValues();
    return corsConfig;
  }

  @Bean
  public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.cors().configurationSource(request -> corsConfiguration());
    http.headers().frameOptions().sameOrigin();
    return http.build();
  }

  public WebSecurityCustomizer webSecurityCustomizer() {
    return (web) -> web.ignoring().antMatchers("/images/**", "/js/**");
  }

  @Bean
  public WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurer() {
      @Override
      public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
          .allowedOrigins("http://localhost:8080")
          .allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "HEAD")
          .allowedHeaders("*");
          //.allowCredentials(true); // this is disabled if CORS allow origin is set to "*"
      }
    };
  }

  @Bean
  public CookieSameSiteSupplier cookieSameSiteSupplier(){
    return CookieSameSiteSupplier.ofNone();
  }
}

我的pom.xml具有以下依赖项和构建配置:

<properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-reactor-netty</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${project.parent.version}</version>
            </plugin>
        </plugins>
    </build>

SpringBoot的主启动类:

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class })
public class WSExampleApplication {

    public static void main(String[] args) {
        SpringApplication.run(WSExampleApplication.class, args);
    }

}

以下是我在前端使用的SockJS部分:

function connect(event) {
    username = document.querySelector('#name').value.trim();
    if(username) {
        var socket = new SockJS('http://localhost:8080/stomp');
        stompClient = Stomp.over(socket);
        stompClient.connect({}, onConnected, onError);
    }
    event.preventDefault();
}

function onConnected() {
    stompClient.subscribe('/topic/public', onMessageReceived); // Subscribe to the Public Topic
    stompClient.send("/app/chat.register", // Tell your username to the server
        {},
        JSON.stringify({sender: username, type: 'JOIN'})
    )
}

function onError(error) {
    connectingElement.textContent = 'Could not connect to WebSocket server!';
    connectingElement.style.color = 'red';
}

CORS error in browser consoleCORS error in browser network tab
CORS/WebSocket配置有问题吗?或者我需要在SockJS的前端进行一些更改吗?
我使用的是Spring Boot版本2.7.4和Java版本18.0.2.1,我使用的是Tomcat。

kr98yfug

kr98yfug1#

由于缺乏关于如何提供前端服务的信息,这可能是三种方式之一。我将在下面逐一发言。

BE和FE是单独的Web服务器,监听不同的本地主机端口

我假设后端正在监听端口8080,因为这是您尝试在前端代码中连接到的端口。
在CORS配置中,您需要允许前端对应的源站。我通过以下修改获得了WebSocket连接(对于FE,我在端口5000上使用了一个简单的Reaction应用程序):
WebSocketConfiguration.java

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer {
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/topic");
        registry.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/stomp")
            .setAllowedOrigins("http://localhost:5000")
            .withSockJS();
    }
}

Java(在您的帖子中是WebSocketSecurity)

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
    @Bean
    @Primary
    public CorsConfigurationSource corsConfiguration() {
        CorsConfiguration corsConfig = new CorsConfiguration();
        corsConfig.setAllowedOrigins(List.of("http://localhost:5000"));
        corsConfig.setAllowedMethods(List.of("GET", "POST"));
        corsConfig.setAllowCredentials(true);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", corsConfig);
        return source;
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http, CorsConfigurationSource configurationSource) throws Exception {
        return http
            .cors().configurationSource(configurationSource)
            .and()
            .headers().frameOptions().sameOrigin()
            .and()
            .build();
    }

    @Bean
    public CookieSameSiteSupplier cookieSameSiteSupplier(){
        return CookieSameSiteSupplier.ofNone();
    }
}

理想情况下,允许的来源列表(以及所有其他CORS配置参数)应该来自自定义属性,以便使它们在Web和WebSocket配置中统一,而不是在字符串文字中统一。

FE通过BE Web服务器提供

如果您通过后端(资源中的静态内容)为前端应用程序提供服务,那么CORS应该完全不是问题,因为前端和后端都有相同的基本URI。在FE中,只指没有它的后端,即简称为/stomp

通过文件系统路径在浏览器中直接打开FE index.html

如果您在没有Web服务器的情况下测试前端(即通过文件系统路径直接在浏览器中打开html文件),那么对于CORS来说,这不是最好的位置。我强烈建议您也为FE启动一台Web服务器(或通过BE为其提供服务),即使是用于本地测试--这会让您回到我的答案的前两部分。

相关问题