Greasy Fork

Greasy Fork is available in English.

JPKI在留申请在线系统证明书模拟提交

模拟点击按钮后直接跳过证书验证并进行下一步操作;支持保存证书内容,可折叠并记住折叠状态

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         JPKI在留申请在线系统证明书模拟提交
// @name:ja      JPKI在留申請オンラインシステムにおける証明書の模擬提出
// @namespace    https://www.ras-immi.moj.go.jp/
// @version      1.0.5
// @description  模拟点击按钮后直接跳过证书验证并进行下一步操作;支持保存证书内容,可折叠并记住折叠状态
// @description:ja ボタンをクリックした後、証明書の検証をスキップして次の操作に進む。証明書の内容を保存し、折りたたんで折りたたみ状態を記憶することができます。
// @match        *://www.ras-immi.moj.go.jp/WC01/*
// @match        *://app.api.myna.go.jp/online-authorization-web/login/check-client*
// @license MIT
// @grant        none
// ==/UserScript==

(function () {
  "use strict";

  // 检查当前URL,如果是myna.go.jp的登录检查页面,则修改loginType
  if (window.location.href.includes('app.api.myna.go.jp/online-authorization-web/login/check-client')) {
    modifyLoginType();
    return; // 在myna页面上只执行loginType修改,不执行其他功能
  }

  // 1. 读取 localStorage 中已经保存的证书信息
  let mockAuthCert = localStorage.getItem("mockAuthCert") || "";
  let mockSignCert = localStorage.getItem("mockSignCert") || "";

  // 2. 读取面板折叠状态(默认 "true":显示面板;"false":折叠)
  // 如果从未设置过,就将其初始化为 "true"
  let panelOpen = localStorage.getItem("panelOpen");
  if (panelOpen === null) {
    panelOpen = "true";
    localStorage.setItem("panelOpen", "true");
  }

  // 3. 将“现代化”CSS插入 <head> 中
  injectModernStyle();

  // 4. 创建证书设置面板 和 小圆标按钮
  const panel = createSettingPanel();
  const openBtn = createOpenButton();

  // 5. 根据 localStorage 中的 panelOpen 值,设置初始显示/隐藏
  if (panelOpen === "true") {
    // 显示面板,隐藏小圆标
    panel.style.display = "block";
    openBtn.style.display = "none";
  } else {
    // 隐藏面板,显示小圆标
    panel.style.display = "none";
    openBtn.style.display = "block";
  }

  // 6. 绑定页面按钮(登录/届出等)事件
  bindButtonEvents();

  /**
   * 面板样式
   */
  function injectModernStyle() {
    const styleEl = document.createElement("style");
    styleEl.innerHTML = `
            /* 悬浮面板总体样式 */
            #custom-cert-panel {
                position: fixed;
                right: 20px;
                bottom: 20px;
                width: 320px;
                padding: 15px 15px 10px;
                background-color: #fff;
                border: 1px solid #dadada;
                border-radius: 8px;
                box-shadow: 0 4px 12px rgba(0,0,0,0.15);
                font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
                font-size: 14px;
                z-index: 99999;
                color: #333;
            }
            /* 面板标题 */
            #custom-cert-panel h2 {
                margin: 0 0 10px 0;
                font-size: 16px;
                font-weight: 600;
                padding-right: 24px; /* 给右上角的关闭按钮留空间 */
            }
            /* 通用标签样式 */
            .modern-label {
                display: block;
                margin-top: 10px;
                font-weight: 600;
            }
            /* 文本域样式 */
            .modern-textarea {
                width: 100%;
                height: 50px;
                resize: vertical;
                padding: 6px 8px;
                margin-top: 4px;
                border: 1px solid #ccc;
                border-radius: 4px;
                font-family: inherit;
                font-size: 14px;
                box-sizing: border-box;
            }
            /* 按钮容器 */
            .button-container {
                margin-top: 12px;
                text-align: right;
            }
            /* 通用按钮样式 */
            .modern-button {
                padding: 6px 12px;
                border: none;
                border-radius: 4px;
                background-color: #387ef5;
                color: #fff;
                cursor: pointer;
                font-size: 14px;
                margin-right: 10px;
            }
            .modern-button:hover {
                background-color: #226ad4;
            }
            .modern-button:active {
                background-color: #1c56a5;
            }
            /* 关闭按钮 (右上角的 × ) */
            .close-btn {
                position: absolute;
                top: 10px;
                right: 10px;
                cursor: pointer;
                font-size: 18px;
                color: #999;
            }
            .close-btn:hover {
                color: #666;
            }

            /* 小圆标按钮样式(折叠后的“+”按钮) */
            #open-panel-button {
                position: fixed;
                right: 20px;
                bottom: 20px;
                width: 45px;
                height: 45px;
                background-color: #387ef5;
                color: #fff;
                border-radius: 22px;
                cursor: pointer;
                text-align: center;
                line-height: 40px;
                font-weight: bold;
                font-size: 26px;
                box-shadow: 0 4px 12px rgba(0,0,0,0.15);
                user-select: none;
                z-index: 99998;
            }
            #open-panel-button:hover {
                background-color: #226ad4;
            }
            #open-panel-button:active {
                background-color: #1c56a5;
            }
        `;
    document.head.appendChild(styleEl);
  }

  /**
   * 面板
   */
  function createSettingPanel() {
    // 容器
    const panel = document.createElement("div");
    panel.id = "custom-cert-panel";

    // 关闭按钮
    const closeBtn = document.createElement("div");
    closeBtn.className = "close-btn";
    closeBtn.innerHTML = "&times;";
    closeBtn.addEventListener("click", () => {
      // 隐藏面板
      panel.style.display = "none";
      // 显示小圆标
      openBtn.style.display = "block";
      // 记住折叠状态
      localStorage.setItem("panelOpen", "false");
    });
    panel.appendChild(closeBtn);

    // 标题
    const title = document.createElement("h2");
    title.innerText = "模拟证书设置";
    panel.appendChild(title);

    // 利用者証明書
    const labelAuth = document.createElement("label");
    labelAuth.className = "modern-label";
    labelAuth.innerText = "利用者証明書:";
    panel.appendChild(labelAuth);

    const inputAuth = document.createElement("textarea");
    inputAuth.className = "modern-textarea";
    inputAuth.value = mockAuthCert;
    panel.appendChild(inputAuth);

    // 署名証明書
    const labelSign = document.createElement("label");
    labelSign.className = "modern-label";
    labelSign.innerText = "署名証明書:";
    panel.appendChild(labelSign);

    const inputSign = document.createElement("textarea");
    inputSign.className = "modern-textarea";
    inputSign.value = mockSignCert;
    panel.appendChild(inputSign);

    // 按钮容器
    const buttonContainer = document.createElement("div");
    buttonContainer.className = "button-container";

    // 保存按钮
    const saveButton = document.createElement("button");
    saveButton.className = "modern-button";
    saveButton.innerText = "保存";
    saveButton.addEventListener("click", function () {
      let authVal = inputAuth.value.trim();
      let signVal = inputSign.value.trim();

      // 如果包含 BEGIN/END,则自动去除头尾
      const regCert = /-----BEGIN CERTIFICATE-----([\s\S]*?)-----END CERTIFICATE-----/i;

      // 去除認証証明書头尾并去除换行
      if (regCert.test(authVal)) {
        authVal = authVal.replace(regCert, "$1").trim();
        authVal = authVal.replace(/\r?\n/g, "");
      }
      // 去除署名証明書头尾并去除换行
      if (regCert.test(signVal)) {
        signVal = signVal.replace(regCert, "$1").trim();
        signVal = signVal.replace(/\r?\n/g, "");
      }

      // 去除空行空格
      authVal = authVal.replace(/\s+/g, "");
      authVal = authVal.replace(/^\s*[\r\n]/gm, "");
      signVal = signVal.replace(/\s+/g, "");
      signVal = signVal.replace(/^\s*[\r\n]/gm, "");

      // 存入 localStorage
      localStorage.setItem("mockAuthCert", authVal);
      localStorage.setItem("mockSignCert", signVal);

      // 同步脚本内的变量
      mockAuthCert = authVal;
      mockSignCert = signVal;

      alert("证书内容已保存!(已自动去除 CERT 头尾和多余换行)");
    });
    buttonContainer.appendChild(saveButton);

    // 清空按钮
    const clearButton = document.createElement("button");
    clearButton.className = "modern-button";
    clearButton.innerText = "清空";
    clearButton.addEventListener("click", function () {
      localStorage.removeItem("mockAuthCert");
      localStorage.removeItem("mockSignCert");
      mockAuthCert = "";
      mockSignCert = "";
      inputAuth.value = "";
      inputSign.value = "";
      alert("证书内容已清空!");
    });
    buttonContainer.appendChild(clearButton);

    panel.appendChild(buttonContainer);

    // 把面板插入页面
    document.body.appendChild(panel);

    return panel;
  }

  /**
   * 创建折叠后的“小圆标”按钮,用于重新展开面板
   */
  function createOpenButton() {
    const openBtn = document.createElement("div");
    openBtn.id = "open-panel-button";
    openBtn.innerHTML = "+";
    // 先不设置 display: none,这里会通过脚本动态决定
    openBtn.style.display = "none";

    openBtn.addEventListener("click", () => {
      // 显示面板
      panel.style.display = "block";
      // 隐藏小圆标
      openBtn.style.display = "none";
      // 记住展开状态
      localStorage.setItem("panelOpen", "true");
    });

    document.body.appendChild(openBtn);
    return openBtn;
  }

  /**
   * 绑定页面按钮(登录/届出)事件:当用户点击时,拦截并自动填充表单
   */
  function bindButtonEvents() {
    // 监听 clickLogInKjnBtn
    waitForElement("[name=clickLogInKjnBtn]", function (button) {
      console.log("找到按钮 clickLogInKjnBtn");
      button.addEventListener("click", function (event) {
        event.preventDefault();
        simulateCertSuccess("clickLogInKjnBtn");
      });
    });

    // 监听 clickTurkKjnBtn
    waitForElement("[name=clickTurkKjnBtn]", function (button) {
      console.log("找到按钮 clickTurkKjnBtn");
      button.addEventListener("click", function (event) {
        event.preventDefault();
        simulateCertSuccess("clickTurkKjnBtn");
      });
    });
  }

  /**
   * 自动填充并提交表单的核心逻辑
   */
  function simulateCertSuccess(btnname) {
    console.log("模拟证书处理...");

    // 没有填写必要证书时,阻止操作
    if (!mockAuthCert) {
      alert("模拟证书:未填写認証用証明書");
      return;
    }
    if (btnname === "clickTurkKjnBtn" && !mockSignCert) {
      alert("模拟证书:未填写署名用証明書");
      return;
    }

    // 将证书写入隐藏字段(根据实际页面的字段名称来修改)
    document.getElementById("hdnUsrCert").value = mockAuthCert;
    if (btnname === "clickTurkKjnBtn") {
      document.getElementById("hdnSignCert").value = mockSignCert;
    }

    // 创建隐藏按钮用于提交
    const form = document.getElementById("WCAAS010Dto"); // 根据实际表单 ID 修改
    let input = document.getElementById(btnname);
    if (!input) {
      input = document.createElement("input");
      input.type = "hidden";
      input.id = btnname;
      input.name = btnname;
      form.appendChild(input);
    }

    // 提交表单
    form.submit();
    console.log("表单已提交!按钮:" + btnname);
  }

  /**
   * 等待某个选择器元素出现
   */
  function waitForElement(selector, callback) {
    const element = document.querySelector(selector);
    if (element) {
      callback(element);
    } else {
      setTimeout(() => waitForElement(selector, callback), 500);
    }
  }

  /**
   * 修改loginType值为0
   */
  function modifyLoginType() {
    waitForElement("#loginType", function(element) {
      console.log("找到loginType元素,修改值为0");
      element.value = "0";
      console.log("loginType值已修改为:", element.value);
    });
  }
})();