解决spring-boot2.0.6中webflux无法获得请求IP的问题
这几天在用spring-boot2的webflux重构一个工程,写到了一个需要获得客户端请求IP的地方,发现写不下去了,在如下的Handler(webflux中Handler相当于mvc中的Controller)中
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.beans.factory.annotation.Qualifier;
importorg.springframework.context.annotation.Bean;
importorg.springframework.context.annotation.Configuration;
importorg.springframework.http.MediaType;
importorg.springframework.stereotype.Component;
importorg.springframework.web.reactive.function.server.RouterFunction;
importorg.springframework.web.reactive.function.server.ServerRequest;
importorg.springframework.web.reactive.function.server.ServerResponse;
importreactor.core.publisher.Mono;
importstaticorg.springframework.web.reactive.function.server.RequestPredicates.GET;
importstaticorg.springframework.web.reactive.function.server.RequestPredicates.accept;
importstaticorg.springframework.web.reactive.function.server.RouterFunctions.route;
/**
*某业务Handler
*/
@Component
publicclassSplashHandler{
privateMonoexecute(ServerRequestserverRequest){
...业务代码
//serverRequest获得IP?
...业务代码
}
@Configuration
publicstaticclassRoutingConfiguration{
@Bean
publicRouterFunctionexecute(SplashHandlerhandler){
returnroute(
GET("/api/ad").and(accept(MediaType.TEXT_HTML)),
handler::execute
);
}
}
}
我发现org.springframework.web.reactive.function.server.ServerRequest根本没有暴露用于获得客户端IP的API,想想这在传统MVC中是相当基本的需求啊,竟然获取不到,然后Google了一下,发现这个是spring-webflux的一个BUG,这个BUG在spring-webflux5.1中解决了,但是,略有些尴尬的是当前最新稳定版的spring-boot还是依赖5.0.x的spring-webflux的。难道要等官方升级么,那不知道得等到什么时候,因此我接着搜了搜资料,看了看文档和源码,自己想了个曲线救国的办法。
正文
在spring-webflux中,有一个org.springframework.web.server.WebFilter接口,类似于ServletAPI中的过滤器,这个API提供了一个方法会将一个限定名为org.springframework.web.server.ServerWebExchange的类暴露出来,而在这个类中就包含了对于请求端IP的获取方法:
org.springframework.web.server.ServerWebExchange#getRequest org.springframework.http.server.reactive.ServerHttpRequest#getRemoteAddress
因此,我们大可以实现一个WebFilter在里面通过暴露的ServerWebExchange拿到客户端IP,然后再将其塞到请求的header中,这样,后续过程就可以从header中取IP了。思路有了,我们开始实现吧。
过滤、取IP、放header,一气呵成:
importorg.springframework.context.annotation.Configuration;
importorg.springframework.http.server.reactive.ServerHttpRequest;
importorg.springframework.stereotype.Component;
importorg.springframework.web.reactive.config.CorsRegistry;
importorg.springframework.web.reactive.config.WebFluxConfigurer;
importorg.springframework.web.server.ServerWebExchange;
importorg.springframework.web.server.WebFilter;
importorg.springframework.web.server.WebFilterChain;
importreactor.core.publisher.Mono;
importjava.net.InetSocketAddress;
importjava.util.Objects;
/*
IfyouwanttokeepSpringBootWebFluxfeaturesandyouwanttoaddadditionalWebFluxconfiguration,youcanaddyourown@ConfigurationclassoftypeWebFluxConfigurerbutwithout@EnableWebFlux.
IfyouwanttotakecompletecontrolofSpringWebFlux,youcanaddyourown@Configurationannotatedwith@EnableWebFlux.
*/
@Configuration
publicclassWebConfigurationimplementsWebFluxConfigurer{
@Override
publicvoidaddCorsMappings(CorsRegistryregistry){
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
*由于在低版本的spring-webflux中不支持直接获得请求IP(https://jira.spring.io/browse/SPR-16681),因此写了一个补丁曲线救国,
*从org.springframework.web.server.ServerWebExchange中获得IP后,在放到header里
*/
@Component
publicstaticclassRetrieveClientIpWebFilterimplementsWebFilter{
@Override
publicMonofilter(ServerWebExchangeexchange,WebFilterChainchain){
InetSocketAddressremoteAddress=exchange.getRequest().getRemoteAddress();
StringclientIp=Objects.requireNonNull(remoteAddress).getAddress().getHostAddress();
ServerHttpRequestmutatedServerHttpRequest=exchange.getRequest().mutate().header("X-CLIENT-IP",clientIp).build();
ServerWebExchangemutatedServerWebExchange=exchange.mutate().request(mutatedServerHttpRequest).build();
returnchain.filter(mutatedServerWebExchange);
}
}
}
后续过程header取值:
privateMonoexecute(ServerRequestserverRequest){ StringclientIp=serverRequest.headers().asHttpHeaders().getFirst("X-CLIENT-IP") ...业务代码 }
通过上述解决方案(其实严格上说是hacking)就解决了我们遇到的问题了。
总结
以上所述是小编给大家介绍的解决spring-boot2.0.6中webflux无法获得请求IP的问题,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!