Skip to content

Conversation

@jamebal
Copy link
Owner

@jamebal jamebal commented Dec 19, 2025

No description provided.

@jamebal jamebal added the improvement Improvement label Dec 19, 2025
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @jamebal, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

此拉取请求主要聚焦于提升系统在获取客户端 IP 地址时的准确性和鲁棒性。通过调整 IP 获取的策略,优先从常见的代理服务器 HTTP 头中提取真实 IP,并新增对特定代理头(如 CF-Connecting-IP)的支持,以适应更复杂的网络环境。同时,项目版本也进行了小幅升级。

Highlights

  • 项目版本更新: 将 pom.xml 中的项目版本从 "2.16.5" 更新至 "2.16.6"。
  • 客户端IP获取逻辑优化: 重构了 LogServicegetIpAddress 方法的实现,旨在更准确地获取客户端 IP 地址。
  • IP获取优先级调整: 调整了获取 IP 地址的优先级,现在优先检查 x-forwarded-for 等 HTTP 请求头,而不是直接使用 request.getRemoteHost()
  • 新增CF-Connecting-IP支持: 在获取客户端 IP 的逻辑中,新增了对 CF-Connecting-IP HTTP 头的检查,以支持更多代理场景。
  • IP返回逻辑变更: 修改了 getIpAddress 方法的最终返回逻辑:在尝试通过多个 HTTP 头获取 IP 后,如果成功获取到 IP,则最终返回 request.getRemoteHost();如果所有 HTTP 头都未能提供 IP,则返回通过头信息获取到的 IP(此时可能为空)。

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

本次PR优化了获取客户端IP的逻辑,将 x-forwarded-for 放在了最前面,并增加了对 CF-Connecting-IP 的支持,这些都是很好的改进。

但是,代码中存在两个问题:

  1. X-Real-IP 请求头进行了重复检查。
  2. 在获取到真实IP后,错误地返回了代理服务器的IP,并且在未获取到IP时没有正确回退。

我已经在代码中提出了具体的修改建议,请查看。修复这些问题后,获取IP的逻辑将更加健壮和正确。

Comment on lines 181 to 184
if (CharSequenceUtil.isNotBlank(ip)) {
return request.getRemoteHost();
}
return ip;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

这里的逻辑是错误的。如果通过请求头成功获取了客户端的真实IP地址(存储在 ip 变量中),代码却返回了 request.getRemoteHost() 的值。getRemoteHost() 返回的是直接连接到服务器的客户端(可能是代理服务器)的主机名,这会丢失真实的客户端IP。此外,如果所有请求头都没有IP地址,该方法将返回一个空字符串,而不是回退到 request.getRemoteAddr()

建议修改逻辑为:如果从请求头中获取到IP,则返回该IP;否则,回退到使用 request.getRemoteAddr()。使用 getRemoteAddr() 而不是 getRemoteHost() 可以避免潜在的DNS查询,性能更好。

Suggested change
if (CharSequenceUtil.isNotBlank(ip)) {
return request.getRemoteHost();
}
return ip;
if (CharSequenceUtil.isNotBlank(ip)) {
return ip;
}
return request.getRemoteAddr();

Comment on lines 178 to 180
if (CharSequenceUtil.isBlank(ip)) {
ip = request.getHeader("X-Real-IP");
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

HttpServletRequest.getHeader() 方法对头名称不区分大小写。因此,在第164行已经检查了 X-real-ip 之后,这里再次检查 X-Real-IP 是多余的。建议删除这个多余的检查。

@jamebal
Copy link
Owner Author

jamebal commented Dec 22, 2025

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

本次 PR 通过将获取客户端 IP 的逻辑重构到一个独立的 IPUtil 工具类中,优化了相关逻辑。这提升了代码的结构性和可维护性。新的实现更加健壮和高效,例如使用 request.getRemoteAddr() 替代 request.getRemoteHost() 来避免潜在的 DNS 查询。此外,添加了 @PreDestroy 方法来正确关闭 ipSearcher 资源,这是一个很好的实践。总体而言,这些变更是积极的。我提供了一些小的建议以进一步改进代码。

try {
ipSearcher.close();
} catch (IOException e) {
log.error("failed to close searcher\n", e);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

日志消息字符串末尾的 \n 是多余的。大多数日志框架会自动为每条日志消息附加换行符。移除它可以使日志输出更整洁,并与其他日志消息保持一致。

Suggested change
log.error("failed to close searcher\n", e);
log.error("failed to close searcher", e);

Comment on lines 7 to 8
public class IPUtil {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

对于只包含静态方法的工具类,最好将其声明为 final 并添加一个私有构造函数,以防止被继承和实例化。

Suggested change
public class IPUtil {
public final class IPUtil {
private IPUtil() {}

}

private static String extractFirstIp(String ip, String header) {
if (header.contains("Forwarded") && ip.contains(",")) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

extractFirstIp 方法中,使用 header.contains("Forwarded") 来判断是否为 X-Forwarded-For 头可能不够健壮。HTTP头是大小写不敏感的,而且 contains 可能会匹配到其他包含 "Forwarded" 的非标准头。建议使用 equalsIgnoreCase 进行精确匹配。

Suggested change
if (header.contains("Forwarded") && ip.contains(",")) {
if ("X-Forwarded-For".equalsIgnoreCase(header) && ip.contains(",")) {

@jamebal
Copy link
Owner Author

jamebal commented Dec 22, 2025

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

本次PR将获取客户端IP的逻辑重构到了新的工具类 IPUtil 中,并优化了实现,整体结构更加清晰。同时,对 region2IpInfo 方法进行了优化,并添加了 @PreDestroy 方法来确保资源被正确释放,这些都是很好的改进。

我在代码中发现两个潜在的正确性问题,并给出了修复建议:

  1. LogService.region2IpInfo 方法中,split 方法的 limit 参数使用不当,可能导致IP区域信息解析错误。
  2. IPUtil.getClientIP 方法中,处理包含前导逗号的IP列表(如 ', 1.2.3.4')时存在缺陷,可能导致获取不到正确的IP地址。

请查看具体的审查评论。

if (!Constants.REGION_DEFAULT.equals(operators)) {
ipInfo.setOperators(operators);

String[] parts = IPUtil.SPLIT_PATTERN.split(region, IPUtil.REGION_LENGTH);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

此处 split 方法的 limit 参数使用不当。当 region 字符串包含的 | 分隔符超过预期(4个)时,split(region, 5) 会将第五个分隔符之后的所有内容都放入最后一个数组元素中,导致 parts.length 仍然为5,从而绕过了长度检查,但最后一个元素的内容是错误的。

例如,如果 region"a|b|c|d|e|f"parts 将是 ["a", "b", "c", "d", "e|f"],这会导致后续的IP区域信息解析错误。

建议移除 limit 参数,让 split 方法分割所有部分,然后通过数组长度检查来保证数据的正确性,这与之前的逻辑行为一致。

Suggested change
String[] parts = IPUtil.SPLIT_PATTERN.split(region, IPUtil.REGION_LENGTH);
String[] parts = IPUtil.SPLIT_PATTERN.split(region);

Comment on lines 30 to 38
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();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

当前的 getClientIP 方法在处理某些边缘情况时存在逻辑缺陷。当HTTP头中的IP地址字符串包含前导逗号(例如 X-Forwarded-For: , 1.2.3.4)时,isValidIp(ip) 会返回 true,但 extractFirstIp(ip) 会返回一个空字符串,导致此方法最终返回一个空字符串而不是正确的IP地址。

为了修复这个问题并使逻辑更健壮,建议调整为先提取第一个IP,然后再对提取出的IP进行有效性验证。这样可以正确处理上述边缘情况,并确保只有有效的IP地址被返回。

Suggested change
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();
}
public static String getClientIP(HttpServletRequest request) {
for (String header : IP_HEADERS) {
String ip = request.getHeader(header);
if (ip == null) {
continue;
}
String firstIp = extractFirstIp(ip);
if (isValidIp(firstIp)) {
return firstIp;
}
}
return request.getRemoteAddr();
}

@jamebal
Copy link
Owner Author

jamebal commented Dec 22, 2025

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

本次 PR 对获取客户端 IP 地址的逻辑进行了重构,将其提取到了一个新的工具类 IPUtil 中,这是一个很好的改进。新的实现通过循环遍历请求头列表、使用 indexOf 代替 split 等方式,提高了代码的可读性、可维护性和性能。同时,为 LogService 添加了 @PreDestroy 方法来关闭 ipSearcher 资源,避免了潜在的资源泄漏。整体来看,代码质量有显著提升。

Comment on lines 178 to 180
if (region == null || region.isEmpty()) {
return ipInfo;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

为了使代码更健壮,建议使用 CharSequenceUtil.isBlank(region) 来代替 region == null || region.isEmpty()isBlank 可以处理 null、空字符串以及只包含空白字符的字符串(例如 " ")。这个类中已经导入并使用了 cn.hutool.core.text.CharSequenceUtil,因此保持代码风格一致性也是一个好处。

Suggested change
if (region == null || region.isEmpty()) {
return ipInfo;
}
if (CharSequenceUtil.isBlank(region)) {
return ipInfo;
}

@jamebal
Copy link
Owner Author

jamebal commented Dec 22, 2025

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

本次 PR 将获取客户端 IP 的逻辑重构到了一个新的工具类 IPUtil 中,这是一个很好的改进,使得代码更加清晰和可维护。新的实现修复了旧代码中的一些问题(例如重复检查和不正确的 header 处理顺序),并增加了对 Cloudflare header 的支持。同时,为 LogService 添加了 @PreDestroy 方法来正确关闭 ipSearcher,避免了资源泄漏。我有一个关于增强 IP 地址验证的建议,以提高代码的健壮性和安全性。

Comment on lines +58 to +62
private static boolean isValidIp(String ip) {
return ip != null
&& !ip.isEmpty()
&& !"unknown".equalsIgnoreCase(ip);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

当前的IP验证逻辑 (isValidIp) 过于宽松,只检查了字符串是否为非空且不等于 'unknown'。这可能允许格式不正确的字符串(例如,恶意注入的文本)被当作有效的IP地址处理和记录,存在潜在的日志注入风险。

为了增强安全性和数据准确性,建议使用 hutool 库中的 NetUtil.isIP() 方法来进行更严格的IP地址格式验证。这将确保只接受有效的IPv4或IPv6地址。

为了方便阅读,建议在文件顶部添加 import cn.hutool.core.net.NetUtil;,然后在代码中使用 NetUtil.isIP(ip)

    private static boolean isValidIp(String ip) {
        return ip != null
                && !ip.isEmpty()
                && !"unknown".equalsIgnoreCase(ip)
                && cn.hutool.core.net.NetUtil.isIP(ip);
    }

@jamebal jamebal merged commit 0cd093f into master Dec 24, 2025
35 of 37 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

improvement Improvement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants