Spring 客户端 IP 地址获取及存储细节

您所在的位置:网站首页 spring获取客户端ip Spring 客户端 IP 地址获取及存储细节

Spring 客户端 IP 地址获取及存储细节

2024-07-10 05:12| 来源: 网络整理| 查看: 265

本文讲解如何在Spring框架内获取客户端 IP 地址以及存储的细节,常见使用场景如下:

网络安全,通常需要知道客户端请求的IP地址,以方便与已有的黑名单等进行对比,从而识别攻击 数据分析,记录用户登陆IP地址,识别用户地理位置,统计各省市用户数量等 请求限制,记录请求IP地址,限制请求频率

Spring 框架没有现成工具可以方便提取客户端的IP地址,普遍做法就是通过 HttpServletRequest 的 getRemoteAddr 方法获取IP地址。

存在以下问题:

proxy:部分客户端使用代理后此方法返回的是代理网络的IP地址,非用户真实 IP SLB:后台经过负载均衡,如阿里云的SLB实例,方法返回地址是SLB实例 IP,并非用户真实 IP 环回地址:在本地测试时获取到的是ipv4:127.0.0.1 或者 ipv6:0:0:0:0:0:0:0:1,并非本机分配地址 代码简洁与耦合:每次获取地址都需要注入 HttpServletRequest 再提取,使用 Spring WebFlux 而不是Spring MVC,没有此对象可用 获取地址可能是IPv6 地址,长度不同,数据库需要兼容处理,适配以后 IPv6需求

问题解决:

proxy :经过代理后通常可用通过 http header 的 Proxy-Client-IP 获取用户真实 IP地址 SLB:经过SLB实例后可通过 http header 的 X-Forwarded-For 获取用户真实IP 环回地址:如果是环回地址,则根据网卡取本机配置的IP,如192.168.199.123 等 代码简洁与耦合:实现参数解析器,使用注解方式获取IP,如 @ClientIp 不同版本 IP 长度不同,取最长作为数据库存储长度(47最长) 版本 例子 字符长度 IPv4 192.168.199.111 15 IPv6 ABCD:ABCD:ABCD:ABCD:ABCD:ABCD:ABCD:ABCD 39 IPv4-mapped IPv6 ABCD:ABCD:ABCD:ABCD:ABCD:ABCD:192.168.158.190 45

注:IPv6 前后可能用:: 描述部分段,会增加2个字符,见 rfc6052 参考Linux系统下 inet.h 文件

#define INET_ADDRSTRLEN (16) #define INET6_ADDRSTRLEN (48)

最后一个字符为终结符,不算在内,最长为47字符

最终使用效果(@ClientIp 注解获取):

import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; @Slf4j @RestController @RequestMapping("/test") @EnableAutoConfiguration public class OrderController { @GetMapping("/hello") @ResponseBody @ResponseStatus(HttpStatus.OK) public String hello(@ClientIp String ip) { return "hello, ip = " + ip; } } 实现代码

注:下面为 Spring MVC 下的实现代码,如需在Spring webFlux 下使用,同理实现下面方法、配置即可

org.springframework.web.reactive.resulthod.HandlerMethodArgumentResolver

org.springframework.web.reactive.config.WebFluxConfigurer

注解

import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) public @interface ClientIp { }

方法参数解析器(Resolver)代码:

import lombok.extern.slf4j.Slf4j; import org.springframework.core.MethodParameter; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; import javax.servlet.ServletRequest; import java.net.InetAddress; import java.net.UnknownHostException; public class ClientIpResolver implements HandlerMethodArgumentResolver { 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" }; @Override public boolean supportsParameter(MethodParameter param) { return param.getParameterType().equals(String.class) && param.hasParameterAnnotation(ClientIp.class); } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) { // 提取header得到IP地址列表(多重代理场景),取第一个IP for (String header : IP_HEADER_CANDIDATES) { String ipList = webRequest.getHeader(header); if (ipList != null && ipList.length() != 0 && !"unknown".equalsIgnoreCase(ipList)) { return ipList.split(",")[0]; } } // 没有经过代理或者SLB,直接 getRemoteAddr 方法获取IP String ip = ((ServletRequest) webRequest.getNativeRequest()).getRemoteAddr(); // 如果是本地环回IP,则根据网卡取本机配置的IP if ("127.0.0.1".equals(ip) || "0:0:0:0:0:0:0:1".equals(ip)) { try { InetAddress inetAddress = InetAddress.getLocalHost(); return inetAddress.getHostAddress(); } catch (UnknownHostException e) { e.printStackTrace(); return ip; } } return ip; } }

全局增加Resolver配置

import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.util.List; @Configuration public class NetWebMvcConfigurer implements WebMvcConfigurer { @Override public void addArgumentResolvers(List resolvers) { resolvers.add(clientIpResolver()); } @Bean public ClientIpResolver clientIpResolver() { return new ClientIpResolver(); } } 参考文档 rfc6052 IPv4/IPv6 转换 rfc1924 IPv6 地址格式 Maximum length of the textual representation of an IPv6 address?

原文:taskhub.work



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3