Check X-Real-IP header when obtaining client IP (#4139)

#### What type of PR is this?

/kind bug
/area core
/milestone 2.7.x

#### What this PR does / why we need it:

In some special situations, Halo can not obtain client IP address from request headers and socket address is unresolved, so that `java.lang.NullPointerException: Cannot invoke "java.net.InetAddress.getHostAddress()" because the return value of "java.net.InetSocketAddress.getAddress()" is null` will happen.

This PR will resolve the problem by checking `X-Real-IP` header and checking if remote address is unresolved.

#### Which issue(s) this PR fixes:

Fixes https://github.com/halo-dev/halo/issues/4134

#### Does this PR introduce a user-facing change?

```release-note
None
```
pull/4069/head^2
John Niang 2023-06-28 17:50:11 +08:00 committed by GitHub
parent 25103b9ff8
commit 4aec1ba8f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 27 additions and 4 deletions

View File

@ -2,6 +2,7 @@ package run.halo.app.infra.utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.StringUtils;
import org.springframework.web.reactive.function.server.ServerRequest;
/**
@ -14,6 +15,7 @@ public class IpAddressUtils {
private static final String[] IP_HEADER_NAMES = {
"X-Forwarded-For",
"X-Real-IP",
"Proxy-Client-IP",
"WL-Proxy-Client-IP",
"CF-Connecting-IP",
@ -36,17 +38,18 @@ public class IpAddressUtils {
public static String getClientIp(ServerHttpRequest request) {
for (String header : IP_HEADER_NAMES) {
String ipList = request.getHeaders().getFirst(header);
if (ipList != null && ipList.length() != 0 && !"unknown".equalsIgnoreCase(ipList)) {
if (StringUtils.hasText(ipList) && !UNKNOWN.equalsIgnoreCase(ipList)) {
String[] ips = ipList.trim().split("[,;]");
for (String ip : ips) {
if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
if (StringUtils.hasText(ip) && !UNKNOWN.equalsIgnoreCase(ip)) {
return ip;
}
}
}
}
var remoteAddress = request.getRemoteAddress();
return remoteAddress == null ? UNKNOWN : remoteAddress.getAddress().getHostAddress();
return remoteAddress == null || remoteAddress.isUnresolved()
? UNKNOWN : remoteAddress.getAddress().getHostAddress();
}

View File

@ -2,6 +2,7 @@ package run.halo.app.infra.utils;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.net.InetSocketAddress;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
@ -19,12 +20,31 @@ class IpAddressUtilsTest {
}
@Test
void testGetUnknownIPAddress() {
void testGetIPAddressFromXRealIpHeader() {
var request = MockServerHttpRequest.get("/")
.header("X-Real-IP", "127.0.0.1")
.build();
var expected = "127.0.0.1";
var actual = IpAddressUtils.getClientIp(request);
assertEquals(expected, actual);
}
@Test
void testGetUnknownIPAddressWhenRemoteAddressIsNull() {
var request = MockServerHttpRequest.get("/").build();
var actual = IpAddressUtils.getClientIp(request);
assertEquals(IpAddressUtils.UNKNOWN, actual);
}
@Test
void testGetUnknownIPAddressWhenRemoteAddressIsUnresolved() {
var request = MockServerHttpRequest.get("/")
.remoteAddress(InetSocketAddress.createUnresolved("localhost", 8090))
.build();
var actual = IpAddressUtils.getClientIp(request);
assertEquals(IpAddressUtils.UNKNOWN, actual);
}
@Test
void testGetIPAddressWithMultipleHeaders() {
var headers = new HttpHeaders();