你在一個微信H5活動頁中嵌入了領(lǐng)券功能,卻因為錯誤使用了彈窗授權(quán),直接導(dǎo)致30%的用戶在看到“申請獲取你的昵稱和頭像”后立即關(guān)閉頁面——你需要的只是獲取用戶的OpenID來記錄參與狀態(tài),而不是那套需要用戶明確同意的完整資料授權(quán)。類似的授權(quán)翻車場景在微信生態(tài)中反復(fù)出現(xiàn),根源在于開發(fā)者沒有嚴格區(qū)分獲取憑證的深淺層級。
本文將聚焦兩個最主要的授權(quán)場景:以公眾號為入口的網(wǎng)頁授權(quán)(OAuth2.0協(xié)議變體),和以小程序為載體的小程序授權(quán)(結(jié)合wx.login與用戶信息組件)。全文將圍繞三種典型目標(biāo)——只拿OpenID、拿基本資料、拿手機號——梳理獲取流程、后端交換憑證的關(guān)鍵代碼,以及容易導(dǎo)致失敗的安全策略變更。
網(wǎng)頁授權(quán)的兩種Scope:Base與Userinfo
微信網(wǎng)頁授權(quán)建立在OAuth2.0的分支實現(xiàn)上,流程如下:
- 你的服務(wù)端引導(dǎo)用戶跳轉(zhuǎn)至微信授權(quán)頁面;
- 用戶同意(或靜默通過)后,微信附帶一次性code回調(diào)到你的redirect_uri;
- 你的后端憑code換取access_token和openid,必要時還可以拉取用戶信息。
整個過程的核心決策點是scope參數(shù),它決定了用戶在第一步會看到什么:
- snsapi_base:靜默授權(quán),用戶無感知。僅能拿到OpenID。
- snsapi_userinfo:彈窗授權(quán),用戶必須手動點擊同意??梢阅玫疥欠Q、頭像、性別、國家、省份、城市等信息,但用戶在跳轉(zhuǎn)時就面臨一個阻斷式頁面。
很多開發(fā)者直接照搬示例,把scope寫成snsapi_userinfo,結(jié)果在只需要標(biāo)識用戶的場景里損失了大量入口流量。正確的做法是:只要不涉及向用戶展示其頭像昵稱、不做個性化問候或分享卡片,就應(yīng)該選擇snsapi_base。
構(gòu)造授權(quán)URL與換取OpenID
第一步是讓用戶跳轉(zhuǎn)到如下地址(微信客戶端內(nèi)訪問):
// 前端或后端拼接的跳轉(zhuǎn)URL
const redirectUri = encodeURIComponent("https://YOUR_DOMAIN/callback");
const authUrl = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${YOUR_APPID}&redirect_uri=${redirectUri}&response_type=code&scope=snsapi_base&state=uniquestring#wechat_redirect`;
state參數(shù)必須攜帶一個不可預(yù)測的隨機串,并在回調(diào)時校驗,以防范CSRF。
用戶同意(或靜默)后,微信會302到 https://YOUR_DOMAIN/callback?code=CODE&state=uniquestring。你的后端需要用code去換正式憑證:
# 后端向微信接口發(fā)起GET請求,務(wù)必使用服務(wù)端代碼調(diào)用,不可在前端暴露AppSecret
GET https://api.weixin.qq.com/sns/oauth2/access_token?appid=YOUR_APPID&secret=YOUR_APPSECRET&code=CODE&grant_type=authorization_code
返回數(shù)據(jù)格式:
{
"access_token": "ACCESS_TOKEN",
"expires_in": 7200,
"refresh_token": "REFRESH_TOKEN",
"openid": "OPENID",
"scope": "snsapi_base"
}
如果你的scope是snsapi_userinfo,可以進一步用拿到的access_token和openid請求用戶信息:
GET https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
關(guān)鍵約束:
- 每個code僅能使用一次,一旦換取成功立刻失效。
- access_token有效期7200秒,不建議頻繁使用refresh_token刷新,因為每次刷新都會使舊token失效并生成新token,過期重刷就行。
- 已關(guān)注的公眾號用戶會在本頁面得到openid,未關(guān)注用戶的openid也可拿,但無法拿到unionid(除非綁定開放平臺)。
小程序用戶授權(quán):從wx.login到精細化獲取
小程序下的授權(quán)邏輯與網(wǎng)頁授權(quán)差異很大。核心身份標(biāo)識同樣依賴一趟兩步交換:前端調(diào)用 wx.login() 得到臨時code,后端用code去換openid和session_key。但用戶昵稱、頭像、手機號等信息的獲取規(guī)則在微信政策變動后經(jīng)歷了重塑,很多舊教程已失效。
獲取openid的標(biāo)準(zhǔn)流程
前端代碼:
wx.login({
success(res) {
if (res.code) {
// 將code發(fā)送給自家后端
wx.request({
url: 'https://YOUR_API/jscode2session',
data: { code: res.code }
});
}
}
});
后端調(diào)用 jscode2session 接口:
GET https://api.weixin.qq.com/sns/jscode2session?appid=YOUR_APPID&secret=YOUR_APPSECRET&js_code=CODE&grant_type=authorization_code
返回:
{
"openid": "OPENID",
"session_key": "SESSION_KEY",
"unionid": "UNIONID" // 僅在滿足開放平臺綁定條件時出現(xiàn)
}
重要:session_key 只能保存在后端,不可返回給前端,更不要拼在URL中。自定義登錄態(tài)應(yīng)當(dāng)由后端生成token并下發(fā),前端只持有token,session_key 作為后端解密用戶敏感數(shù)據(jù)、校驗簽名的密鑰。
獲取用戶昵稱和頭像的新規(guī)
自2021年4月起,wx.getUserInfo 不再彈出授權(quán)框,而是返回匿名的灰色默認信息。如果你需要真實的用戶昵稱和頭像,必須使用頭像昵稱填寫組件或通過 button 組件讓用戶主動點擊,觸發(fā)綁定:
<!-- wxml 示例:通過按鈕獲取用戶信息,必須在bindtap或回調(diào)中處理 -->
<button open-type="getUserInfo" bindgetuserinfo="onGetUserInfo">授權(quán)獲取基本信息</button>
雖然 bindgetuserinfo 仍可拿到加密數(shù)據(jù),但官方已不推薦此方式,更推薦使用頭像昵稱填寫組件 <cover-image> 與 <input> 的 type="nickname" 來實現(xiàn)用戶自行填寫和確認。這意味著你要調(diào)整交互——不再指望一個API直接返回昵稱和頭像,而是把輸入權(quán)交給用戶。
獲取手機號的流程
獲取手機號仍依托 button 的 open-type="getPhoneNumber",用戶點擊后觸發(fā)回調(diào),前端收到加密數(shù)據(jù) detail.encryptedData 和 iv,交給后端利用 session_key 解密獲取真實手機號。該接口需要小程序前置認證并通過微信審核,不可隨意使用。
后端換取憑證的通識流程與安全注意事項
不論是網(wǎng)頁授權(quán)還是小程序授權(quán),核心安全原則一致:AppSecret永不出現(xiàn)在客戶端。因此所有涉及AppSecret的接口(如獲取access_token、session_key、解密數(shù)據(jù))必須由你的后端完成,前端只傳遞臨時code并接收登錄態(tài)token。
一個健壯的憑證交換服務(wù)應(yīng)包含以下處理:
- code一次性校驗:在換取成功后立刻標(biāo)記或刪除code,防止重復(fù)請求導(dǎo)致錯誤。
- session_key/access_token緩存:令牌類憑證按有效期緩存并提前刷新,避免每次請求都去微信接口換取,否則容易耗盡調(diào)用配額。
- state參數(shù)驗證:對于網(wǎng)頁授權(quán),必須校驗回調(diào)中的state與本服務(wù)生成的一致。
- 數(shù)據(jù)最小化原則:能只用openid完成的場景,不要索取更多信息。每次多余的授權(quán)彈窗都在消耗用戶的信任與耐心。
當(dāng)你需要標(biāo)記設(shè)備或用戶會話時,OpenID是穩(wěn)定的用戶標(biāo)識。不過要注意,同一微信用戶在不同公眾平臺AppID下獲得的OpenID不同,如有跨平臺打通需求,需提前將應(yīng)用綁入微信開放平臺并獲取UnionID。
最后,請追蹤微信官方的更新公告。小程序中獲取用戶信息的API在持續(xù)收緊,未來可能完全依賴用戶主動填寫。這意味著你的產(chǎn)品設(shè)計需要盡早把“用戶主動提交信息”作為默認路徑,而非依賴一鍵返回。
行動建議
- 梳理真實數(shù)據(jù)需求:只標(biāo)識用戶 → 靜默授權(quán)(snsapi_base);需要展示頭像昵稱 → 明確告知用戶并讓其選擇填寫或確認;涉及手機號 → 設(shè)計明確的隱私勾選和觸發(fā)按鈕。
- 統(tǒng)一后端憑證交換層:在服務(wù)端封裝獲取OpenID、解密手機號的接口,前端統(tǒng)一通過code換取自定義token,禁止前端直接接觸AppSecret或session_key。
- 順應(yīng)交互變化:將小程序中頭像昵稱的獲取改為用戶自主填寫,放棄依賴
wx.getUserInfo的舊模式,避免某天突然失效。 - 監(jiān)控授權(quán)轉(zhuǎn)化漏斗:記錄從進入頁面到跳轉(zhuǎn)授權(quán)、回調(diào)成功、獲取用戶信息各環(huán)節(jié)的轉(zhuǎn)化率,當(dāng)發(fā)現(xiàn)授權(quán)環(huán)節(jié)流失驟升時,首先檢查是否錯誤使用了userinfo scope或者無必要的按鈕。
把授權(quán)層級選對,你的功能就能在合規(guī)與體驗之間找到平衡——用戶不會因不必要的彈窗而流失,而你依然能拿到業(yè)務(wù)所需的標(biāo)識與數(shù)據(jù)。