boot spring 代码获取ip_解决spring-boot2.0.6中webflux无法获得请求IP的问题

这几天在用 spring-boot 2 的 webflux 重构一个工程,写到了一个需要获得客户端请求 IP 的地方,发现写不下去了,在如下的 Handler(webflux 中 Handler 相当于 mvc 中的 Controller)中

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;

/**

* 某业务 Handler

*/

@Component

public class SplashHandler {

private Mono execute(ServerRequest serverRequest) {

... 业务代码

// serverRequest 获得 IP ?

... 业务代码

}

@Configuration

public static class RoutingConfiguration {

@Bean

public RouterFunction execute(SplashHandler handler) {

return route(

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-webflux 5.1 中解决了,但是,略有些尴尬的是当前最新稳定版的 spring-boot 还是依赖 5.0.x 的 spring-webflux 的。难道要等官方升级么,那不知道得等到什么时候,因此我接着搜了搜资料,看了看文档和源码,自己想了个曲线救国的办法。

正文

在 spring-webflux 中,有一个 org.springframework.web.server.WebFilter接口,类似于 Servlet API 中的过滤器,这个 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,一气呵成:

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

* 由于在低版本的 spring-webflux 中不支持直接获得请求 IP(https://jira.spring.io/browse/SPR-16681),因此写了一个补丁曲线救国,

* 从 org.springframework.web.server.ServerWebExchange 中获得 IP 后,在放到 header 里

*/

@Component

public static class RetrieveClientIpWebFilter implements WebFilter {

@Override

public Mono 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();

return chain.filter(mutatedServerWebExchange);

}

}

}

后续过程 header 取值:

private Mono execute(ServerRequest serverRequest) {

String clientIp = serverRequest.headers().asHttpHeaders().getFirst("X-CLIENT-IP")

... 业务代码

}

通过上述解决方案(其实严格上说是 hacking)就解决了我们遇到的问题了。

总结

以上所述是小编给大家介绍的解决spring-boot2.0.6中webflux无法获得请求IP的问题,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值