簡體   English   中英

如何在 SpringBoot 中獲取 Rsocket 連接的遠程 IP 地址

[英]How do I get the remote IP address for an Rsocket connection in SpringBoot

我正在嘗試獲取連接到 RSocket+SpringBoot 網絡服務器的瀏覽器的遠程 IP。 連接是 WebSocket 上的 RSocket。

網絡服務器是 Java-8,SpringBoot-2,在 WebSocket 上使用 RSocket 並將 RequestStreams 發送到瀏覽器。 我將 SpringBoot 自動配置用於 RSocket 設置,因此服務器端的代碼非常少 - 見下文。

下面代碼中的@Headers and MessageHeader只是為了看看他們是否有任何可能導致遠程 IP 的東西,沒有其他原因他們在那里。

我已經搜索了 web 尋找答案 - 很多 http,一些用於 websockets,用於 RSocket。 這個 - https://github.com/rsocket/rsocket-java/issues/735 - 看起來很有希望,但無法處理 DuplexConnection,所以那里沒有雪茄。

有任何想法嗎? 謝謝 !

應用程序.yml:

spring.rsocket.server:
  mapping-path: /rsocket-test
  transport: websocket

server.port: 8080

測試控制器.java

 /**
     * TODO: get the remote IP address and log.
     * Options:
     * 1. @see <a href="https://github.com/rsocket/rsocket-java/issues/735">Ability to intercept requests and access channel information such as remote address</a>.
     * 2. Inject IP in header by nginx. See if it shows up in the @Headers param here.
     * 3. Browser gets its public IP and adds it to the request object. Doable, but lame
     * 4. (Unlikely) Find a way to access thru this chain of private members: headers.req.rsocket().source.connection.source.connection.connection.channel.remoteAddress
     */
    @MessageMapping("subscribe-topic")
    public Flux<StreamingEvent> subscribeToEventStream(
                @Headers Map<String,Object> hdrs,
                MessageHeaders mh,
                testRequest request) {
        return testService.subscribeTopic(request.getRequestId(), request.getTopic());
    }

客戶端的 ip 地址在DuplexConnection class 中可用。 您可以像這樣向RSocketServer添加攔截器:

@Bean
public RSocketServerCustomizer ipCustomizer() {
    return rSocketServer -> rSocketServer.interceptors(registry -> registry.forConnection(new ConnectionInterceptor()));
}

其中ConnectionInterceptor是:

static class ConnectionInterceptor implements DuplexConnectionInterceptor {
    @Override
    public DuplexConnection apply(Type type, DuplexConnection duplexConnection) {
        SocketAddress socketAddress = duplexConnection.remoteAddress();
        if (socketAddress instanceof InetSocketAddress) {
            InetSocketAddress iso = (InetSocketAddress) socketAddress;
            // Here is the ip: iso.getHostString()
        }
        return duplexConnection;
    }
}

在我的場景中,我想將遠程主機和原始 HTTP 請求標頭傳遞給我的@MessageMapping處理程序。 我想出的最好方法是創建自己的NettyRouteProvider並使用所需數據豐富訂閱上下文:

@Component
public class MyRouteProvider implements NettyRouteProvider {

    private final String mappingPath;
    private final SocketAcceptor socketAcceptor;
    private final List<RSocketServerCustomizer> customizers;

    @Autowired
    MyRouteProvider(RSocketProperties properties, RSocketMessageHandler messageHandler,
                    ObjectProvider<RSocketServerCustomizer> customizers) {
        this.mappingPath = properties.getServer().getMappingPath();
        this.socketAcceptor = messageHandler.responder();
        this.customizers = customizers.orderedStream().collect(Collectors.toList());
    }

    @Override
    public HttpServerRoutes apply(HttpServerRoutes httpServerRoutes) {
        RSocketServer server = RSocketServer.create(this.socketAcceptor);
        this.customizers.forEach((customizer) -> customizer.customize(server));
        ServerTransport.ConnectionAcceptor connectionAcceptor = server.asConnectionAcceptor();

        return httpServerRoutes.ws(this.mappingPath, (in, out) ->
                connectionAcceptor.apply(new WebsocketDuplexConnection((Connection) in)).then(out.neverComplete())
                        .contextWrite(context -> {
                            String ip = in.headers().get("x-forwarded-for");
                            SocketAddress remoteAddress = ((Connection) in).channel().remoteAddress();
                            
                            if(in.headers().contains("x-forwarded-for")){
                                return context
                                        .put("remoteHost",remoteAddress)
                                        .put(HttpHeaders.class, in.headers())
                                        .put("client-ip", ip);
                            }
                            return context
                                    .put("remoteHost",remoteAddress)
                                    .put(HttpHeaders.class, in.headers());
                        }));
    }

}

在此之后,您將能夠在任何 @MessageMapping 方法中從上下文中獲取數據,例如:

@MessageMapping("remote-host")
fun remoteHost(): Mono<SocketAddress> {
    return Mono.deferContextual { context ->
        Mono.just(context.get<SocketAddress>("remoteHost"))
    }
}

我與這個問題斗爭了很長一段時間。 我不喜歡接受的答案,因為無法將信息傳遞給處理程序。 諾金娜的回答奏效了,但僅限於最初的請求。 在后續請求中,上下文將不包含地址。 花了幾個小時后,我剛剛回到了良好的反思中,並想出了這個:

import io.rsocket.DuplexConnection;
import io.rsocket.RSocket;
import org.springframework.messaging.rsocket.RSocketRequester;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.net.SocketAddress;

public class RSocketUtil {

    public static DuplexConnection getConnectionFromRequester(RSocketRequester requester) {
        RSocket rsocket = requester.rsocket();
        Field connectionField = ReflectionUtils.findField(rsocket.getClass(), "connection");
        connectionField.setAccessible(true);
        return (DuplexConnection) ReflectionUtils.getField(connectionField, rsocket);
    }

    public static SocketAddress getRemoteAddressFromRequester(RSocketRequester requester) {
        DuplexConnection connection = getConnectionFromRequester(requester);
        return connection.remoteAddress();
    }

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM