Permissions API
一、Permissions API 概述
1.1 什么是 Permissions API
Permissions API 提供了一种查询浏览器 API 权限状态的一致方式。在 Web 应用需要判断是否能使用某个功能(如定位、摄像头、通知)时,传统的做法是直接尝试使用然后处理错误。Permissions API 让这个过程更加声明式和可控。
// 查询权限状态
const result = await navigator.permissions.query({ name: 'geolocation' });
console.log(result.state); // 'granted' | 'denied' | 'prompt'
1.2 权限状态
| 状态 | 含义 | 用户操作 |
|---|---|---|
granted |
已授权 | 用户明确允许 |
denied |
已拒绝 | 用户明确拒绝或系统自动拒绝 |
prompt |
待询问 | 需要向用户请求 |
1.3 支持的权限
| API | Permission Name |
|---|---|
| Geolocation | geolocation |
| 摄像头/麦克风 | camera, microphone |
| 通知 | notifications |
| 剪贴板 | clipboard-read, clipboard-write |
| 推送通知 | push |
| 屏幕捕获 | display-capture |
二、核心 API
2.1 查询权限
// 查询定位权限
const geolocation = await navigator.permissions.query({
name: 'geolocation'
});
console.log(geolocation.state); // 'granted' | 'denied' | 'prompt'
// 查询通知权限
const notifications = await navigator.permissions.query({
name: 'notifications'
});
2.2 监听权限变化
const permission = await navigator.permissions.query({ name: 'geolocation' });
permission.addEventListener('change', () => {
console.log('Permission changed to:', permission.state);
});
// 也可以使用 onchange
permission.onchange = () => {
console.log('Permission state changed');
};
2.3 Worker 中的使用
// Permissions API 也可以在 Worker 中使用
navigator.permissions.query({ name: 'push' }).then((result) => {
console.log(result.state);
});
三、实际应用
3.1 定位权限检查
async function checkLocationPermission() {
const result = await navigator.permissions.query({
name: 'geolocation'
});
if (result.state === 'granted') {
// 直接使用定位
navigator.geolocation.getCurrentPosition((pos) => {
console.log('Location:', pos.coords);
});
} else if (result.state === 'denied') {
console.log('Location permission denied');
} else {
// 'prompt' - 需要请求权限
console.log('Need to request location permission');
}
// 监听状态变化
result.addEventListener('change', () => {
console.log('Location permission changed:', result.state);
});
}
3.2 摄像头/麦克风权限
async function checkMediaPermission() {
const camera = await navigator.permissions.query({ name: 'camera' });
const mic = await navigator.permissions.query({ name: 'microphone' });
console.log('Camera:', camera.state);
console.log('Microphone:', mic.state);
// 监听变化
camera.addEventListener('change', () => {
if (camera.state === 'denied') {
showCameraDeniedMessage();
}
});
}
3.3 优雅的权限检查工具
class PermissionManager {
static async check(permissionName) {
try {
const result = await navigator.permissions.query({ name: permissionName });
return {
state: result.state,
granted: result.state === 'granted',
denied: result.state === 'denied',
prompt: result.state === 'prompt',
onChange: (callback) => {
result.addEventListener('change', () => callback(result.state));
}
};
} catch (error) {
console.warn(`Permission ${permissionName} not supported`);
return null;
}
}
static async checkMultiple(permissions) {
return Promise.all(
permissions.map(p => this.check(p))
);
}
}
// 使用
const { granted } = await PermissionManager.check('geolocation');
if (granted) {
navigator.geolocation.getCurrentPosition(handleSuccess);
}
四、权限撤销
4.1 没有撤销 API
重要:Permissions API 只提供查询,没有提供撤销权限的方法。曾经提议的 permissions.revoke() 方法已被移除。
// ❌ 不存在
navigator.permissions.revoke({ name: 'geolocation' }); // 错误
4.2 手动撤销
用户需要通过浏览器设置手动撤销权限:
| 浏览器 | 操作 |
|---|---|
| Chrome | 设置 → 隐私和安全 → 位置信息 → 管理例外/清除授权 |
| Firefox | 设置 → 隐私与安全 → 权限 |
| Safari | 系统偏好设置 → 隐私与安全 |
4.3 引导用户
async function ensurePermission(permissionName) {
const result = await navigator.permissions.query({ name: permissionName });
if (result.state === 'granted') {
return true;
}
if (result.state === 'denied') {
// 提示用户手动开启
showSettingsInstruction(permissionName);
return false;
}
// prompt 状态 - 需要触发请求
return await requestPermission(permissionName);
}
五、安全与隐私
5.1 透明性原则
// ✅ 只在真正需要时才请求权限
async function onUserClickShareButton() {
// 检查权限状态
const result = await navigator.permissions.query({ name: 'geolocation' });
if (result.state === 'prompt') {
// 用户主动触发时才请求
const position = await new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(resolve, reject);
});
shareLocation(position);
} else if (result.state === 'granted') {
// 直接使用
navigator.geolocation.getCurrentPosition(shareLocation);
}
}
5.2 权限策略
// 在 Permissions Policy 中声明
// <iframe allow="geolocation" src="...">
// 或者 HTTP 头
// Permissions-Policy: geolocation=(self)
六、面试高频问题
Q: Permissions API 可以用来申请权限吗?
回答要点:不能。Permissions API 只提供查询权限状态和监听变化的能力。实际申请权限需要调用对应的 API(如 navigator.geolocation.getCurrentPosition())来触发浏览器原生的权限对话框。
Q: 权限被拒绝后如何处理?
回答要点:根据拒绝的原因处理——如果是用户明确拒绝,通常需要引导用户到浏览器设置手动开启;如果是权限未定义,说明浏览器不支持该权限。需要提供替代方案或降级体验。
Q: Permissions API 支持所有浏览器 API 吗?
回答要点:不是的。Permissions API 支持的权限是有限的,主要包括 Geolocation、Camera、Microphone、Notifications、Clipboard、Push 等。部分 API 如 storage 权限尚未标准化。查询前应检查 API 是否支持。