Spring CORS过滤器的Access-Control-Allow-Private-Network不起作用。

huangapple go评论70阅读模式
英文:

Spring CORS Filter for Access-Control-Allow-Private-Network not working

问题

I have a webapp hosted on a public URL, trying to call a local webservice API (running on localhost)
我有一个托管在公共URL上的Web应用程序,试图调用本地运行的webservice API(运行在localhost上)

I am using Chrome to access the public website, this creates a CORS issue as described here
我正在使用Chrome访问公共网站,这会导致跨源资源共享(CORS)问题,如下所述

The solution stated in the above link is to either,
上述链接中提到的解决方法是要么:

  1. Disable Private Network Access checks in Chrome (which works)

  2. 在Chrome中禁用私有网络访问检查(可行)

  3. Set response header "Access-Control-Allow-Private-Network: true" (doesn't work)

  4. 设置响应头"Access-Control-Allow-Private-Network: true"(不起作用)

There is no Spring CORS support for the header "Access-Control-Allow-Private-Network" yet. So I have tried various ways to create a filter in my Spring Boot application, but to no avail. The filter never gets invoked and I encounter the CORS issue.
目前Spring还不支持"Access-Control-Allow-Private-Network"响应头的CORS。因此,我已经尝试了多种方法来在我的Spring Boot应用程序中创建过滤器,但都没有成功。过滤器从未被调用,我遇到了CORS问题。

Please point me in the right direction to fix this issue.
请指导我正确的方向来解决这个问题。

This is my filter class
这是我的过滤器类

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class MyFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        log.info("MyFilter invoked............");
        final HttpServletResponse response = (HttpServletResponse) servletResponse;
        response.setHeader("Access-Control-Allow-Private-Network", "true");
        filterChain.doFilter(servletRequest, servletResponse);
    }
}

This is my config class
这是我的配置类

@Configuration
public class MyConfig {

    @Bean
    public WebMvcConfigurer corsMappingConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**");
            }
        };
    }
}
英文:

I have a webapp hosted on a public URL, trying to call a local webservice API (running on localhost)
I am using Chrome to access the public website, this creates a CORS issue as described here

https://developer.chrome.com/blog/private-network-access-preflight/

The solution stated in the above link is to either,

  1. Disable Private Network Access checks in Chrome (which works)
  2. Set response header "Access-Control-Allow-Private-Network: true" (doesnt work)

There is no Spring CORS support for the header "Access-Control-Allow-Private-Network" yet. So I have tried various ways to create a filter in my Spring Boot application, but to no avail. The filter never gets invoked and I encounter the CORS issue.

Please point me in the right direction to fix this issue.

This is my filter class

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class MyFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        log.info("MyFilter invoked............");
        final HttpServletResponse response = (HttpServletResponse) servletResponse;
        response.setHeader("Access-Control-Allow-Private-Network", "true");
        filterChain.doFilter(servletRequest, servletResponse);
    }
}

This is my config class

@Configuration
public class MyConfig {

    @Bean
    public WebMvcConfigurer corsMappingConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**");
            }
        };
    }

答案1

得分: 1

这不完全是解释为什么你的代码不起作用的答案,但我刚刚创建了一种使用Spring实现的方法,可能对你有所帮助。我正在使用Spring Boot 2.7,因此包括了Spring 5.3。

它使用自定义的CorsProcessor类来向响应添加相关的标头。

CustomCorsProcessor.java

public class CustomCorsProcessor extends DefaultCorsProcessor implements CorsProcessor {

    private static final String ACCESS_CONTROL_REQUEST_PRIVATE_NETWORK = "Access-Control-Request-Private-Network";
    private static final String ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK = "Access-Control-Allow-Private-Network";

    @Override
    public boolean processRequest(@Nullable CorsConfiguration config, HttpServletRequest request,
                                  HttpServletResponse response) throws IOException {

        // 允许DefaultCorsProcessor首先运行
        boolean superResult = super.processRequest(config, request, response);
        if (!superResult) return false;

        ServerHttpRequest serverRequest = new ServletServerHttpRequest(request);

        // 如果包含请求私有网络访问的CORS标头,则允许访问
        if (serverRequest.getHeaders().containsKey(ACCESS_CONTROL_REQUEST_PRIVATE_NETWORK)) {
            response.addHeader(ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK, Boolean.toString(true));
        }

        return true;
    }
}

你可以自定义CorsConfiguration类以避免硬编码布尔值true

然后,在WebSecurityConfig中使用CustomCorsProcessor类作为CorsProcessor

请注意,在这个私有网络更改之前,CorsConfigurationSource本身是一个@Bean,但现在CorsFilter@Bean

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            // 默认情况下使用名为corsFilter的Bean
            .cors()
            .and()
            // 根据你的应用程序需求添加其他配置

        return http.build();
    }

    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowCredentials(true);
        configuration.addAllowedOrigin("http://domain1.com");
        configuration.addAllowedHeader("*");
        configuration.addAllowedMethod("*");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

    @Bean
    public CorsFilter corsFilter() {
        CorsConfigurationSource corsConfigurationSource = corsConfigurationSource();
        CorsFilter corsFilter = new CorsFilter(corsConfigurationSource);

        // 注册我们的自定义CorsProcessor,包含私有网络允许的标头
        corsFilter.setCorsProcessor(new CustomCorsProcessor());
        return corsFilter;
    }
}

这样可以让Chrome连接到私有网络的Web服务。

这部分部分基于我从以下链接中找到的信息:

https://spring.io/blog/2015/06/08/cors-support-in-spring-framework
https://www.baeldung.com/spring-cors

英文:

This is not quite an answer to why your code does not work, but I have just created a Spring way to achieve this, so it may assist you. I am using Spring Boot 2.7 therefore Spring 5.3 is included.

It uses a custom CorsProcessor class to add the relevant header to the response.

CustomCorsProcessor.java:

public class CustomCorsProcessor extends DefaultCorsProcessor implements CorsProcessor {

	private static final String ACCESS_CONTROL_REQUEST_PRIVATE_NETWORK = "Access-Control-Request-Private-Network";
	private static final String ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK = "Access-Control-Allow-Private-Network";
	
	@Override
	public boolean processRequest(@Nullable CorsConfiguration config, HttpServletRequest request,
			HttpServletResponse response) throws IOException {
		
		//Allow DefaultCorsProcessor to run first
		boolean superResult = super.processRequest(config, request, response);
		if (superResult == false) return false;
		
		ServerHttpRequest serverRequest = new ServletServerHttpRequest(request);
		
		//If the CORS header requesting Private Network access is present, respond allowing access
		if(serverRequest.getHeaders().containsKey(ACCESS_CONTROL_REQUEST_PRIVATE_NETWORK)) {
			response.addHeader(ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK, Boolean.toString(true));
		}
		
		return true;
	}
}

You might like to customise the CorsConfiguration class to avoid hardcoding the Boolean true.

Then the CustomCorsProcessor class is used in WebSecurityConfig as a CorsProcessor.

Note that before this Private Network change, the CorsConfigurationSource was itself an @Bean but now the CorsFilter is the @Bean.

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
			// by default uses a Bean by the name of corsFilter
			.cors()
			.and()
            ... as needed for your application

        return http.build();
    }

	public CorsConfigurationSource corsConfigurationSource() {
		CorsConfiguration configuration = new CorsConfiguration();
		configuration.setAllowCredentials(true);
		configuration.addAllowedOrigin("http://domain1.com");
		configuration.addAllowedHeader("*");
		configuration.addAllowedMethod("*");
		UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
		source.registerCorsConfiguration("/**", configuration);
		return source;
	}
	
	@Bean
	public CorsFilter corsFilter() {
		CorsConfigurationSource corsConfigurationSource = corsConfigurationSource();
		CorsFilter corsFilter = new CorsFilter(corsConfigurationSource);

		//Register our custom CorsProcessor that includes the Private Network allowed header
		corsFilter.setCorsProcessor(new CustomCorsProcessor());
		return corsFilter;
	}
}

This then allowed Chrome to connect to the Private Network web service.

This is partly based on what I found from these URLs:

https://spring.io/blog/2015/06/08/cors-support-in-spring-framework
https://www.baeldung.com/spring-cors

huangapple
  • 本文由 发表于 2023年4月17日 22:55:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/76036487.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定