目錄:
1. 做微信小程序遇到授權(quán)問題
2. 沒接觸過授權(quán),先理解授權(quán)是什么意思
3. 具體開發(fā)如何實(shí)現(xiàn)授權(quán)
3.1 使用賬號(hào)密碼進(jìn)行傳統(tǒng)授權(quán)
3.2 傳統(tǒng)授權(quán)顯得很蠢,Oauth 授權(quán)方式出現(xiàn)
4. 回過頭分析小程序?yàn)槭裁慈绱嗽O(shè)計(jì)授權(quán)
5. 參考文章
一详拙、遇到授權(quán)
最近開發(fā)微信小程序删性,做登錄功能時(shí)遇到下圖。
看太懂钳吟,有幾處疑惑地方:
- 同事告訴我這是認(rèn)證和授權(quán),之前沒接觸過,不了解是要解決什么問題瓮钥;
- 為什么要有認(rèn)證和授權(quán),要解決什么問題烹吵;
- appid骏庸、appsecret、openid年叮、session_key 這些單詞都是什么意思具被。
下面就是尋找答案的過程。
二只损、沒接觸過授權(quán)一姿,先理解授權(quán)是什么意思
豪車模型
如果你開車去酒店赴宴,你經(jīng)常會(huì)苦于找不到停車位而耽誤很多時(shí)間跃惫。是否有好辦法可以避免這個(gè)問題呢叮叹?有的,聽說有一些豪車的車主就不擔(dān)心這個(gè)問題爆存。豪車一般配備兩種鑰匙:主鑰匙和泊車鑰匙蛉顽。當(dāng)你到酒店后,只需要將泊車鑰匙交給服務(wù)生先较,停車的事情就由服務(wù)生去處理携冤。與主鑰匙相比,這種泊車鑰匙的使用功能是受限制的:它只能啟動(dòng)發(fā)動(dòng)機(jī)并讓車行駛一段有限的距離闲勺,可以鎖車曾棕,但無法打開后備箱,無法使用車內(nèi)其他設(shè)備菜循。這里就體現(xiàn)了一種簡單的“開放授權(quán)”思想:通過一把泊車鑰匙翘地,車主便能將汽車的部分使用功能(如啟動(dòng)發(fā)動(dòng)機(jī)、行駛一段有限的距離)授權(quán)給服務(wù)生。
鄰居模型
你和你的家人在外面游玩衙耕,突然陰雨蒙蒙昧穿,眼看就要下雨了,你想起家中的被子還在外面曬著橙喘。你給你的鄰居打電話时鸵,告訴他在你家門口地毯下面有一把鑰匙,希望他將被子收到房間中渴杆。通過告知他鑰匙的位置寥枝,就是你授權(quán)你的鄰居進(jìn)入你家的權(quán)限,雖然你只是希望他拿到被子送到客廳就出來鎖門磁奖,但是你給了他所有的權(quán)限囊拜,他不僅可以進(jìn)入客廳,還可以進(jìn)入你們的臥室比搭、廚房等冠跷,這不是你想看到的。
在上面兩個(gè)模型中身诺。豪車的主鑰匙賦予泊車鑰匙部分權(quán)限蜜托,而你賦予你的鄰居所有的權(quán)限。比較之下霉赡,我們更希望使用豪車模型賦予部分權(quán)限橄务,而不是鄰居模型,萬一他偷偷配了把鑰匙呢穴亏?這種授權(quán)存在不安全性蜂挪,在網(wǎng)絡(luò)開發(fā)中也是如此。
三嗓化、具體開發(fā)如何實(shí)現(xiàn)授權(quán)
登錄簡書時(shí)可以選擇社交賬號(hào)登錄棠涮,這里以 QQ 為例。這里涉及三方:當(dāng)前要登錄簡書的用戶刺覆,以下簡稱 用戶
严肪,其它兩方是:簡書
,QQ 服務(wù)器
谦屑。因?yàn)?用戶
之前注冊(cè)過 QQ驳糯,簡書
請(qǐng)求 QQ 服務(wù)器
授權(quán)用戶的 QQ 基本信息,這樣用戶就不用在簡書上再進(jìn)行注冊(cè)了伦仍。
下面來探討下 QQ 服務(wù)器
如何授權(quán) 簡書
當(dāng)前 用戶
的 QQ 基本信息结窘。
3.1 使用賬號(hào)密碼進(jìn)行傳統(tǒng)授權(quán)
這種方法比較簡單,用戶
告訴 簡書
它的 QQ 賬號(hào)密碼充蓝,簡書拿著賬號(hào)密碼去請(qǐng)求 QQ 服務(wù)器獲取用戶基本信息。
優(yōu)點(diǎn):操作簡單。
缺點(diǎn):用戶將 qq 賬號(hào)密碼給了簡書谓苟,簡書獲得了該用戶在 QQ 上的所有特權(quán)官脓,不僅可以查看基本信息,還可以查看 QQ 空間涝焙,查看 QQ 郵箱等卑笨,而用戶的本意是只讓簡書獲取 QQ 基本信息這一項(xiàng)特權(quán)。
正是傳統(tǒng)授權(quán)方式存在缺陷仑撞,Oauth 2.0 授權(quán)才會(huì)出現(xiàn):只授權(quán)指定權(quán)限赤兴。
3.2 傳統(tǒng)授權(quán)顯得很蠢,Oauth2.0 授權(quán)方式出世
下面是在 小胡子哥的個(gè)人博客 里看到的隧哮,將整個(gè)授權(quán)過程講解的生動(dòng)形象桶良,我就不做二次加工,直接截取部分內(nèi)容如下沮翔。
以用戶使用 github 登錄網(wǎng)站留言為例陨帆,簡述 OAuth 2.0 的運(yùn)作流程。
假如我有一個(gè)網(wǎng)站采蚀,你是我網(wǎng)站上的訪客疲牵,看了文章想留言表示「朕已閱」,留言時(shí)發(fā)現(xiàn)有這個(gè)網(wǎng)站的帳號(hào)才能夠留言榆鼠,此時(shí)給了你兩個(gè)選擇:一個(gè)是在我的網(wǎng)站上注冊(cè)擁有一個(gè)新賬戶纲爸,然后用注冊(cè)的用戶名來留言;一個(gè)是使用 github 帳號(hào)登錄妆够,使用你的 github 用戶名來留言识啦。前者你覺得過于繁瑣,于是慣性地點(diǎn)擊了 github 登錄按鈕责静,此時(shí) OAuth 認(rèn)證流程就開始了袁滥。
需要明確的是,即使用戶剛登錄過 github灾螃,我的網(wǎng)站也不可能向 github 發(fā)一個(gè)什么請(qǐng)求便能夠拿到訪客信息题翻,這顯然是不安全的。就算用戶允許你獲取他在 github 上的信息腰鬼,github 為了保障用戶信息安全嵌赠,也不會(huì)讓你隨意獲取。所以操作之前熄赡,我的網(wǎng)站與 github 之間需要要有一個(gè)協(xié)商姜挺。
- 網(wǎng)站和 Github 之間的協(xié)商
Github 會(huì)對(duì)用戶的權(quán)限做分類,比如讀取倉庫信息的權(quán)限彼硫、寫入倉庫的權(quán)限炊豪、讀取用戶信息的權(quán)限凌箕、修改用戶信息的權(quán)限等等。如果我想獲取用戶的信息词渤,Github 會(huì)要求我牵舱,先在它的平臺(tái)上注冊(cè)一個(gè)應(yīng)用,在申請(qǐng)的時(shí)候標(biāo)明需要獲取用戶信息的哪些權(quán)限缺虐,用多少就申請(qǐng)多少芜壁,并且在申請(qǐng)的時(shí)候填寫你的網(wǎng)站域名,Github 只允許在這個(gè)域名中獲取用戶信息高氮。
此時(shí)我的網(wǎng)站已經(jīng)和 Github 之間達(dá)成了共識(shí)慧妄,Github 也給我發(fā)了兩張門票,一張門票叫做 Client Id剪芍,另一張門票叫做 Client Secret塞淹。
- 用戶和 Github 之間的協(xié)商
用戶進(jìn)入我的網(wǎng)站,點(diǎn)擊 github 登錄按鈕的時(shí)候紊浩,我的網(wǎng)站會(huì)把上面拿到的 Client Id 交給用戶窖铡,讓他進(jìn)入到 Github 的授權(quán)頁面,Github 看到了用戶手中的門票坊谁,就知道這是我的網(wǎng)站讓他過來的费彼,于是它就把我的網(wǎng)站想要獲取的權(quán)限擺出來,并詢問用戶是否允許我獲取這些權(quán)限口芍。
// 用戶登錄 github箍铲,協(xié)商
GET //github.com/login/oauth/authorize
// 協(xié)商憑證
params = {
client_id: "xxxx",
redirect_uri: "http://my-website.com"
}
如果用戶覺得我的網(wǎng)站要的權(quán)限太多,或者壓根就不想我知道他這些信息鬓椭,選擇了拒絕的話颠猴,整個(gè) OAuth 2.0 的認(rèn)證就結(jié)束了,認(rèn)證也以失敗告終小染。如果用戶覺得 OK翘瓮,在授權(quán)頁面點(diǎn)擊了確認(rèn)授權(quán)后,頁面會(huì)跳轉(zhuǎn)到我預(yù)先設(shè)定的
redirect_uri
并附帶一個(gè)蓋了章的門票 code裤翩。
// 協(xié)商成功后帶著蓋了章的 code
Location: http://my-website.com?code=xxx
這個(gè)時(shí)候资盅,用戶和 Github 之間的協(xié)商就已經(jīng)完成,Github 也會(huì)在自己的系統(tǒng)中記錄這次協(xié)商踊赠,表示該用戶已經(jīng)允許在我的網(wǎng)站訪問上直接操作和使用他的部分資源呵扛。
- 告訴 Github 我的網(wǎng)站要來拜訪了
第二步中,我們已經(jīng)拿到了蓋過章的門票 code筐带,但這個(gè) code 只能表明今穿,用戶允許我的網(wǎng)站從 github 上獲取該用戶的數(shù)據(jù),如果我直接拿這個(gè) code 去 github 訪問數(shù)據(jù)一定會(huì)被拒絕伦籍,因?yàn)槿魏稳硕伎梢猿钟?code蓝晒,github 并不知道 code 持有方就是我本人腮出。
還記得之前申請(qǐng)應(yīng)用的時(shí)候 github 給我的兩張門票么,Client Id 在上一步中已經(jīng)用過了拔创,接下來輪到另一張門票 Client Secret利诺。
// 網(wǎng)站和 github 之間的協(xié)商
POST //github.com/login/oauth/access_token
// 協(xié)商憑證包括 github 給用戶蓋的章和 github 發(fā)給我的門票
params = {
code: "xxx",
client_id: "xxx",
client_secret: "xxx",
redirect_uri: "http://my-website.com"
}
拿著用戶蓋過章的 code 和能夠標(biāo)識(shí)個(gè)人身份的 client_id富蓄、client_secret 去拜訪 github剩燥,拿到最后的綠卡 access_token。
// 拿到最后的綠卡
response = {
access_token: "e72e16c7e42f292c6912e7710c838347ae178b4a"
scope: "user,gist"
token_type: "bearer",
refresh_token: "xxxx"
}
- 用戶開始使用 github 帳號(hào)在我的頁面上留言
// 訪問用戶數(shù)據(jù)
GET //api.github.com/user?access_token=e72e16c7e42f292c6912e7710c838347ae178b4a
上一步 github 已經(jīng)把最后的綠卡 access_token 給我了立倍,通過 github 提供的 API 加綠卡就能夠訪問用戶的信息了灭红,能獲取用戶的哪些權(quán)限在 response 中也給了明確的說明,scope 為 user 和 gist口注,也就是只能獲取 user 組和 gist 組兩個(gè)小組的權(quán)限变擒,user 組中就包含了用戶的名字和郵箱等信息了。
// 告訴我用戶的名字和郵箱
response = {
username: "barretlee",
email: "barret.china@gmail.com"
}
整個(gè) OAuth2 流程在這里也基本完成了寝志。
四娇斑、回過頭分析小程序?yàn)槭裁慈绱嗽O(shè)計(jì)授權(quán)
明確一點(diǎn):小程序和微信是兩個(gè)程序,只不過小程序的入口在微信內(nèi)材部。所以微信的接口一般小程序是不能直接調(diào)用的毫缆,得先授權(quán)。
微信小程序登錄時(shí)序圖真正的目的不是小程序自身去請(qǐng)求授權(quán)乐导,而是通過小程序去獲取授權(quán) token苦丁,然后給第三方服務(wù)器用,第三方服務(wù)器會(huì)拿這個(gè) token 去調(diào)用微信服務(wù)器授權(quán)的相關(guān)接口物臂。
1. 注冊(cè)微信公眾賬號(hào)
開發(fā)微信小程序第一步要在 微信公眾平臺(tái) 上注冊(cè)一個(gè)賬號(hào)旺拉,注冊(cè)完成后在 設(shè)置 > 開發(fā)設(shè)置
里可以看到 AppID 和 AppSecret,這相當(dāng)于上面提到的 Client Id 和 Client Secret棵磷。
2. 明確對(duì)應(yīng)關(guān)系
用戶 | 第三方應(yīng)用 | 授權(quán)服務(wù)器 | 備注 |
---|---|---|---|
用戶 | 小胡子的網(wǎng)站 | github | 小胡子網(wǎng)站獲取 github 授權(quán)用戶基本信息 |
用戶 | 簡書 | QQ 服務(wù)器 | 簡書獲取 QQ 服務(wù)器授權(quán)基本信息 |
微信登錄用戶 | 小程序 | 微信服務(wù)器 | 小程序獲得微信服務(wù)器授權(quán)可以調(diào)用一些微信接口蛾狗,如獲取微信登錄用戶的基本信息、調(diào)用微信支付接口 |
3. wx.login
接口獲取 code
wx.login({
success: function(res) {
if (res.code) {
//發(fā)起網(wǎng)絡(luò)請(qǐng)求
wx.request({
url: 'https://test.com/onLogin',
data: {
code: res.code
}
})
} else {
console.log('獲取用戶登錄態(tài)失斠敲健沉桌!' + res.errMsg)
}
}
});
微信登錄用戶
在 微信
中打開 小程序
,小程序
中運(yùn)行了 wx.login
接口规丽,此時(shí) 小程序
拿著 appid 去請(qǐng)求 微信服務(wù)器
給 小程序
授權(quán)蒲牧,讓它可以調(diào)用一些微信接口,比如獲取微信登錄用戶的基本信息赌莺。因?yàn)槭?微信登錄用戶
進(jìn)行操作的冰抢,所以微信那端默認(rèn)是同意授權(quán)的。(appid 哪里來艘狭?在新建小程序項(xiàng)目時(shí)輸入挎扰,每個(gè) appid 和小程序都是一一對(duì)應(yīng)的)
4. 拿著 code
去獲取令牌
上面也提到翠订,這個(gè) code 可能會(huì)泄露,所以需要 code + appid + appsecret
去再次拜訪微信服務(wù)器獲取令牌 session_key 和用戶標(biāo)識(shí) openid遵倦。
這里有個(gè)疑問尽超?為什么獲取 code 在小程序上做,而獲取 session_key 在第三方服務(wù)器上操作梧躺?這是因?yàn)楂@取 code 需要使用 appid 去跳轉(zhuǎn)微信服務(wù)器授權(quán)頁面似谁,再使用當(dāng)前 微信登錄用戶
默認(rèn)同意此次授權(quán),如果坐在第三方服務(wù)器上掠哥,appid 我們是知道的巩踏,但是當(dāng)前 微信登錄用戶
是不知道,只要手機(jī)上的微信知道续搀。
那為什么請(qǐng)求 session_key 在第三方服務(wù)器上操作塞琼?這是因?yàn)榍懊嬉舱f過 code 只能表明微信服務(wù)器統(tǒng)一此次授權(quán),但是 code 可能會(huì)泄露禁舷,還需要第二張門票 appsecret 告訴微信服務(wù)器 “就是我彪杉,不是別人” 來請(qǐng)求獲取 session_key 的∏A可見 appsecret 對(duì)于安全的重要性派近,放在第三方服務(wù)器方便管理。
關(guān)于 openid 和 session_key 說明霜大?session_key 就是返回的綠卡构哺,此后每次訪問微信服務(wù)器資源都要帶上它,這樣微信服務(wù)器才知道此次請(qǐng)求是有授權(quán)的战坤,我可以把資源給你曙强。openid 是同意授權(quán)的那個(gè)人,這里是 微信登錄用戶
的標(biāo)識(shí) ID途茫。也就是為什么 code 要在小程序中獲取中我們不知道的那個(gè) 微信登錄用戶
ID碟嘴。
到此,上圖中涉及 Oauth2.0 的部分已經(jīng)結(jié)束囊卜,下面的那些都是普通的 session 維持會(huì)話機(jī)制娜扇,這里就不再贅述。
五栅组、參考文章
六、最后
由于作者文筆有限玉掸,文章如有遺漏或表達(dá)有誤刃麸,請(qǐng)不吝賜教。如果仍對(duì)微信授權(quán)或 Oauth2.0 有疑問的司浪,歡迎留言討論泊业。