electron 如何申请 Mac 系统权限

对于一些使用 Electron开发的app, 需要获取一些系统权限,比如录屏权限, 获取摄像头权限,麦克风等等,类似于以下界面:

那么Electron App 应该如何申请呢?

首先我们明确一下macOS中基础权限的分类,可以分为以下几种:

  1. 隐私权限(Private Permissions) :
xml 复制代码
<!-- entitlements.mac.plist -->
<dict>
    <!-- 摄像头 -->
    <key>com.apple.security.device.camera</key>
    <true/>
    
    <!-- 麦克风 -->
    <key>com.apple.security.device.microphone</key>
    <true/>
    
    <!-- 位置信息 -->
    <key>com.apple.security.personal-information.location</key>
    <true/>
    
    <!-- 通讯录 -->
    <key>com.apple.security.personal-information.addressbook</key>
    <true/>
    
    <!-- 日历 -->
    <key>com.apple.security.personal-information.calendars</key>
    <true/>
    
    <!-- 照片 -->
    <key>com.apple.security.personal-information.photos-library</key>
    <true/>
    
    <!-- 屏幕录制 -->
    <key>com.apple.security.screen-recording</key>
    <true/>
    
    <!-- 辅助功能 -->
    <key>com.apple.security.automation.apple-events</key>
    <true/>
</dict>
  1. 系统功能权限
xml 复制代码
<dict>
    <!-- 网络访问 -->
    <key>com.apple.security.network.client</key>
    <true/>
    
    <!-- 作为服务器接收连接 -->
    <key>com.apple.security.network.server</key>
    <true/>
    
    <!-- 文件访问 -->
    <key>com.apple.security.files.user-selected.read-write</key>
    <true/>
    
    <!-- USB访问 -->
    <key>com.apple.security.device.usb</key>
    <true/>
    
    <!-- 蓝牙访问 -->
    <key>com.apple.security.device.bluetooth</key>
    <true/>
    
    <!-- 打印权限 -->
    <key>com.apple.security.print</key>
    <true/>
</dict>
  1. App Sandbox 相关权限:
xml 复制代码
<dict>
    <!-- 启用沙箱 -->
    <key>com.apple.security.app-sandbox</key>
    <true/>
    
    <!-- 读取下载文件夹 -->
    <key>com.apple.security.files.downloads.read-write</key>
    <true/>
    
    <!-- 读写用户选择的文件 -->
    <key>com.apple.security.files.user-selected.read-write</key>
    <true/>
    
    <!-- 读写图片文件夹 -->
    <key>com.apple.security.files.pictures.read-write</key>
    <true/>
    
    <!-- 读写音乐文件夹 -->
    <key>com.apple.security.files.music.read-write</key>
    <true/>
</dict>
  1. 硬件权限
xml 复制代码
<dict>
    <!-- 音频输入 -->
    <key>com.apple.security.device.audio-input</key>
    <true/>
    
    <!-- HID设备访问 -->
    <key>com.apple.security.device.usb</key>
    <true/>
    
    <!-- 打印机访问 -->
    <key>com.apple.security.print</key>
    <true/>
</dict>

那么基础权限请求方式为:

javascript 复制代码
const { systemPreferences } = require('electron')

// 检查和请求屏幕录制权限
async function requestScreenCapture() {
  // 检查权限状态
  const status = systemPreferences.getMediaAccessStatus('screen')
  
  if (status !== 'granted') {
    // 请求权限
    const granted = await systemPreferences.askForMediaAccess('screen')
    return granted
  }
  
  return true
}

辅助权限的请求方式为:

javascript 复制代码
const { app } = require('electron')

// 检查辅助功能权限
function checkAccessibilityPermission() {
  return systemPreferences.isTrustedAccessibilityClient(false)
}

// 请求辅助功能权限
function requestAccessibilityPermission() {
  return systemPreferences.isTrustedAccessibilityClient(true)
}

完善的权限管理类为:

javascript 复制代码
class MacPermissions {
  constructor() {
    this.systemPreferences = require('electron').systemPreferences
  }

  async checkPermission(type) {
    switch(type) {
      case 'screen':
        return this.systemPreferences.getMediaAccessStatus('screen')
      case 'camera':
        return this.systemPreferences.getMediaAccessStatus('camera')
      case 'microphone':
        return this.systemPreferences.getMediaAccessStatus('microphone')
      case 'accessibility':
        return this.systemPreferences.isTrustedAccessibilityClient(false)
    }
  }

  async requestPermission(type) {
    try {
      switch(type) {
        case 'screen':
        case 'camera':
        case 'microphone':
          return await this.systemPreferences.askForMediaAccess(type)
        case 'accessibility':
          return this.systemPreferences.isTrustedAccessibilityClient(true)
      }
    } catch(error) {
      console.error(`Error requesting ${type} permission:`, error)
      return false
    }
  }
}

同时需要一个build文件夹,文件夹地址与dist同级别:

在 build文件夹中, 需要一个 entitlements.mac.plist文件,文件中需要声明所需要的权限:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <!-- 屏幕录制权限 -->
    <key>com.apple.security.screen-recording</key>
    <true/>
    
    <!-- 辅助功能权限 -->
    <key>com.apple.security.automation.apple-events</key>
    <true/>
    
    <!-- 摄像头访问权限 -->
    <key>com.apple.security.device.camera</key>
    <true/>
    
    <!-- 麦克风访问权限 -->
    <key>com.apple.security.device.microphone</key>
    <true/>
    
    <!-- 照片库访问权限 -->
    <key>com.apple.security.personal-information.photos-library</key>
    <true/>
    
    <!-- 位置信息访问权限 -->
    <key>com.apple.security.personal-information.location</key>
    <true/>
</dict>
</plist>

配置好之后, 需要在 package.json中配置mac的属性:

json 复制代码
{
  "build": {
    "mac": {
      "hardenedRuntime": true,
      "entitlements": "build/entitlements.mac.plist",
      "entitlementsInherit": "build/entitlements.mac.plist"
    }
  }
}

在打包时, electron-builder 会自动将这些权限配置应用到最终的应用程序中。

然后再App启动时,可以使用代码控制权限申请,会得到类似的对话框:

然后点击系统设置,即可跳转到系统界面,点击手动打开相应的权限,即可完成系统权限的设置.

备注:

那么哪些权限需要我们手动申请呢(可以通过代码开启)?

  1. 媒体权限
javascript 复制代码
const { systemPreferences } = require('electron')

// 摄像头
await systemPreferences.askForMediaAccess('camera')

// 麦克风
await systemPreferences.askForMediaAccess('microphone')

// 屏幕录制
// 注意:屏幕录制权限需要用户在系统设置中手动授权
systemPreferences.getMediaAccessStatus('screen')
  1. 通知权限
javascript 复制代码
const { Notification } = require('electron')

// 请求通知权限
async function requestNotificationPermission() {
  if (!Notification.isSupported()) return false
  
  const permission = await Notification.requestPermission()
  return permission === 'granted'
}
  1. 辅助功能权限
javascript 复制代码
const { systemPreferences } = require('electron')

// 检查辅助功能权限
systemPreferences.isTrustedAccessibilityClient(false)

以下权限需要用户在系统设置中手动开启(无法通过代码直接请求):

文件系统权限:

  • 访问Documents、Desktop、Downloads等目录
  • 访问照片库
  • 访问通讯录
  • 访问日历
  • 访问提醒事项

系统权限:

  • 屏幕录制
  • 辅助功能
  • 完全磁盘访问权限
  • 自动化权限

那么如何引导用户开启呢?

javascript 复制代码
const { dialog, shell } = require('electron')

class PermissionGuide {
  static async showSettingsGuide(permissionType) {
    const guides = {
      screen: {
        title: '需要屏幕录制权限',
        message: '请在系统设置中允许屏幕录制权限',
        prefPane: 'Privacy_ScreenCapture'
      },
      photos: {
        title: '需要照片访问权限',
        message: '请在系统设置中允许照片访问权限',
        prefPane: 'Privacy_Photos'
      },
      files: {
        title: '需要文件访问权限',
        message: '请在系统设置中允许文件访问权限',
        prefPane: 'Privacy_FilesAndFolders'
      },
      accessibility: {
        title: '需要辅助功能权限',
        message: '请在系统设置中允许辅助功能权限',
        prefPane: 'Privacy_Accessibility'
      }
    }

    const guide = guides[permissionType]
    if (!guide) return

    const result = await dialog.showMessageBox({
      type: 'info',
      title: guide.title,
      message: guide.message,
      buttons: ['打开系统设置', '取消']
    })

    if (result.response === 0) {
      // 打开系统设置对应页面
      shell.openExternal(`x-apple.systempreferences:com.apple.preference.security?${guide.prefPane}`)
    }
  }
}

// 完整的权限管理类
class PermissionManager {
  // 检查需要手动申请的权限
  async checkMediaPermission(type) {
    const status = await systemPreferences.getMediaAccessStatus(type)
    if (status === 'not-determined') {
      return await systemPreferences.askForMediaAccess(type)
    }
    return status === 'granted'
  }

  // 检查需要手动开启的权限
  async checkSystemPermission(type) {
    let status = false
    
    switch(type) {
      case 'screen':
        status = systemPreferences.getMediaAccessStatus('screen') === 'granted'
        break
      case 'accessibility':
        status = systemPreferences.isTrustedAccessibilityClient(false)
        break
      // 其他系统权限检查...
    }

    if (!status) {
      await PermissionGuide.showSettingsGuide(type)
    }

    return status
  }

  // 权限检查和请求的统一接口
  async ensurePermission(type) {
    // 需要手动申请的权限
    if (['camera', 'microphone'].includes(type)) {
      return await this.checkMediaPermission(type)
    }
    
    // 需要在系统设置中手动开启的权限
    if (['screen', 'accessibility', 'photos', 'files'].includes(type)) {
      return await this.checkSystemPermission(type)
    }

    return false
  }
}

// 使用示例
async function example() {
  const permissionManager = new PermissionManager()

  // 检查和请求摄像头权限
  const hasCameraPermission = await permissionManager.ensurePermission('camera')
  if (!hasCameraPermission) {
    console.log('未获得摄像头权限')
    return
  }

  // 检查屏幕录制权限
  const hasScreenPermission = await permissionManager.ensurePermission('screen')
  if (!hasScreenPermission) {
    console.log('未获得屏幕录制权限')
    return
  }

  // 正常执行需要权限的功能...
}
相关推荐
摸着石头过河的石头1 小时前
Service Worker 深度解析:让你的 Web 应用离线也能飞
前端·javascript·性能优化
不爱吃糖的程序媛2 小时前
Electron 如何判断运行平台是鸿蒙系统(OpenHarmony)
javascript·electron·harmonyos
Hilaku2 小时前
我用AI重构了一段500行的屎山代码,这是我的Prompt和思考过程
前端·javascript·架构
Cxiaomu2 小时前
React Native App 自动检测版本更新完整实现指南
javascript·react native·react.js
掘金安东尼3 小时前
前端周刊第439期(2025年11月3日–11月9日)
前端·javascript·vue.js
嚴寒3 小时前
我在 Mac 用一行脚本征服了 5TB NTFS:拒绝付费,彻底搞定免费方案
macos·mac
起这个名字3 小时前
微前端应用通信使用和原理
前端·javascript·vue.js
鹏多多4 小时前
Web使用natapp进行内网穿透和预览本地页面
前端·javascript
钱端工程师4 小时前
uniapp封装uni.request请求,实现重复接口请求中断上次请求(防抖)
前端·javascript·uni-app
茶憶4 小时前
uni-app app移动端实现纵向滑块功能,并伴随自动播放
javascript·vue.js·uni-app·html·scss