-
Notifications
You must be signed in to change notification settings - Fork 97
perf: 优化获取客户端IP地址的逻辑 #358
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
perf: 优化获取客户端IP地址的逻辑 #358
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -10,12 +10,14 @@ | |||||
| import com.jmal.clouddisk.model.LogOperationDTO; | ||||||
| import com.jmal.clouddisk.model.rbac.ConsumerDO; | ||||||
| import com.jmal.clouddisk.service.Constants; | ||||||
| import com.jmal.clouddisk.util.IPUtil; | ||||||
| import com.jmal.clouddisk.util.ResponseResult; | ||||||
| import com.jmal.clouddisk.util.ResultUtil; | ||||||
| import com.jmal.clouddisk.util.TimeUntils; | ||||||
| import io.reactivex.rxjava3.core.Completable; | ||||||
| import io.reactivex.rxjava3.schedulers.Schedulers; | ||||||
| import jakarta.annotation.PostConstruct; | ||||||
| import jakarta.annotation.PreDestroy; | ||||||
| import jakarta.servlet.http.HttpServletRequest; | ||||||
| import jakarta.servlet.http.HttpServletResponse; | ||||||
| import lombok.RequiredArgsConstructor; | ||||||
|
|
@@ -26,6 +28,7 @@ | |||||
| import org.springframework.web.context.request.RequestContextHolder; | ||||||
| import org.springframework.web.context.request.ServletRequestAttributes; | ||||||
|
|
||||||
| import java.io.IOException; | ||||||
| import java.nio.charset.StandardCharsets; | ||||||
| import java.time.LocalDateTime; | ||||||
| import java.util.List; | ||||||
|
|
@@ -41,8 +44,6 @@ | |||||
| @RequiredArgsConstructor | ||||||
| public class LogService { | ||||||
|
|
||||||
| private static final int REGION_LENGTH = 5; | ||||||
|
|
||||||
| private final ILogDAO logDAO; | ||||||
|
|
||||||
| private final UserLoginHolder userLoginHolder; | ||||||
|
|
@@ -145,43 +146,12 @@ public LogOperation getLogOperation(HttpServletRequest request, LogOperation log | |||||
| // 请求方式 | ||||||
| logOperation.setMethod(request.getMethod()); | ||||||
| // 客户端ip | ||||||
| String ip = getIpAddress(request); | ||||||
| String ip = IPUtil.getClientIP(request); | ||||||
| logOperation.setIp(ip); | ||||||
| setIpInfo(logOperation, ip); | ||||||
| return logOperation; | ||||||
| } | ||||||
|
|
||||||
| private String getIpAddress(HttpServletRequest request) { | ||||||
| String ip = request.getRemoteHost(); | ||||||
| if (CharSequenceUtil.isNotBlank(ip)) { | ||||||
| return ip; | ||||||
| } | ||||||
| ip = request.getHeader("x-forwarded-for"); | ||||||
| if (!CharSequenceUtil.isBlank(ip) && (ip.contains(","))) { | ||||||
| // 多次反向代理后会有多个ip值,第一个ip才是真实ip | ||||||
| ip = ip.split(",")[0]; | ||||||
| } | ||||||
| if (CharSequenceUtil.isBlank(ip)) { | ||||||
| ip = request.getHeader("X-real-ip"); | ||||||
| } | ||||||
| if (CharSequenceUtil.isBlank(ip)) { | ||||||
| ip = request.getHeader("Proxy-Client-IP"); | ||||||
| } | ||||||
| if (CharSequenceUtil.isBlank(ip)) { | ||||||
| ip = request.getHeader("WL-Proxy-Client-IP"); | ||||||
| } | ||||||
| if (CharSequenceUtil.isBlank(ip)) { | ||||||
| ip = request.getHeader("HTTP_CLIENT_IP"); | ||||||
| } | ||||||
| if (CharSequenceUtil.isBlank(ip)) { | ||||||
| ip = request.getHeader("HTTP_X_FORWARDED_FOR"); | ||||||
| } | ||||||
| if (CharSequenceUtil.isBlank(ip)) { | ||||||
| ip = request.getHeader("X-Real-IP"); | ||||||
| } | ||||||
| return ip; | ||||||
| } | ||||||
|
|
||||||
| /*** | ||||||
| * 设置IP详细信息 | ||||||
| * @param logOperation LogOperation | ||||||
|
|
@@ -199,33 +169,30 @@ private void setIpInfo(LogOperation logOperation, String ip) { | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| /*** | ||||||
| * 解析IP区域信息 | ||||||
| /** | ||||||
| * 解析IP区域信息, region格式: 国家|区域|省份|城市|运营商 | ||||||
| */ | ||||||
| public LogOperation.IpInfo region2IpInfo(String region) { | ||||||
| LogOperation.IpInfo ipInfo = new LogOperation.IpInfo(); | ||||||
| String[] r = region.split("\\|"); | ||||||
| if (r.length != REGION_LENGTH) return ipInfo; | ||||||
| String country = r[0]; | ||||||
| if (!Constants.REGION_DEFAULT.equals(country)) { | ||||||
| ipInfo.setCountry(country); | ||||||
| } | ||||||
| String area = r[1]; | ||||||
| if (!Constants.REGION_DEFAULT.equals(area)) { | ||||||
| ipInfo.setArea(area); | ||||||
| } | ||||||
| String province = r[2]; | ||||||
| if (!Constants.REGION_DEFAULT.equals(province)) { | ||||||
| ipInfo.setProvince(province); | ||||||
| } | ||||||
| String city = r[3]; | ||||||
| if (!Constants.REGION_DEFAULT.equals(city)) { | ||||||
| ipInfo.setCity(city); | ||||||
|
|
||||||
| if (region == null || region.isEmpty()) { | ||||||
| return ipInfo; | ||||||
| } | ||||||
| String operators = r[4]; | ||||||
| if (!Constants.REGION_DEFAULT.equals(operators)) { | ||||||
| ipInfo.setOperators(operators); | ||||||
|
|
||||||
| String[] parts = IPUtil.SPLIT_PATTERN.split(region, IPUtil.REGION_LENGTH); | ||||||
|
||||||
| String[] parts = IPUtil.SPLIT_PATTERN.split(region, IPUtil.REGION_LENGTH); | |
| String[] parts = IPUtil.SPLIT_PATTERN.split(region); |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,59 @@ | ||||||||||||||||||||||||||||||||||||||||||||||
| package com.jmal.clouddisk.util; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| import jakarta.servlet.http.HttpServletRequest; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.regex.Pattern; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| public final class IPUtil { | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| private IPUtil() { | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| public static final int REGION_LENGTH = 5; | ||||||||||||||||||||||||||||||||||||||||||||||
| public static final Pattern SPLIT_PATTERN = Pattern.compile("\\|"); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| private static final String[] IP_HEADERS = { | ||||||||||||||||||||||||||||||||||||||||||||||
| "CF-Connecting-IP", | ||||||||||||||||||||||||||||||||||||||||||||||
| "X-Real-IP", | ||||||||||||||||||||||||||||||||||||||||||||||
| "X-Forwarded-For", | ||||||||||||||||||||||||||||||||||||||||||||||
| "Proxy-Client-IP", | ||||||||||||||||||||||||||||||||||||||||||||||
| "WL-Proxy-Client-IP" | ||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||
| * 获取客户端真实IP | ||||||||||||||||||||||||||||||||||||||||||||||
| * 自动处理大小写不敏感的请求头 | ||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||
| * @param request HTTP请求对象 | ||||||||||||||||||||||||||||||||||||||||||||||
| * @return 客户端IP地址 | ||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||
| public static String getClientIP(HttpServletRequest request) { | ||||||||||||||||||||||||||||||||||||||||||||||
| for (String header : IP_HEADERS) { | ||||||||||||||||||||||||||||||||||||||||||||||
| String ip = request.getHeader(header); | ||||||||||||||||||||||||||||||||||||||||||||||
| if (isValidIp(ip)) { | ||||||||||||||||||||||||||||||||||||||||||||||
| return extractFirstIp(ip); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| return request.getRemoteAddr(); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
30
to
42
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 当前的 为了修复这个问题并使逻辑更健壮,建议调整为先提取第一个IP,然后再对提取出的IP进行有效性验证。这样可以正确处理上述边缘情况,并确保只有有效的IP地址被返回。
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||
| * 提取第一个IP(处理逗号分隔的多IP情况) | ||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||
| private static String extractFirstIp(String ip) { | ||||||||||||||||||||||||||||||||||||||||||||||
| if (ip.contains(",")) { | ||||||||||||||||||||||||||||||||||||||||||||||
| int commaIndex = ip.indexOf(','); | ||||||||||||||||||||||||||||||||||||||||||||||
| return ip.substring(0, commaIndex).trim(); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| return ip.trim(); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||
| * 检查IP是否有效 | ||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||
| private static boolean isValidIp(String ip) { | ||||||||||||||||||||||||||||||||||||||||||||||
| return ip != null | ||||||||||||||||||||||||||||||||||||||||||||||
| && !ip.isEmpty() | ||||||||||||||||||||||||||||||||||||||||||||||
| && !"unknown".equalsIgnoreCase(ip); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+58
to
+62
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 当前的IP验证逻辑 ( 为了增强安全性和数据准确性,建议使用 为了方便阅读,建议在文件顶部添加 private static boolean isValidIp(String ip) {
return ip != null
&& !ip.isEmpty()
&& !"unknown".equalsIgnoreCase(ip)
&& cn.hutool.core.net.NetUtil.isIP(ip);
} |
||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
为了使代码更健壮,建议使用
CharSequenceUtil.isBlank(region)来代替region == null || region.isEmpty()。isBlank可以处理null、空字符串以及只包含空白字符的字符串(例如" ")。这个类中已经导入并使用了cn.hutool.core.text.CharSequenceUtil,因此保持代码风格一致性也是一个好处。