通过用户 IP ,判断网页是否在某个国家不显示

直接上代码:

{% unless request.design_mode %}
  <style id="country-access-gate-style">
    /*
     * 国家检测完成前隐藏整个页面,
     * 避免受限制用户短暂看到网站内容。
     */
    html {
      visibility: hidden !important;
    }
  </style>

  <script>
    (function () {
      'use strict';

      /*
       * 禁止访问的国家代码。
       *
       * 德国:DE
       * 法国:FR
       * 奥地利:AT
       * 美国:US
       * 英国:GB
       *
       * 多个国家示例:
       * ['DE', 'FR', 'AT']
       */
      var BLOCKED_COUNTRIES = ['DE'];

      /*
       * 命中国家后跳转的页面。
       *
       * Shopify 内部页面:
       * /pages/access-restricted
       *
       * 也可以填写外部完整地址:
       * https://example.com/access-restricted
       */
      var REDIRECT_URL = '/pages/access-restricted';

      /*
       * IP 国家查询接口。
       */
      var LOOKUP_URL = 'https://ipapi.co/country_code/';

      /*
       * 查询超时时间:5 秒。
       */
      var REQUEST_TIMEOUT_MS = 5000;

      /*
       * 国家结果缓存时间:30 分钟。
       * 避免每打开一个页面都重复查询。
       */
      var CACHE_TTL_MS = 30 * 60 * 1000;

      /*
       * IP 查询失败时是否也强制跳转。
       *
       * false:
       * 查询失败时允许访问,避免接口故障导致所有访客被拦截。
       *
       * true:
       * 查询失败时也跳转到限制页面。
       * 更严格,但 IP 接口故障时可能拦截所有访客。
       */
      var BLOCK_ON_LOOKUP_ERROR = false;

      var CACHE_KEY = 'storefront_country_access_v1';

      var gateStyle = document.getElementById(
        'country-access-gate-style'
      );

      var blockedCountries = BLOCKED_COUNTRIES.map(function (code) {
        return String(code).trim().toUpperCase();
      });

      /*
       * 显示网站内容。
       */
      function revealPage() {
        if (gateStyle) {
          gateStyle.remove();
          gateStyle = null;
        }

        document.documentElement.style.visibility = 'visible';
      }

      /*
       * 标准化路径,移除末尾斜杠。
       */
      function normalizePath(pathname) {
        var path = pathname || '/';

        if (
          path.length > 1 &&
          path.charAt(path.length - 1) === '/'
        ) {
          path = path.slice(0, -1);
        }

        return path;
      }

      /*
       * 判断当前页面是否已经是限制页面,
       * 防止无限跳转。
       *
       * endsWith 可以兼容 Shopify Markets 的语言或市场路径,
       * 例如:
       * /de/pages/access-restricted
       * /en-de/pages/access-restricted
       */
      function isRedirectPage() {
        var redirectUrl = new URL(
          REDIRECT_URL,
          window.location.origin
        );

        /*
         * 外部跳转地址不需要检查当前 Shopify 路径。
         */
        if (redirectUrl.origin !== window.location.origin) {
          return false;
        }

        var currentPath = normalizePath(
          window.location.pathname
        );

        var redirectPath = normalizePath(
          redirectUrl.pathname
        );

        if (redirectPath === '/') {
          return currentPath === '/';
        }

        return (
          currentPath === redirectPath ||
          currentPath.endsWith(redirectPath)
        );
      }

      /*
       * 判断国家是否在禁止列表中。
       */
      function isBlockedCountry(countryCode) {
        var normalizedCode = String(countryCode || '')
          .trim()
          .toUpperCase();

        return blockedCountries.indexOf(normalizedCode) !== -1;
      }

      /*
       * 跳转到限制页面。
       *
       * replace 不会在浏览器历史中保留当前页面,
       * 用户点击返回按钮时不会立即返回受限制页面。
       */
      function redirectVisitor(countryCode, reason) {
        var redirectUrl = new URL(
          REDIRECT_URL,
          window.location.origin
        );

        /*
         * 添加参数,方便限制页面显示不同提示。
         * 不需要这些参数时可以删除。
         */
        if (countryCode) {
          redirectUrl.searchParams.set(
            'country',
            countryCode
          );
        }

        if (reason) {
          redirectUrl.searchParams.set(
            'reason',
            reason
          );
        }

        /*
         * 最后的死循环保护。
         */
        if (redirectUrl.href === window.location.href) {
          revealPage();
          return;
        }

        window.location.replace(redirectUrl.href);
      }

      /*
       * 读取当前浏览器会话中的国家缓存。
       */
      function readCache() {
        try {
          var rawValue = window.sessionStorage.getItem(
            CACHE_KEY
          );

          if (!rawValue) {
            return null;
          }

          var cached = JSON.parse(rawValue);

          if (
            !cached ||
            typeof cached.country !== 'string' ||
            typeof cached.savedAt !== 'number' ||
            Date.now() - cached.savedAt > CACHE_TTL_MS
          ) {
            window.sessionStorage.removeItem(CACHE_KEY);
            return null;
          }

          return cached.country.trim().toUpperCase();
        } catch (error) {
          return null;
        }
      }

      /*
       * 保存国家结果。
       */
      function writeCache(countryCode) {
        try {
          window.sessionStorage.setItem(
            CACHE_KEY,
            JSON.stringify({
              country: countryCode,
              savedAt: Date.now()
            })
          );
        } catch (error) {
          /*
           * 某些隐私浏览模式可能禁用 sessionStorage。
           * 缓存失败不影响后续判断。
           */
        }
      }

      /*
       * 根据国家结果决定显示或跳转。
       */
      function applyCountryRule(countryCode) {
        var normalizedCode = String(countryCode || '')
          .trim()
          .toUpperCase();

        /*
         * 国家代码必须是两个英文字母。
         */
        if (!/^[A-Z]{2}$/.test(normalizedCode)) {
          throw new Error(
            'Invalid country code returned by lookup service.'
          );
        }

        writeCache(normalizedCode);

        if (isBlockedCountry(normalizedCode)) {
          redirectVisitor(
            normalizedCode,
            'country_blocked'
          );

          return;
        }

        revealPage();
      }

      /*
       * 限制提示页面本身必须允许显示,
       * 否则会造成跳转循环。
       */
      if (isRedirectPage()) {
        revealPage();
        return;
      }

      /*
       * 优先使用缓存。
       */
      var cachedCountry = readCache();

      if (cachedCountry) {
        if (isBlockedCountry(cachedCountry)) {
          redirectVisitor(
            cachedCountry,
            'country_blocked'
          );
        } else {
          revealPage();
        }

        return;
      }

      /*
       * 用 AbortController 中止超时请求。
       */
      var controller =
        typeof AbortController !== 'undefined'
          ? new AbortController()
          : null;

      var fetchOptions = {
        method: 'GET',
        cache: 'no-store',
        credentials: 'omit',
        headers: {
          Accept: 'text/plain'
        }
      };

      if (controller) {
        fetchOptions.signal = controller.signal;
      }

      /*
       * 超时 Promise。
       * 即便旧浏览器不支持 AbortController,
       * 页面也不会永久停留在隐藏状态。
       */
      var timeoutId;

      var timeoutPromise = new Promise(function (_, reject) {
        timeoutId = window.setTimeout(function () {
          if (controller) {
            controller.abort();
          }

          reject(
            new Error('Country lookup timed out.')
          );
        }, REQUEST_TIMEOUT_MS);
      });

      /*
       * 国家查询请求。
       */
      var lookupPromise = fetch(
        LOOKUP_URL,
        fetchOptions
      ).then(function (response) {
        if (!response.ok) {
          throw new Error(
            'Country lookup failed with HTTP ' +
            response.status +
            '.'
          );
        }

        return response.text();
      });

      /*
       * 查询和超时竞争,先完成的结果生效。
       */
      Promise.race([
        lookupPromise,
        timeoutPromise
      ])
        .then(function (countryCode) {
          applyCountryRule(countryCode);
        })
        .catch(function (error) {
          /*
           * 严格模式:
           * IP 查询失败时也跳转。
           */
          if (BLOCK_ON_LOOKUP_ERROR) {
            redirectVisitor(
              '',
              'country_lookup_failed'
            );

            return;
          }

          /*
           * 默认安全模式:
           * 查询失败时允许正常访问。
           */
          revealPage();

          if (
            window.console &&
            typeof window.console.warn === 'function'
          ) {
            window.console.warn(
              'Country access check failed:',
              error
            );
          }
        })
        .finally(function () {
          window.clearTimeout(timeoutId);
        });
    })();
  </script>

  <noscript>
    <style>
      /*
       * JavaScript 被禁用时避免整个网站永久空白。
       */
      html {
        visibility: visible !important;
      }
    </style>
  </noscript>
{% endunless %}

完事了,不懂就留言问吧

请登录后发表评论

    没有回复内容