【问题标题】:How to correctly implement CORS in WebFlux?如何在 WebFlux 中正确实现 CORS?
【发布时间】:2019-11-23 01:25:03
【问题描述】:

我想创建一个简单的 Spring Boot/Webflux 服务器作为 REST API。我正在尝试在本地测试它。 Webflux 服务器在端口 8080 上运行,我有另一台服务器在端口 3000 上运行 html (React.js)。我想从网站向服务器发出 CORS 请求。为此,我使用 addCorsMappings 方法创建了一个 Java 类,并添加了 @Configuration 注释。我想知道添加注释是否会神奇地让 Spring 意识到带注释的类,但根据这个 article 我还需要在 spring.factories file 中添加 org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.mypackage.CORSHandler 属性,我做了。但是我仍然看到 addCorsMappings 甚至没有被调用(我没有看到日志消息)。作为 Spring Boot 的新手,我还缺少其他配置吗?

这是我的主要课程:

package com.mypackage;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import reactor.ipc.netty.http.server.HttpServer;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;

import static org.springframework.http.HttpStatus.UNAUTHORIZED;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.POST;
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
import static org.springframework.web.reactive.function.server.RequestPredicates.contentType;
import static org.springframework.web.reactive.function.server.RequestPredicates.method;
import static org.springframework.web.reactive.function.server.RequestPredicates.path;
import static org.springframework.web.reactive.function.server.RouterFunctions.nest;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
import static org.springframework.web.reactive.function.server.RouterFunctions.toHttpHandler;

public class Server {
    private static final Logger log = LogManager.getLogger(Server.class);
    public static final String HOST = "localhost";
    public static final int PORT = 8080;

    public static void main(String[] args) throws Exception {
        Server server = new Server();
        server.startReactorServer();

        System.out.println("Press ENTER to exit.");
        System.in.read();
    }

    public RouterFunction<ServerResponse> routingFunction() {
        PersonRepository repository = new DummyPersonRepository();
        PersonHandler handler = new PersonHandler(repository);

        return nest(path("/person"),
                nest(accept(APPLICATION_JSON),
                        route(GET("/{id}"), handler::getPerson)
                                .andRoute(method(HttpMethod.GET), handler::listPeople)
                ).andRoute(POST("/").and(contentType(APPLICATION_JSON)), handler::createPerson));
    }

    public void startReactorServer() {
        RouterFunction<ServerResponse> route = routingFunction().filter((request, next) -> {
            log.warn(request.path());

            if (request.path().contains("person")) {
                log.warn("calling next()");
                return next.handle(request);
            } else {
                return ServerResponse.status(UNAUTHORIZED).build();
            }
        });

        HttpHandler httpHandler = toHttpHandler(route);

        ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
        HttpServer server = HttpServer.create(HOST, PORT);
        server.newHandler(adapter).block();

    }
}

这是我的CORSHandler 课程:

package com.mypackage;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.reactive.config.CorsRegistry;
import org.springframework.web.reactive.config.EnableWebFlux;
import org.springframework.web.reactive.config.WebFluxConfigurer;
import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;

@Configuration
public class CORSHandler implements WebFluxConfigurer {
    private static final Logger log = LogManager.getLogger(CORSHandler.class);

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        log.warn("from addCorsMappings!!");
        registry.addMapping("*")
                .allowedOrigins("*")
                .allowedMethods("*")
                .allowedHeaders("Content-Type", "Access-Control-Allow-Headers", "Authorization",
                        "X-Requested-With", "mode")
                .allowCredentials(true);
    }
}

【问题讨论】:

    标签: java spring spring-boot cors


    【解决方案1】:

    我目前找到的解决方案是包含配置方法的类需要作为参数提供给AnnotationConfigApplicationContext上下文。需要将上下文作为参数提供给WebHttpHandlerBuilder.webHandler(RouterFunctions.toWebHandler(route)).applicationContext(context)。所以主类看起来像这样:

    package com.mypackage;
    
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.web.reactive.function.server.RouterFunctions;
    import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
    import reactor.ipc.netty.http.server.HttpServer;
    import org.springframework.http.HttpMethod;
    import org.springframework.http.server.reactive.HttpHandler;
    import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;
    import org.springframework.web.reactive.function.server.RouterFunction;
    import org.springframework.web.reactive.function.server.ServerResponse;
    
    import static org.springframework.http.HttpStatus.UNAUTHORIZED;
    import static org.springframework.http.MediaType.APPLICATION_JSON;
    import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
    import static org.springframework.web.reactive.function.server.RequestPredicates.POST;
    import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
    import static org.springframework.web.reactive.function.server.RequestPredicates.contentType;
    import static org.springframework.web.reactive.function.server.RequestPredicates.method;
    import static org.springframework.web.reactive.function.server.RequestPredicates.path;
    import static org.springframework.web.reactive.function.server.RouterFunctions.nest;
    import static org.springframework.web.reactive.function.server.RouterFunctions.route;
    import static org.springframework.web.reactive.function.server.RouterFunctions.toHttpHandler;
    
    @SpringBootApplication
    public class Server {
        private static final Logger log = LogManager.getLogger(Server.class);
        public static final String HOST = "localhost";
        public static final int PORT = 8080;
    
        public static void main(String[] args) throws Exception {
            AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(CorsConfiguration.class);
            Server server = new Server();
            server.startReactorServer(ctx);
    
            System.out.println("Press ENTER to exit.");
            System.in.read();
        }
    
        public RouterFunction<ServerResponse> routingFunction() {
            PersonRepository repository = new DummyPersonRepository();
            PersonHandler handler = new PersonHandler(repository);
    
            return nest(path("/person"),
                    nest(accept(APPLICATION_JSON),
                            route(GET("/{id}"), handler::getPerson)
                                    .andRoute(method(HttpMethod.GET), handler::listPeople)
                    ).andRoute(POST("/").and(contentType(APPLICATION_JSON)), handler::createPerson));
        }
    
        public void startReactorServer(AnnotationConfigApplicationContext ctx) {
            RouterFunction<ServerResponse> route = this.routingFunction().filter((request, next) -> {
                log.warn(request.path());
    
                if (request.path().contains("person")) {
                    log.warn("calling next()");
                    return next.handle(request);
                } else {
                    return ServerResponse.status(UNAUTHORIZED).build();
                }
            });
    
            HttpHandler httpHandler = WebHttpHandlerBuilder.webHandler(RouterFunctions.toWebHandler(route))
            .applicationContext(ctx).build();
    
            ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
            HttpServer server = HttpServer.create(HOST, PORT);
            server.newHandler(adapter).block();
        }
    }
    

    CorsConfiguration 类看起来像这样:

    package com.mypackage;
    
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpMethod;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.server.reactive.ServerHttpRequest;
    import org.springframework.http.server.reactive.ServerHttpResponse;
    import org.springframework.web.cors.reactive.CorsUtils;
    import org.springframework.web.reactive.config.EnableWebFlux;
    import org.springframework.web.server.ServerWebExchange;
    import org.springframework.web.server.WebFilter;
    import org.springframework.web.server.WebFilterChain;
    import reactor.core.publisher.Mono;
    
    @Configuration
    @EnableWebFlux
    public class CorsConfiguration {
    
        private static final Logger log = LogManager.getLogger(CorsConfiguration.class);
    
        private static final String ALLOWED_HEADERS = "x-requested-with, authorization, Content-Type, Authorization, credential, X-XSRF-TOKEN, mode";
        private static final String ALLOWED_METHODS = "GET, PUT, POST, DELETE, OPTIONS";
        private static final String ALLOWED_ORIGIN = "*";
        private static final String MAX_AGE = "3600";
    
        @Bean
        public WebFilter corsFilter() {
            log.warn("from CorsConfiguration!!!");
            return (ServerWebExchange ctx, WebFilterChain chain) -> {
                ServerHttpRequest request = ctx.getRequest();
                log.warn("after ServerHttpRequest");
                if (CorsUtils.isCorsRequest(request)) {
                    log.warn("inside isCorsRequest");
                    ServerHttpResponse response = ctx.getResponse();
                    HttpHeaders headers = response.getHeaders();
                    headers.add("Access-Control-Allow-Origin", ALLOWED_ORIGIN);
                    headers.add("Access-Control-Allow-Methods", ALLOWED_METHODS);
                    headers.add("Access-Control-Max-Age", MAX_AGE);
                    headers.add("Access-Control-Allow-Headers", ALLOWED_HEADERS);
                    if (request.getMethod() == HttpMethod.OPTIONS) {
                        response.setStatusCode(HttpStatus.OK);
                        return Mono.empty();
                    }
                }
                return chain.filter(ctx);
            };
        }
    }
    

    在此代码中,corsFilter 被调用并设置了 CORS 标头,但是路由不起作用(返回 404 状态)。

    【讨论】:

      猜你喜欢
      • 2020-09-30
      • 2020-08-07
      • 2020-10-11
      • 2021-11-03
      • 2021-12-13
      • 2021-08-12
      • 2013-10-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多