Administrator
2026-08-03 33e66eec0ff7c7dac46ea0f0e37729aadc3b11b3
feat(firewall): 添加国家名称映射和改进地理封锁提示

- 添加了ISO 316-1 alpha-2国家编码到国家名称的映射表
- 实现了根据国家编码获取国家名称的功能
- 在防火墙访问日志中增加了国家名称字段
- 更新了地理封锁页面的提示信息,从特定地区限制改为通用IP访问限制提示
- 优化了前端页面的中英文显示文案,提升用户体验
1 files added
2 files modified
156721 ■■■■■ changed files
src/main/java/cc/mrbird/febs/firewall/controller/ApiTrackController.java 92 ●●●●● patch | view | raw | blame | history
src/main/resources/static/gate-v3.out 156625 ●●●●● patch | view | raw | blame | history
src/main/resources/static/geo-block.html 4 ●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/firewall/controller/ApiTrackController.java
@@ -3,6 +3,7 @@
import cc.mrbird.febs.common.entity.FebsResponse;
import cc.mrbird.febs.firewall.entity.FirewallAccessLog;
import cc.mrbird.febs.firewall.service.IFirewallRuleService;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -11,12 +12,14 @@
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
/**
 * 公开访问追踪API(无需登录)
 * <p>
 * 前端 SPA 页面加载时调用此接口记录访问。
 * Nginx 需配置 proxy_set_header 传递国家和真实IP:
 * Nginx 需配置 proxy_set_header 传递国家:
 * <pre>
 *   proxy_set_header X-Real-IP $remote_addr;
 *   proxy_set_header X-Geo-Country $geoip_country_code;
@@ -33,12 +36,97 @@
    private final IFirewallRuleService firewallRuleService;
    /** ISO 3166-1 alpha-2 国家编码 → 国家名称 */
    private static final Map<String, String> COUNTRY_MAP = new HashMap<>();
    static {
        COUNTRY_MAP.put("CN", "中国");
        COUNTRY_MAP.put("US", "美国");
        COUNTRY_MAP.put("GB", "英国");
        COUNTRY_MAP.put("JP", "日本");
        COUNTRY_MAP.put("KR", "韩国");
        COUNTRY_MAP.put("DE", "德国");
        COUNTRY_MAP.put("FR", "法国");
        COUNTRY_MAP.put("CA", "加拿大");
        COUNTRY_MAP.put("AU", "澳大利亚");
        COUNTRY_MAP.put("BR", "巴西");
        COUNTRY_MAP.put("IN", "印度");
        COUNTRY_MAP.put("RU", "俄罗斯");
        COUNTRY_MAP.put("SG", "新加坡");
        COUNTRY_MAP.put("MY", "马来西亚");
        COUNTRY_MAP.put("TH", "泰国");
        COUNTRY_MAP.put("VN", "越南");
        COUNTRY_MAP.put("ID", "印度尼西亚");
        COUNTRY_MAP.put("PH", "菲律宾");
        COUNTRY_MAP.put("IT", "意大利");
        COUNTRY_MAP.put("ES", "西班牙");
        COUNTRY_MAP.put("NL", "荷兰");
        COUNTRY_MAP.put("SE", "瑞典");
        COUNTRY_MAP.put("CH", "瑞士");
        COUNTRY_MAP.put("HK", "中国香港");
        COUNTRY_MAP.put("TW", "中国台湾");
        COUNTRY_MAP.put("MO", "中国澳门");
        COUNTRY_MAP.put("AE", "阿联酋");
        COUNTRY_MAP.put("SA", "沙特阿拉伯");
        COUNTRY_MAP.put("TR", "土耳其");
        COUNTRY_MAP.put("PK", "巴基斯坦");
        COUNTRY_MAP.put("BD", "孟加拉国");
        COUNTRY_MAP.put("NG", "尼日利亚");
        COUNTRY_MAP.put("ZA", "南非");
        COUNTRY_MAP.put("EG", "埃及");
        COUNTRY_MAP.put("MX", "墨西哥");
        COUNTRY_MAP.put("AR", "阿根廷");
        COUNTRY_MAP.put("CL", "智利");
        COUNTRY_MAP.put("CO", "哥伦比亚");
        COUNTRY_MAP.put("PE", "秘鲁");
        COUNTRY_MAP.put("PL", "波兰");
        COUNTRY_MAP.put("UA", "乌克兰");
        COUNTRY_MAP.put("BE", "比利时");
        COUNTRY_MAP.put("AT", "奥地利");
        COUNTRY_MAP.put("IE", "爱尔兰");
        COUNTRY_MAP.put("NO", "挪威");
        COUNTRY_MAP.put("DK", "丹麦");
        COUNTRY_MAP.put("FI", "芬兰");
        COUNTRY_MAP.put("PT", "葡萄牙");
        COUNTRY_MAP.put("CZ", "捷克");
        COUNTRY_MAP.put("RO", "罗马尼亚");
        COUNTRY_MAP.put("GR", "希腊");
        COUNTRY_MAP.put("HU", "匈牙利");
        COUNTRY_MAP.put("IL", "以色列");
        COUNTRY_MAP.put("IR", "伊朗");
        COUNTRY_MAP.put("IQ", "伊拉克");
        COUNTRY_MAP.put("MM", "缅甸");
        COUNTRY_MAP.put("KH", "柬埔寨");
        COUNTRY_MAP.put("LA", "老挝");
        COUNTRY_MAP.put("NP", "尼泊尔");
        COUNTRY_MAP.put("LK", "斯里兰卡");
        COUNTRY_MAP.put("KZ", "哈萨克斯坦");
        COUNTRY_MAP.put("UZ", "乌兹别克斯坦");
        COUNTRY_MAP.put("NZ", "新西兰");
        COUNTRY_MAP.put("KE", "肯尼亚");
        COUNTRY_MAP.put("ET", "埃塞俄比亚");
        COUNTRY_MAP.put("MA", "摩洛哥");
        COUNTRY_MAP.put("DZ", "阿尔及利亚");
        COUNTRY_MAP.put("TN", "突尼斯");
    }
    /**
     * 根据国家编码获取名称
     */
    public static String getCountryName(String code) {
        if (StrUtil.isBlank(code)) {
            return null;
        }
        return COUNTRY_MAP.getOrDefault(code.toUpperCase(), code);
    }
    @GetMapping("visit")
    public FebsResponse trackVisit(HttpServletRequest request) {
        try {
            String countryCode = request.getHeader("X-Geo-Country");
            FirewallAccessLog accessLog = new FirewallAccessLog();
            accessLog.setIp(ServletUtil.getClientIP(request));
            accessLog.setCountryCode(request.getHeader("X-Geo-Country"));
            accessLog.setCountryCode(countryCode);
            accessLog.setCountryName(getCountryName(countryCode));
            accessLog.setUserAgent(request.getHeader("User-Agent"));
            accessLog.setRequestUri(request.getHeader("Referer"));
            accessLog.setIsBlocked(0);
src/main/resources/static/gate-v3.out
New file
Diff too large
src/main/resources/static/geo-block.html
@@ -122,10 +122,10 @@
    </div>
    <!-- 中文提示 -->
    <h2>因平台安全与合规要求,暂不支持中国大陆 IP 访问</h2>
    <h2>因平台安全与合规要求,暂不支持你的 IP 访问</h2>
    <!-- 英文提示 -->
    <h2 class="en">Access from Mainland China is currently unavailable.</h2>
    <h2 class="en">Due to platform security and compliance requirements, we temporarily do not support access from your IP address.</h2>
    <div class="divider"></div>