如何在Spring MVC Controller获取呼叫中提取IP地址?


94

我正在从事Spring MVC控制器项目,在该项目中我从浏览器进行GET URL调用-

以下是我从浏览器发出GET调用的网址-

http://127.0.0.1:8080/testweb/processing?workflow=test&conf=20140324&dc=all

下面是在浏览器中调用后调用的代码-

@RequestMapping(value = "processing", method = RequestMethod.GET)
public @ResponseBody ProcessResponse processData(@RequestParam("workflow") final String workflow,
    @RequestParam("conf") final String value, @RequestParam("dc") final String dc) {

        System.out.println(workflow);
        System.out.println(value);
        System.out.println(dc);

        // some other code
    }

问题陈述:-

现在有什么方法可以从某些标头中提取IP地址吗?这意味着我想知道从哪个IP地址发出呼叫,这意味着无论谁在URL上方进行呼叫,我都需要知道其IP地址。这可能吗?


尝试使用Spring AOP并处理HttpServletRequest。stackoverflow.com/questions/19271807/...
迪利普ģ

Answers:


153

解决方案是

@RequestMapping(value = "processing", method = RequestMethod.GET)
public @ResponseBody ProcessResponse processData(@RequestParam("workflow") final String workflow,
    @RequestParam("conf") final String value, @RequestParam("dc") final String dc, HttpServletRequest request) {

        System.out.println(workflow);
        System.out.println(value);
        System.out.println(dc);
        System.out.println(request.getRemoteAddr());
        // some other code
    }

添加HttpServletRequest request到您的方法定义中,然后使用Servlet API

春季文档在这里

15.3.2.3支持的处理程序方法参数和返回类型

Handler methods that are annotated with @RequestMapping can have very flexible signatures.
Most of them can be used in arbitrary order (see below for more details).

Request or response objects (Servlet API). Choose any specific request or response type,
for example ServletRequest or HttpServletRequest

1
感谢Koitoer的帮助。一个简单的问题,假设该呼叫是否来自负载均衡器而不是特定的计算机,那么这也能正常工作吗?我猜不是..
john

不,不会,但是负载
平衡器中

检查负载均衡器是否可以在标头中发送这些值,因此请考虑使用HttpServletRequest的getHeader方法。
Koitoer 2014年

Koitoer它完美地工作!!但是除了HttpServletRequest之外,还有其他方法。
Harshal Patil 2014年

1
不推荐使用。
Gewure

114

我来晚了,但是这可能会帮助寻找答案的人。通常servletRequest.getRemoteAddr()有效。

在许多情况下,您的应用程序用户可能正在通过代理服务器访问您的Web服务器,或者您的应用程序位于负载平衡器的后面。

因此,在这种情况下,您应该访问X-Forwarded-For http标头以获取用户的IP地址。

例如 String ipAddress = request.getHeader("X-FORWARDED-FOR");

希望这可以帮助。


11
请注意,X-Forwarded-For通常是逗号分隔的ip列表,链中的每个代理都会将它看到的远程地址添加到列表中。因此,良好的实现通常将具有可信代理的列表,并在从右向左读取此标头时“跳过”这些ip。
拉曼

如何获取IP地址为111.111.111.111/X
Muneeb Mirza

26

我用这种方法做到这一点

public class HttpReqRespUtils {

    private static final String[] IP_HEADER_CANDIDATES = {
        "X-Forwarded-For",
        "Proxy-Client-IP",
        "WL-Proxy-Client-IP",
        "HTTP_X_FORWARDED_FOR",
        "HTTP_X_FORWARDED",
        "HTTP_X_CLUSTER_CLIENT_IP",
        "HTTP_CLIENT_IP",
        "HTTP_FORWARDED_FOR",
        "HTTP_FORWARDED",
        "HTTP_VIA",
        "REMOTE_ADDR"
    };

    public static String getClientIpAddressIfServletRequestExist() {

        if (RequestContextHolder.getRequestAttributes() == null) {
            return "0.0.0.0";
        }

        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        for (String header: IP_HEADER_CANDIDATES) {
            String ipList = request.getHeader(header);
            if (ipList != null && ipList.length() != 0 && !"unknown".equalsIgnoreCase(ipList)) {
                String ip = ipList.split(",")[0];
                return ip;
            }
        }

        return request.getRemoteAddr();
    }
}

1
我在哪里使用它?
Maifee Ul Asad

12

您可以从静态获取IP地址RequestContextHolder,如下所示:

HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
        .getRequest();

String ip = request.getRemoteAddr();

4

将此方法放在您的BaseController中:

@SuppressWarnings("ConstantConditions")
protected String fetchClientIpAddr() {
    HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.getRequestAttributes())).getRequest();
    String ip = Optional.ofNullable(request.getHeader("X-FORWARDED-FOR")).orElse(request.getRemoteAddr());
    if (ip.equals("0:0:0:0:0:0:0:1")) ip = "127.0.0.1";
    Assert.isTrue(ip.chars().filter($ -> $ == '.').count() == 3, "Illegal IP: " + ip);
    return ip;
}

4

见下文。该代码适用于spring-boot和spring-boot + apache CXF / SOAP。

    // in your class RequestUtil
    private static final String[] IP_HEADER_NAMES = { 
                                                        "X-Forwarded-For",
                                                        "Proxy-Client-IP",
                                                        "WL-Proxy-Client-IP",
                                                        "HTTP_X_FORWARDED_FOR",
                                                        "HTTP_X_FORWARDED",
                                                        "HTTP_X_CLUSTER_CLIENT_IP",
                                                        "HTTP_CLIENT_IP",
                                                        "HTTP_FORWARDED_FOR",
                                                        "HTTP_FORWARDED",
                                                        "HTTP_VIA",
                                                        "REMOTE_ADDR"
                                                    };

    public static String getRemoteIP(RequestAttributes requestAttributes)
    {
        if (requestAttributes == null)
        {
            return "0.0.0.0";
        }
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        String ip = Arrays.asList(IP_HEADER_NAMES)
            .stream()
            .map(request::getHeader)
            .filter(h -> h != null && h.length() != 0 && !"unknown".equalsIgnoreCase(h))
            .map(h -> h.split(",")[0])
            .reduce("", (h1, h2) -> h1 + ":" + h2);
        return ip + request.getRemoteAddr();
    }

    //... in service class:
    String remoteAddress = RequestUtil.getRemoteIP(RequestContextHolder.currentRequestAttributes());

:)


3

下面是Spring方法,autowired@Controller类中有request bean :

@Autowired 
private HttpServletRequest request;

System.out.println(request.getRemoteHost());

1
这可能导致跨多个线程同时使用控制器的问题。使用只能将单例注入@Controller实例@Autowired。否则,您必须@Scope(BeanDefinition.SCOPE_PROTOTYPE)在控制器类上使用以确保每个请求都创建一个新的控制器实例。这效率较低,但是如果必须将某些东西作为属性注入控制器类,则这是一种解决方法。
安迪

1

就我而言,我在应用程序前使用Nginx并具有以下配置:

location / {
     proxy_pass        http://localhost:8080/;
     proxy_set_header  X-Real-IP $remote_addr;
     proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
     proxy_set_header  Host $http_host;
     add_header Content-Security-Policy 'upgrade-insecure-requests';
}

所以在我的应用程序中,我得到了真实的用户ip,如下所示:

String clientIP = request.getHeader("X-Real-IP");

0
private static final String[] IP_HEADER_CANDIDATES = {
            "X-Forwarded-For",
            "Proxy-Client-IP",
            "WL-Proxy-Client-IP",
            "HTTP_X_FORWARDED_FOR",
            "HTTP_X_FORWARDED",
            "HTTP_X_CLUSTER_CLIENT_IP",
            "HTTP_CLIENT_IP",
            "HTTP_FORWARDED_FOR",
            "HTTP_FORWARDED",
            "HTTP_VIA",
            "REMOTE_ADDR"
    };

    public static String getIPFromRequest(HttpServletRequest request) {
        String ip = null;
        if (request == null) {
            if (RequestContextHolder.getRequestAttributes() == null) {
                return null;
            }
            request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        }

        try {
            ip = InetAddress.getLocalHost().getHostAddress();
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (!StringUtils.isEmpty(ip))
            return ip;

        for (String header : IP_HEADER_CANDIDATES) {
            String ipList = request.getHeader(header);
            if (ipList != null && ipList.length() != 0 && !"unknown".equalsIgnoreCase(ipList)) {
                return ipList.split(",")[0];
            }
        }

        return request.getRemoteAddr();
    }

在大多数情况下,我将上面的代码与此代码结合使用。将HttpServletRequest request您从api获得的信息传递给方法

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.