preface

These days, I used The webFlux of Spring-Boot 2 to reconstruct a project. When I wrote to a place where I needed to obtain the client’s request IP, I found that I could not continue writing. In the following Handler (webFlux Handler is the equivalent of Controller in MVC)

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;

/** * A business Handler */
@Component
public class SplashHandler {

    private Mono<ServerResponse> execute(ServerRequest serverRequest) {... Business code// serverRequest get IP address?. Business code}@Configuration
    public static class RoutingConfiguration {

        @Bean
        public RouterFunction<ServerResponse> execute(SplashHandler handler) {
            return route(
                    GET("/api/ad").and(accept(MediaType.TEXT_HTML)), handler::execute ); }}}Copy the code

I find org. Springframework. Web. Reactive. The function. The server ServerRequest no exposure are used to obtain the client IP API, think about it in the traditional MVC is fairly basic demand, should get less than, Then I googled and found that this was a BUG in Spring-WebFlux, which was fixed in Spring-WebFlux 5.1, but, Somewhat awkwardly, the latest stable version of Spring-Boot still relies on 5.0.x Spring-WebFlux. Do you want to wait for the official upgrade, that do not know when to wait, so I then searched the information, looked at the documents and source code, they want a curve to save the country’s way.

The body of the

In spring – webflux, a org. Springframework.. The web server. The WebFilter interface, similar to the Servlet filters in the API, This API provides a way to a qualified called org. Springframework.. Web server ServerWebExchange class exposed, and in this class contains methods to obtain request end IP:

org.springframework.web.server.ServerWebExchange#getRequest
org.springframework.http.server.reactive.ServerHttpRequest#getRemoteAddress
Copy the code

Therefore, we could implement a WebFilter that gets the client IP from the exposed ServerWebExchange and then inserts it into the header of the request so that the subsequent process can get the IP from the header. So we have the idea, so let’s start implementing it.

Filter, take IP, put header, all in one go:

import org.springframework.context.annotation.Configuration;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.config.CorsRegistry;
import org.springframework.web.reactive.config.WebFluxConfigurer;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

import java.net.InetSocketAddress;
import java.util.Objects;

/* If you want to keep Spring Boot WebFlux features and you want to add additional WebFlux configuration, you can add your own @Configuration class of type WebFluxConfigurer but without @EnableWebFlux. If you want to take complete control of Spring WebFlux, you can add your own @Configuration annotated with @EnableWebFlux. */
@Configuration
public class WebConfiguration implements WebFluxConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry
                .addMapping("/ * *")
                .allowedOrigins("*")
                .allowedMethods("GET"."POST"."PUT"."PATCH"."DELETE"."OPTION")
                .allowedHeaders("header1"."header2"."header3")
                .exposedHeaders("header1"."header2")
                .allowCredentials(true)
                .maxAge(3600);
    }

    /** * https://stackoverflow.com/questions/51192630/how-do-you-get-clients-ip-address-spring-webflux-websocket?rq=1 * https://stackoverflow.com/questions/50981136/how-to-get-client-ip-in-webflux * https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-filters * due to low version Spring - webflux does not support direct access to the request of the IP (https://jira.spring.io/browse/SPR-16681), thus saving wrote a patch curve, * from org. Springframework. Web. Server. After obtain IP ServerWebExchange, * / in the in the header
    @Component
    public static class RetrieveClientIpWebFilter implements WebFilter {

        @Override
        public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
            InetSocketAddress remoteAddress = exchange.getRequest().getRemoteAddress();
            String clientIp = Objects.requireNonNull(remoteAddress).getAddress().getHostAddress();
            ServerHttpRequest mutatedServerHttpRequest = exchange.getRequest().mutate().header("X-CLIENT-IP", clientIp).build();
            ServerWebExchange mutatedServerWebExchange = exchange.mutate().request(mutatedServerHttpRequest).build();
            returnchain.filter(mutatedServerWebExchange); }}}Copy the code

Follow-up Procedure Header values:

    private Mono<ServerResponse> execute(ServerRequest serverRequest) {
        String clientIp = serverRequest.headers().asHttpHeaders().getFirst("X-CLIENT-IP")... Business code}Copy the code

With the above solution (actually hacking, strictly speaking), we’ve solved our problem.

The resources

How do you get Client’s IP address? (Spring WebFlux WebSocket)

How to get client IP in webflux?

Spring-webflux Documentation section 1.2.3. ‘Filters’

Spring FrameworkSPR-16681 (jira)