Electron协议处理器设置Skill electron-protocol-handler-setup

本技能用于为基于Electron框架的桌面应用程序,在Windows、macOS和Linux操作系统上,实现自定义URL协议(如myapp://)的注册、处理和安全验证。它支持深度链接、应用程序间通信、单实例强制执行,并提供跨平台的配置方案和代码实现,是构建现代化、可深度集成的桌面应用的关键技术。关键词:Electron, 深度链接, 自定义协议, URL Scheme, 桌面应用, 跨平台, 协议处理器, 安全验证。

前端开发 0 次安装 0 次浏览 更新于 2/25/2026

name: electron-protocol-handler-setup description: 为Electron应用程序注册和处理跨平台的自定义URL协议(深度链接) allowed-tools: Read, Write, Edit, Bash, Glob, Grep tags: [electron, deep-linking, protocol-handler, url-scheme, desktop]

electron-protocol-handler-setup

为Electron应用程序在Windows、macOS和Linux上注册和处理自定义URL协议(深度链接)。此技能使应用程序能够响应自定义URL方案,如myapp://,以实现深度链接和应用程序间通信。

能力

  • 在操作系统级别注册自定义协议处理器
  • 在运行的应用程序中处理协议URL
  • 为协议注册配置electron-builder
  • 实现安全的URL解析和验证
  • 处理应用程序启动时的协议激活
  • 支持带协议处理的单实例强制执行
  • 生成平台特定的注册脚本
  • 在开发环境中测试协议处理

输入模式

{
  "type": "object",
  "properties": {
    "projectPath": {
      "type": "string",
      "description": "Electron项目根目录的路径"
    },
    "protocols": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "scheme": { "type": "string", "description": "协议方案(例如,'myapp')" },
          "name": { "type": "string", "description": "人类可读的名称" },
          "role": { "enum": ["Viewer", "Editor", "Shell", "None"], "default": "Viewer" }
        },
        "required": ["scheme", "name"]
      }
    },
    "singleInstance": {
      "type": "boolean",
      "description": "通过协议中继强制执行单实例",
      "default": true
    },
    "securityOptions": {
      "type": "object",
      "properties": {
        "validateUrls": { "type": "boolean", "default": true },
        "allowedHosts": { "type": "array", "items": { "type": "string" } },
        "sanitizeParams": { "type": "boolean", "default": true }
      }
    },
    "targetPlatforms": {
      "type": "array",
      "items": { "enum": ["win32", "darwin", "linux"] }
    }
  },
  "required": ["projectPath", "protocols"]
}

输出模式

{
  "type": "object",
  "properties": {
    "success": { "type": "boolean" },
    "files": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "path": { "type": "string" },
          "description": { "type": "string" }
        }
      }
    },
    "configuration": {
      "type": "object",
      "properties": {
        "electronBuilder": { "type": "object" },
        "packageJson": { "type": "object" }
      }
    },
    "testUrls": {
      "type": "array",
      "items": { "type": "string" }
    }
  },
  "required": ["success"]
}

平台注册

macOS (Info.plist)

<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLName</key>
    <string>我的应用程序协议</string>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>myapp</string>
    </array>
  </dict>
</array>

Windows (注册表)

// electron-builder.yml
nsis:
  perMachine: true
  include: "installer.nsh"
; installer.nsh
!macro customInstall
  WriteRegStr HKCU "Software\\Classes\\myapp" "" "URL:我的应用程序协议"
  WriteRegStr HKCU "Software\\Classes\\myapp" "URL Protocol" ""
  WriteRegStr HKCU "Software\\Classes\\myapp\\shell\\open\\command" "" '"$INSTDIR\\MyApp.exe" "%1"'
!macroend

Linux (桌面入口)

[Desktop Entry]
Name=我的应用程序
Exec=/opt/myapp/myapp %u
Type=Application
MimeType=x-scheme-handler/myapp;

实现

协议处理器类

// protocol-handler.js
const { app, shell } = require('electron');
const url = require('url');

class ProtocolHandler {
  constructor(mainWindow, options = {}) {
    this.mainWindow = mainWindow;
    this.scheme = options.scheme || 'myapp';
    this.allowedHosts = options.allowedHosts || [];
    this.handlers = new Map();
  }

  register() {
    // 设置为默认协议客户端
    if (process.defaultApp) {
      // 开发环境:使用electron路径注册
      app.setAsDefaultProtocolClient(this.scheme, process.execPath, [
        path.resolve(process.argv[1])
      ]);
    } else {
      // 生产环境
      app.setAsDefaultProtocolClient(this.scheme);
    }
  }

  unregister() {
    app.removeAsDefaultProtocolClient(this.scheme);
  }

  handleUrl(protocolUrl) {
    if (!this.validateUrl(protocolUrl)) {
      console.error('无效的协议URL:', protocolUrl);
      return;
    }

    const parsed = url.parse(protocolUrl, true);
    const route = parsed.host || parsed.pathname?.slice(2);
    const params = parsed.query;

    // 分派给已注册的处理器
    const handler = this.handlers.get(route);
    if (handler) {
      handler(params, parsed);
    } else {
      console.warn('没有找到路由的处理器:', route);
    }

    // 聚焦窗口
    if (this.mainWindow) {
      if (this.mainWindow.isMinimized()) {
        this.mainWindow.restore();
      }
      this.mainWindow.focus();
    }
  }

  validateUrl(protocolUrl) {
    try {
      const parsed = url.parse(protocolUrl);

      // 检查方案
      if (parsed.protocol !== `${this.scheme}:`) {
        return false;
      }

      // 如果配置了,检查允许的主机
      if (this.allowedHosts.length > 0 && parsed.host) {
        if (!this.allowedHosts.includes(parsed.host)) {
          return false;
        }
      }

      return true;
    } catch {
      return false;
    }
  }

  on(route, handler) {
    this.handlers.set(route, handler);
  }
}

module.exports = ProtocolHandler;

主进程集成

// main.js
const { app } = require('electron');
const ProtocolHandler = require('./protocol-handler');

// 单实例锁
const gotTheLock = app.requestSingleInstanceLock();

if (!gotTheLock) {
  app.quit();
} else {
  let mainWindow;
  let protocolHandler;

  app.on('second-instance', (event, commandLine) => {
    // 有人试图运行第二个实例
    // 处理来自命令行的协议URL(Windows)
    const url = commandLine.find(arg => arg.startsWith('myapp://'));
    if (url) {
      protocolHandler.handleUrl(url);
    }

    // 聚焦现有窗口
    if (mainWindow) {
      if (mainWindow.isMinimized()) mainWindow.restore();
      mainWindow.focus();
    }
  });

  // macOS:处理协议URL
  app.on('open-url', (event, url) => {
    event.preventDefault();
    if (protocolHandler) {
      protocolHandler.handleUrl(url);
    }
  });

  app.whenReady().then(() => {
    mainWindow = createWindow();

    protocolHandler = new ProtocolHandler(mainWindow, {
      scheme: 'myapp',
      allowedHosts: ['open', 'auth', 'share']
    });

    protocolHandler.register();

    // 注册路由处理器
    protocolHandler.on('open', (params) => {
      mainWindow.webContents.send('protocol:open', params);
    });

    protocolHandler.on('auth', (params) => {
      handleOAuthCallback(params);
    });

    // 如果应用程序启动时带有URL,则处理它
    const launchUrl = process.argv.find(arg => arg.startsWith('myapp://'));
    if (launchUrl) {
      protocolHandler.handleUrl(launchUrl);
    }
  });
}

electron-builder 配置

# electron-builder.yml
protocols:
  - name: "我的应用程序协议"
    schemes:
      - myapp
    role: Viewer

# macOS
mac:
  extendInfo:
    CFBundleURLTypes:
      - CFBundleURLName: "我的应用程序协议"
        CFBundleURLSchemes:
          - myapp

# Linux
linux:
  mimeTypes:
    - x-scheme-handler/myapp
  desktop:
    MimeType: "x-scheme-handler/myapp;"

安全考虑

  1. 验证所有URL:永远不要信任协议URL的内容
  2. 白名单路由:只处理已知的路由
  3. 清理参数:在使用前清理查询参数
  4. 避免代码执行:永远不要执行协议URL内容
  5. 记录可疑URL:跟踪无效的协议尝试
// 安全示例
validateParams(params) {
  const sanitized = {};
  const allowedParams = ['id', 'action', 'token'];

  for (const [key, value] of Object.entries(params)) {
    if (allowedParams.includes(key)) {
      // 清理值
      sanitized[key] = String(value).slice(0, 1000);
    }
  }

  return sanitized;
}

测试

# 在macOS上测试
open "myapp://open?file=test.txt"

# 在Windows上测试
start "" "myapp://open?file=test.txt"

# 在Linux上测试
xdg-open "myapp://open?file=test.txt"

相关技能

  • electron-ipc-security-audit - 安全的协议处理
  • inter-app-communication process - IPC模式
  • electron-builder-config - 打包协议处理器

相关代理

  • electron-architect - 架构指导
  • desktop-security-auditor - 安全审查