微信小程序用户隐私合规指南:2026年最新要求
·
随着《个人信息保护法》深入落地和微信隐私保护机制的全面升级,小程序隐私合规已从"可选项"变为"必选项"。不合规的小程序将被限制调用敏感接口甚至下架。本文系统梳理2026年合规要求,提供可直接落地的代码方案。
一、微信隐私保护框架
1.1 隐私保护指引
自2023年9月起,微信要求所有小程序配置《小程序用户隐私保护指引》。开发者需要在小程序管理后台填写以下信息:
- 小程序收集的个人信息类型
- 每类信息的使用目的
- 第三方信息共享情况
- 用户权利保障机制
2026年新增要求:
- 隐私保护指引必须与实际代码中使用的接口一一对应
- 新增隐私接口类型时必须更新指引
- 指引内容需覆盖数据存储位置、保留期限、删除方式
1.2 隐私协议弹窗机制
微信提供了内置的隐私保护弹窗机制,所有小程序必须接入:
javascript复制
// app.js 中检查隐私协议状态
App({
onLaunch() {
// 检查是否需要弹出隐私协议
if (wx.getPrivacySetting) {
wx.getPrivacySetting({
success: (res) => {
if (res.needAuthorization) {
// 需要用户同意隐私协议
this.globalData.privacyAuthorizationNeeded = true
}
console.log('隐私授权状态:', res)
// res.needAuthorization: 是否需要授权
// res.privacyContractName: 隐私协议名称
}
})
}
}
})
关键点:
wx.getPrivacySetting判断是否需要弹出隐私协议- 用户未同意前,所有隐私接口将被拦截
- 微信会自动弹出隐私协议弹窗(开发者也可自定义)
二、隐私接口清单
2.1 需要声明授权的接口
以下接口在用户未同意隐私协议前无法调用:
| 接口 | 收集信息 | 声明类型 |
|---|---|---|
| wx.getUserProfile | 昵称、头像 | 用户信息 |
| wx.getLocation / wx.chooseLocation | 地理位置 | 位置信息 |
| wx.chooseImage / wx.chooseMedia | 图片/视频 | 相册/摄像头 |
| wx.getPhoneNumber | 手机号 | 手机号码 |
| wx.chooseAddress | 地址 | 地址信息 |
| wx.getClipboardData | 剪贴板 | 剪贴板 |
| wx.getRecorderManager | 录音 | 麦克风 |
| wx.startBluetoothDevicesDiscovery | 蓝牙设备 | 蓝牙 |
| wx.addPhoneContact | 通讯录 | 通讯录 |
| wx.getUserInfo | 用户信息 | 用户信息 |
| wx.chooseVideo | 视频 | 摄像头 |
| wx.saveImageToPhotosAlbum | 相册写入 | 相册 |
2.2 2026年新增敏感接口类别
微信持续扩大隐私接口范围,以下操作也需在隐私保护指引中声明:
- 广告组件:收集设备信息用于广告投放
- 自定义组件中使用
wx.createSelectorQuery获取节点信息 - Webview 中加载的H5页面的数据收集行为
- 插件引入的第三方隐私接口调用
三、2026年隐私合规核心要求
3.1 隐私保护指引完整性
javascript复制
// 检查隐私保护指引是否完整
// 在 app.js 的 onLaunch 中执行
function checkPrivacyGuide() {
return new Promise((resolve, reject) => {
wx.request({
url: 'https://mp.weixin.qq.com/wxa-dev-logic/get_privacy_setting',
method: 'POST',
data: { appid: getApp().globalData.appId },
success(res) {
const { privacySetting } = res.data
// 检查是否配置了隐私保护指引
if (!privacySetting || !privacySetting.privacyContractName) {
console.error('未配置隐私保护指引')
reject(new Error('PRIVACY_GUIDE_MISSING'))
return
}
// 检查指引是否通过审核
if (privacySetting.status !== 'approved') {
console.warn('隐私保护指引审核状态:', privacySetting.status)
}
resolve(privacySetting)
},
fail: reject
})
})
}
3.2 敏感接口授权后才能调用
javascript复制
// utils/privacyGuard.js
/**
* 隐私接口调用守卫
* 确保用户已同意隐私协议后才执行敏感操作
*/
class PrivacyGuard {
constructor() {
this.resolved = false
this.pendingCallbacks = []
}
/**
* 调用隐私接口前的统一检查
* @param {Function} callback 用户同意后执行的操作
*/
ensure(callback) {
if (this.resolved) {
callback()
return
}
if (wx.requirePrivacyAuthorize) {
this.pendingCallbacks.push(callback)
wx.requirePrivacyAuthorize({
success: () => {
this.resolved = true
this.pendingCallbacks.forEach(cb => cb())
this.pendingCallbacks = []
},
fail: (err) => {
console.warn('用户拒绝隐私协议:', err)
this.pendingCallbacks = []
}
})
} else {
// 低版本基础库,直接执行
callback()
}
}
/**
* 重置授权状态(用户主动触发)
*/
reset() {
this.resolved = false
}
/**
* 检查是否已授权
*/
isAuthorized() {
return this.resolved
}
}
const privacyGuard = new PrivacyGuard()
export default privacyGuard
3.3 不得默认勾选/强制授权
2026年合规红线:
- 禁止将"同意隐私协议"默认勾选
- 禁止将隐私同意与其他业务操作绑定(如"同意隐私协议才能领取优惠券"可以,但必须能跳过)
- 禁止在用户拒绝授权后反复弹出隐私弹窗(建议同一会话最多弹2次)
- 禁止在隐私协议中使用含糊表述(“收集必要信息”→需明确列出)
3.4 数据最小化原则
javascript复制
// 数据收集审查工具
const DATA_MINIMIZATION_RULES = {
// 只收集业务必需的数据,不"贪多"
'头像昵称': {
required: false, // 非登录必需
alternative: '可使用默认头像和匿名昵称',
retention: '用户主动更新前保留'
},
'手机号': {
required: false, // 可用微信登录替代
alternative: '使用微信一键登录',
retention: '仅用于必要业务场景'
},
'地理位置': {
required: true, // LBS类业务必需
alternative: '可手动选择城市',
retention: '服务完成后即删除'
},
'相册': {
required: false, // 仅上传图片时需要
alternative: '可使用相机直接拍摄',
retention: '仅服务期间使用'
}
}
3.5 数据存储与传输安全
javascript复制
// utils/secureStorage.js
const crypto = require('crypto-js') // npm install crypto-js
/**
* 安全存储工具
* 对敏感数据进行AES加密后存储到本地
*/
class SecureStorage {
constructor() {
// 从服务端获取动态密钥,不要硬编码
this.storageKey = 'secure_data_v2'
}
/**
* 获取加密密钥(每个用户独立的盐值)
*/
_getKey(salt = '') {
const userId = wx.getStorageSync('userId') || 'default'
return `${userId}_${salt}_${this.storageKey}`
}
/**
* 加密存储
*/
set(key, value, salt = '') {
try {
const jsonString = JSON.stringify(value)
const encrypted = crypto.AES.encrypt(
jsonString,
this._getKey(salt)
).toString()
wx.setStorageSync(key, {
encrypted: true,
version: 2,
data: encrypted,
timestamp: Date.now()
})
return true
} catch (err) {
console.error('加密存储失败:', err)
return false
}
}
/**
* 解密读取
*/
get(key, salt = '') {
try {
const stored = wx.getStorageSync(key)
if (!stored) return null
if (!stored.encrypted) {
// 兼容旧版未加密数据
console.warn('数据未加密,建议迁移')
return stored.data || stored
}
const bytes = crypto.AES.decrypt(
stored.data,
this._getKey(salt)
)
return JSON.parse(bytes.toString(crypto.enc.Utf8))
} catch (err) {
console.error('解密失败:', err)
return null
}
}
/**
* 安全删除
*/
remove(key) {
wx.removeStorageSync(key)
}
}
const secureStorage = new SecureStorage()
export default secureStorage
四、隐私弹窗最佳实践
4.1 自定义隐私弹窗组件
javascript复制
// components/privacy-popup/privacy-popup.js
Component({
properties: {
// 是否显示
show: {
type: Boolean,
value: false
},
// 隐私协议URL
privacyUrl: {
type: String,
value: ''
},
// 用户协议URL
userAgreementUrl: {
type: String,
value: ''
}
},
data: {
agreed: false
},
methods: {
// 同意隐私协议
handleAgree() {
if (!this.data.agreed) {
// 防止未勾选就同意
wx.showToast({ title: '请先勾选同意', icon: 'none' })
return
}
// 调用微信隐私协议确认接口
if (wx.onNeedPrivacyAuthorization) {
wx.onNeedPrivacyAuthorization(resolve => {
resolve({ event: 'agree' })
})
}
this.triggerEvent('agree')
// 记录同意时间(用于审计)
wx.setStorageSync('privacy_agreed_at', Date.now())
wx.setStorageSync('privacy_version', '2026-06')
},
// 拒绝
handleDisagree() {
this.triggerEvent('disagree')
},
// 切换勾选状态
toggleAgree() {
this.setData({ agreed: !this.data.agreed })
},
// 打开隐私协议
openPrivacy() {
if (this.properties.privacyUrl) {
wx.navigateTo({
url: `/pages/webview/index?url=${encodeURIComponent(this.properties.privacyUrl)}`
})
} else {
// 使用微信内置隐私协议页
wx.openPrivacyContract()
}
},
// 打开用户协议
openUserAgreement() {
if (this.properties.userAgreementUrl) {
wx.navigateTo({
url: `/pages/webview/index?url=${encodeURIComponent(this.properties.userAgreementUrl)}`
})
}
}
}
})
xml复制
<!-- components/privacy-popup/privacy-popup.wxml -->
<view class="privacy-mask" wx:if="{{show}}">
<view class="privacy-dialog">
<view class="dialog-title">用户隐私保护提示</view>
<view class="dialog-body">
<view class="dialog-desc">
为了更好地保障您的合法权益,在使用我们的服务前,请仔细阅读
<text class="link" bindtap="openPrivacy">《隐私保护指引》</text>
和
<text class="link" bindtap="openUserAgreement">《用户服务协议》</text>。
我们将按照指引收集、使用和保护您的个人信息。
</view>
<view class="check-row" bindtap="toggleAgree">
<view class="checkbox {{agreed ? 'checked' : ''}}">
<text wx:if="{{agreed}}">✓</text>
</view>
<text class="check-text">我已阅读并同意</text>
</view>
</view>
<view class="dialog-footer">
<button class="btn-disagree" bindtap="handleDisagree">不同意</button>
<button class="btn-agree" bindtap="handleAgree">同意并继续</button>
</view>
</view>
</view>
css复制
/* components/privacy-popup/privacy-popup.wxss */
.privacy-mask {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0, 0, 0, 0.6);
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
}
.privacy-dialog {
width: 600rpx;
background: #fff;
border-radius: 24rpx;
overflow: hidden;
}
.dialog-title {
text-align: center;
padding: 40rpx 32rpx 20rpx;
font-size: 34rpx;
font-weight: 600;
}
.dialog-body {
padding: 20rpx 32rpx;
}
.dialog-desc {
font-size: 26rpx;
color: #666;
line-height: 1.6;
margin-bottom: 32rpx;
}
.link {
color: #07c160;
text-decoration: underline;
}
.check-row {
display: flex;
align-items: center;
}
.checkbox {
width: 36rpx;
height: 36rpx;
border: 2rpx solid #ccc;
border-radius: 8rpx;
margin-right: 16rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 24rpx;
color: #fff;
}
.checkbox.checked {
background: #07c160;
border-color: #07c160;
}
.check-text {
font-size: 26rpx;
color: #333;
}
.dialog-footer {
display: flex;
border-top: 1rpx solid #eee;
}
.btn-disagree {
flex: 1;
background: #f7f7f7;
font-size: 30rpx;
border-radius: 0;
border: none;
}
.btn-agree {
flex: 1;
background: #07c160;
color: #fff;
font-size: 30rpx;
border-radius: 0;
border: none;
border-left: 1rpx solid #eee;
}
4.2 弹窗时机
| 时机 | 是否推荐 | 说明 |
|---|---|---|
| 首次打开小程序 | ✅ 推荐 | 标准做法,在onLaunch中检查 |
| 用户首次点击敏感功能时 | ✅ 推荐 | 延迟到实际需要时弹,体验更好 |
| 每次打开都弹 | ❌ 不推荐 | 用户已同意后无需重复弹 |
| 隐私协议更新后 | ✅ 推荐 | 检测版本变化,重新弹出 |
javascript复制
// app.js - 标准启动流程
App({
onLaunch() {
this.checkPrivacyAndInit()
},
checkPrivacyAndInit() {
const agreedVersion = wx.getStorageSync('privacy_version')
const currentVersion = '2026-06' // 隐私协议版本号
if (wx.getPrivacySetting) {
wx.getPrivacySetting({
success: (res) => {
// 需要授权 或 协议版本更新
if (res.needAuthorization || agreedVersion !== currentVersion) {
this.globalData.showPrivacyPopup = true
} else {
this.initApp()
}
},
fail: () => {
// 低版本基础库
this.initApp()
}
})
} else {
this.initApp()
}
},
initApp() {
// 初始化应用逻辑
},
onPrivacyAgree() {
this.globalData.showPrivacyPopup = false
this.globalData.privacyAuthorized = true
this.initApp()
},
onPrivacyDisagree() {
// 用户拒绝,提供降级体验
this.globalData.privacyAuthorized = false
this.initApp()
}
})
五、授权拒绝后的降级方案
核心原则:用户拒绝授权后,小程序的基本功能仍应可用,只是涉及敏感数据的功能不可用。
javascript复制
// utils/authFallback.js
/**
* 授权降级方案
* 根据用户授权状态提供对应的功能集合
*/
const FALLBACK_CONFIG = {
// 拒绝位置授权 → 手动选择城市
location: {
degraded: true,
fallback: 'manual_city_select',
message: '开启定位可自动发现附近商家,您也可以手动选择城市'
},
// 拒绝手机号授权 → 微信一键登录
phone: {
degraded: true,
fallback: 'wechat_auto_login',
message: '使用微信登录即可体验全部功能'
},
// 拒绝相册授权 → 仅使用相机
album: {
degraded: true,
fallback: 'camera_only',
message: '您可以使用相机直接拍照上传'
},
// 拒绝用户信息 → 使用匿名头像
userInfo: {
degraded: true,
fallback: 'anonymous_profile',
message: '将使用默认头像和昵称'
}
}
/**
* 通用敏感接口调用封装
* 自动处理拒绝后的降级
*/
function callWithFallback(apiName, apiFunc, fallbackFunc) {
return new Promise((resolve) => {
privacyGuard.ensure(() => {
apiFunc()
.then(resolve)
.catch((err) => {
console.warn(`${apiName} 调用失败:`, err)
const config = FALLBACK_CONFIG[apiName]
if (config && fallbackFunc) {
wx.showToast({
title: config.message,
icon: 'none',
duration: 3000
})
resolve(fallbackFunc())
} else {
resolve(null)
}
})
})
})
}
// 使用示例
Page({
async onLoad() {
// 获取位置:失败则降级为手动选择
const location = await callWithFallback(
'location',
() => wx.getLocation({ type: 'gcj02' }),
() => ({ latitude: 39.9, longitude: 116.4, manual: true })
)
}
})
六、个保法/个人信息保护法对小程序的影响
6.1 用户数据权利
根据《个人信息保护法》,用户享有的权利及其在小程序中的实现:
| 权利 | 小程序实现方式 |
|---|---|
| 知情权 | 隐私弹窗清晰告知收集内容和目的 |
| 同意权 | 隐私弹窗需用户主动同意 |
| 撤回同意 | 提供关闭入口,撤回后停止收集 |
| 更正权 | 允许用户修改个人信息 |
| 删除权 | 提供注销/删除数据入口 |
| 数据导出 | 提供个人信息副本下载 |
6.2 用户数据删除权实现
javascript复制
// services/userDataService.js
class UserDataService {
/**
* 用户注销账号 - 删除所有个人数据
* 注意:订单记录等法定保留数据需脱敏后保留
*/
async deleteUserData(userId) {
// 1. 订单数据脱敏(法律要求保留交易记录)
await db.query(`
UPDATE orders
SET user_name = '已注销用户',
phone = '***',
address = '***'
WHERE user_id = ?
`, [userId])
// 2. 删除用户画像和行为数据
await db.query(`
DELETE FROM user_profiles WHERE user_id = ?
`, [userId])
await db.query(`
DELETE FROM user_behavior_log WHERE user_id = ?
`, [userId])
await db.query(`
DELETE FROM user_preferences WHERE user_id = ?
`, [userId])
// 3. 删除微信openid关联
await db.query(`
UPDATE users
SET openid = NULL,
phone = NULL,
nickname = '已注销用户',
avatar = '',
status = 'deleted',
deleted_at = NOW()
WHERE id = ?
`, [userId])
// 4. 清除缓存
await redis.del(`user:${userId}:profile`)
await redis.del(`user:${userId}:preferences`)
}
/**
* 导出用户数据
* 返回用户所有个人信息的JSON文件
*/
async exportUserData(userId) {
const [user] = await db.query(`
SELECT id, nickname, phone, created_at
FROM users WHERE id = ?
`, [userId])
const [preferences] = await db.query(`
SELECT * FROM user_preferences WHERE user_id = ?
`, [userId])
const [orders] = await db.query(`
SELECT order_id, amount, status, created_at
FROM orders WHERE user_id = ?
ORDER BY created_at DESC
LIMIT 100
`, [userId])
return {
exportTime: new Date().toISOString(),
user,
preferences,
recentOrders: orders
}
}
}
module.exports = new UserDataService()
七、隐私合规自查清单
以下清单可用于上线前的逐项检查:
markdown复制
## 隐私合规自查清单
### 隐私保护指引
- [ ] 已在小程序管理后台配置《隐私保护指引》
- [ ] 指引中列出的收集类型与代码中实际使用的接口一致
- [ ] 指引通过审核(状态为"已通过")
- [ ] 指引中包含数据存储位置和保留期限
- [ ] 指引中包含第三方共享说明(如有)
### 隐私弹窗
- [ ] 首次打开小程序时弹出隐私协议
- [ ] 弹窗包含《隐私保护指引》可点击链接
- [ ] 用户未勾选同意时不能点"同意并继续"
- [ ] 同意状态本地持久化,不会每次都弹
- [ ] 隐私协议更新后能重新弹出
- [ ] 同一会话内最多弹2次
### 敏感接口
- [ ] 所有隐私接口调用前检查授权状态
- [ ] 使用 wx.requirePrivacyAuthorize 或 wx.getPrivacySetting 检查
- [ ] 授权拒绝后有降级方案
- [ ] 不在用户授权前静默收集数据
### 数据安全
- [ ] 敏感数据(手机号、地址等)加密存储
- [ ] 网络传输使用HTTPS
- [ ] 不在日志中打印用户敏感信息
- [ ] access_token等密钥使用环境变量管理
### 用户权利
- [ ] 提供账号注销/删除功能
- [ ] 注销时删除或脱敏用户数据
- [ ] 提供用户数据导出功能(可选但推荐)
- [ ] 提供撤回授权的入口
### 专项检查
- [ ] 不在后台静默获取地理位置
- [ ] 不使用剪贴板读取用户敏感数据
- [ ] 不向第三方共享用户原始数据(如需共享需单独告知)
- [ ] 广告组件合规配置(使用微信官方广告组件)
- [ ] WebView中加载的H5页面也有隐私合规
- [ ] 插件引入的隐私接口在指引中声明
八、隐私协议模板
以下是一个通用的隐私协议模板,适用于大多数小程序。请根据实际业务情况修改。
markdown复制
# 隐私保护指引
更新日期:2026年6月19日
生效日期:2026年6月19日
## 1. 我们如何收集和使用您的个人信息
### 1.1 小程序运行必需的信息
- **微信授权登录**:获取您的微信OpenID,用于创建和关联您的账号。
- **设备信息**:设备型号、操作系统版本,用于适配显示和排查故障。
### 1.2 为提供特定功能而收集的信息
- **地理位置**(仅在您使用LBS相关功能时):获取您的位置信息,用于推荐附近服务。
- **手机号码**(仅在您手动授权时):用于账号安全验证和重要通知。
- **相册/摄像头**(仅在您上传图片时):用于头像设置或内容发布。
### 1.3 我们不会收集的信息
- 我们不会收集您的聊天记录、通讯录等与小程序服务无关的信息。
## 2. 信息的存储
- 您的个人信息存储在中华人民共和国境内的服务器上。
- 我们将仅在实现服务目的所必需的合理期限内保留您的个人信息。
## 3. 信息共享
- 我们不会向第三方出售您的个人信息。
- 为提供服务所必需,我们可能与关联公司共享部分信息,且受同等隐私保护约束。
## 4. 您的权利
- **查询和更正**:您可以在小程序"个人中心"查看和修改您的信息。
- **删除**:您可以在"设置→账号与安全→注销账号"中删除您的个人信息。
- **撤回同意**:您可以在微信设置中关闭对应权限。
- **获取副本**:您可以联系我们获取您的个人信息副本。
## 5. 联系我们
如有隐私相关疑问,请联系:privacy@example.com
九、总结
2026年小程序隐私合规的要点:
- 指引先行:隐私保护指引必须完整配置,且与代码一致
- 弹窗必接:微信隐私协议弹窗是强制机制,不接就限制接口
- 最小收集:只收集业务必需的数据,非必要不收集
- 安全存储:敏感数据加密存储,传输全程HTTPS
- 用户主导:提供删除、导出、撤回授权等权利通道
- 持续自查:每次更新隐私接口后重新走一遍自查清单
隐私合规不是一次性工作,而是贯穿小程序整个生命周期的持续义务。建议将自查清单集成到CI/CD流程中,每次发布前自动检查。
javascript复制
// build-time 检查脚本(集成到 CI/CD)
const fs = require('fs')
const path = require('path')
function checkPrivacyApiUsage() {
const PRIVACY_APIS = [
'getLocation', 'chooseLocation', 'getUserInfo', 'getUserProfile',
'chooseImage', 'chooseMedia', 'getPhoneNumber', 'chooseAddress',
'getClipboardData', 'chooseVideo', 'saveImageToPhotosAlbum',
'startRecord', 'startBluetoothDevicesDiscovery'
]
// 扫描所有JS文件
const jsFiles = scanFiles(path.resolve('./miniprogram'), '.js')
const violations = []
for (const file of jsFiles) {
const content = fs.readFileSync(file, 'utf8')
for (const api of PRIVACY_APIS) {
if (content.includes(`wx.${api}`) || content.includes(`.${api}(`)) {
// 检查是否包裹在 privacyGuard.ensure 中
if (!content.includes('privacyGuard')) {
violations.push(`${file}: 使用了隐私接口 wx.${api} 但未包裹隐私守卫`)
}
}
}
}
if (violations.length > 0) {
console.error('⚠️ 隐私合规检查未通过:')
violations.forEach(v => console.error(` - ${v}`))
process.exit(1) // CI构建失败
} else {
console.log('✅ 隐私合规检查通过')
}
}
更多推荐

所有评论(0)