基于python flask詳述 OAuth 2.0 的運(yùn)作流程

OAuth2流程圖

OAuth2 對(duì)于我來(lái)說(shuō)是一個(gè)神秘的東西,我想初步的弄懂中間的整個(gè)流程,于是就去google搜索相關(guān)的文檔資料。

在瀏覽了參差不齊的各種文章后,簡(jiǎn)述 OAuth 2.0 的運(yùn)作流程 基本對(duì)于小白來(lái)說(shuō)是最淺顯明了的。

這篇文章以用戶使用 github 登錄網(wǎng)站留言為例,詳述 OAuth 2.0 的運(yùn)作流程。

整個(gè)OAuth2 的流程分為三個(gè)階段:

  1. 網(wǎng)站和 Github 之間的協(xié)商
  2. 用戶和 Github 之間的協(xié)商
  3. 網(wǎng)站和 Github 用戶數(shù)據(jù)之間的協(xié)商

由于這篇文章是簡(jiǎn)述,所以并不涉及代碼相關(guān)的東西,我在原來(lái)的文章基礎(chǔ)上添加了代碼相關(guān)的具體實(shí)現(xiàn)和一些關(guān)鍵網(wǎng)絡(luò)交互截圖說(shuō)明方便理解。對(duì)于一些文字,由于原文已經(jīng)寫(xiě)的很流暢嚴(yán)謹(jǐn),我直接就從原來(lái)的博文中復(fù)制過(guò)來(lái)了。


假如我有一個(gè)網(wǎng)站,你是我網(wǎng)站上的訪客,看了文章想留言表示「朕已閱」,留言時(shí)發(fā)現(xiàn)有這個(gè)網(wǎng)站的帳號(hào)才能夠留言,此時(shí)給了你兩個(gè)選擇:一個(gè)是在我的網(wǎng)站上注冊(cè)擁有一個(gè)新賬戶,然后用注冊(cè)的用戶名來(lái)留言;一個(gè)是使用 github 帳號(hào)登錄,使用你的 github 用戶名來(lái)留言。前者你覺(jué)得過(guò)于繁瑣,于是慣性地點(diǎn)擊了 github 登錄按鈕,此時(shí) OAuth 認(rèn)證流程就開(kāi)始了。

需要明確的是,即使用戶剛登錄過(guò) github,我的網(wǎng)站也不可能向 github 發(fā)一個(gè)什么請(qǐng)求便能夠拿到訪客信息,這顯然是不安全的。就算用戶允許你獲取他在 github 上的信息,github 為了保障用戶信息安全,也不會(huì)讓你隨意獲取。所以操作之前,我的網(wǎng)站與 github 之間需要要有一個(gè)協(xié)商。

1. 網(wǎng)站和 Github 之間的協(xié)商

Github 會(huì)對(duì)用戶的權(quán)限做分類,比如讀取倉(cāng)庫(kù)信息的權(quán)限、寫(xiě)入倉(cāng)庫(kù)的權(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í)候填寫(xiě)你的網(wǎng)站域名,Github 只允許在這個(gè)域名中獲取用戶信息。

此時(shí)我的網(wǎng)站已經(jīng)和 Github 之間達(dá)成了共識(shí),Github 也給我發(fā)了兩張門(mén)票,一張門(mén)票叫做 Client Id,另一張門(mén)票叫做 Client Secret。

我先去閱讀了一下github上相關(guān)OAuth2的資料,然后在這里注冊(cè)了一個(gè)應(yīng)用。


其中最后一個(gè)callback URL表示用戶授權(quán)之后github默認(rèn)要跳轉(zhuǎn)的url地址,在代碼中需要添加一個(gè)路由來(lái)處理針對(duì)這個(gè)地址的請(qǐng)求。

創(chuàng)建好之后就會(huì)顯示在OAuth Apps的列表中。

這一步非常簡(jiǎn)單,github生成了兩個(gè)鑰匙,Client ID和Client Secret。現(xiàn)在我的網(wǎng)站就可以使用合法的使用github提供的OAuth登陸機(jī)制了。

2. 用戶和 Github 之間的協(xié)商

用戶進(jìn)入我的網(wǎng)站,點(diǎn)擊 github 登錄按鈕的時(shí)候,我的網(wǎng)站會(huì)把上面拿到的 Client Id 交給用戶,讓他進(jìn)入到 Github 的授權(quán)頁(yè)面,Github 看到了用戶手中的門(mén)票,就知道這是我的網(wǎng)站讓他過(guò)來(lái)的,于是它就把我的網(wǎng)站想要獲取的權(quán)限擺出來(lái),并詢問(wèn)用戶是否允許我獲取這些權(quán)限。

如果用戶覺(jué)得我的網(wǎng)站要的權(quán)限太多,或者壓根就不想我知道他這些信息,選擇了拒絕的話,整個(gè) OAuth 2.0 的認(rèn)證就結(jié)束了,認(rèn)證也以失敗告終。如果用戶覺(jué)得 OK,在授權(quán)頁(yè)面點(diǎn)擊了確認(rèn)授權(quán)后,頁(yè)面會(huì)跳轉(zhuǎn)到我預(yù)先設(shè)定的 redirect_uri 并附帶一個(gè)蓋了章的門(mén)票 code。

這個(gè)時(shí)候,用戶和 Github 之間的協(xié)商就已經(jīng)完成,Github 也會(huì)在自己的系統(tǒng)中記錄這次協(xié)商,表示該用戶已經(jīng)允許在我的網(wǎng)站訪問(wèn)上直接操作和使用他的部分資源。

這個(gè)中間會(huì)涉及到非常多的流程,我選擇使用python基于flask來(lái)演示整個(gè)流程。

# github生成的兩把鑰匙
client_id = '1f93ab8ba338b032b8e7'
client_secret = 'f0cf5600d2749d1651f2d5f7225c81f562******'


@app.route('/', methods=['GET', 'POST'])
def index():
    url = 'https://github.com/login/oauth/authorize'
    params = {
        'client_id': client_id,
        # 如果不填寫(xiě)redirect_uri那么默認(rèn)跳轉(zhuǎn)到oauth中配置的callback url。
        # 'redirect_uri': 'http://dig404.com/oauth2/github/callback',
        'scope': 'read:user',
        # 隨機(jī)字符串,防止csrf攻擊
        'state': 'An unguessable random string.',
        'allow_signup': 'true'
    }
    url = furl(url).set(params)
    return redirect(url, 302)

當(dāng)用戶在瀏覽器中訪問(wèn)127.0.0.1:5000的時(shí)候,flask會(huì)將請(qǐng)求重定向到github的oauth服務(wù)頁(yè)面,重定向的url會(huì)攜帶上兩個(gè)主要的參數(shù),一個(gè)是client_id,一個(gè)是scope,這兩個(gè)參數(shù)可以讓github知道這個(gè)請(qǐng)求是從哪里過(guò)來(lái)的,并且想要獲取的權(quán)限。

訪問(wèn)125.0.0.1:5000后flask重定向到github授權(quán)頁(yè)面
如果沒(méi)有登陸github那么首先github會(huì)先跳轉(zhuǎn)到用戶的登陸頁(yè)面
用戶登陸自己的github賬號(hào)
登陸成功后跳轉(zhuǎn)到授權(quán)頁(yè)面
點(diǎn)擊授權(quán)后github跳轉(zhuǎn)到之前設(shè)置的callback頁(yè)面

其中最終重定向url中的code參數(shù)就是github分配的針對(duì)當(dāng)前登陸用戶的授權(quán)碼,也就是一張門(mén)票。在github的后臺(tái),這個(gè)code和client_id,user是對(duì)應(yīng)的。

到這里用戶和github之間的協(xié)商就完成了,剩下的事情就是網(wǎng)站和github之間的事情了。

3. 從 Github 獲取用戶的信息

第二步中,已經(jīng)拿到了蓋過(guò)章的門(mén)票 code,但這個(gè) code 只能表明,用戶允許我的網(wǎng)站從 github 上獲取該用戶的數(shù)據(jù),如果我直接拿這個(gè) code 去 github 訪問(wèn)數(shù)據(jù)一定會(huì)被拒絕,因?yàn)槿魏稳硕伎梢猿钟?code,github 并不知道 code 持有方就是我本人。

還記得之前申請(qǐng)應(yīng)用的時(shí)候 github 給我的兩張門(mén)票么,Client Id 在上一步中已經(jīng)用過(guò)了,接下來(lái)輪到另一張門(mén)票 Client Secret。

創(chuàng)建一個(gè)處理callback路由的處理函數(shù),首先是獲取github返回的code。

@app.route('/oauth2/<service>/callback')
def oauth2_callback(service):
    print(service)

    code = request.args.get('code')
    # 根據(jù)返回的code獲取access token
    access_token_url = 'https://github.com/login/oauth/access_token'
    payload = {
        'client_id': client_id,
        'client_secret': client_secret,
        'code': code,
        # 'redirect_uri':
        'state': 'An unguessable random string.'
    }
    r = requests.post(access_token_url, json=payload, headers={'Accept': 'application/json'})
    access_token = json.loads(r.text).get('access_token')
    # 拿到access token之后就可以去讀取用戶的信息了
    access_user_url = 'https://api.github.com/user'
    r = requests.get(access_user_url, headers={'Authorization': 'token ' + access_token})
    return jsonify({
        'status': 'success',
        'data': json.loads(r.text)
    })

拿著用戶蓋過(guò)章的 code 和能夠標(biāo)識(shí)個(gè)人身份的 client_id、client_secret 去拜訪 github,拿到最后的綠卡 access_token。

有了access_token之后就可以讀取用戶授權(quán)的信息了,最后為了演示我把讀取到的信息回顯到了網(wǎng)頁(yè)上。
這其中的過(guò)程對(duì)于用戶來(lái)說(shuō)不可見(jiàn)的,用戶最終在瀏覽器中的url還是第二步重定向的url。

讀取到的用戶信息

拿到用戶信息后其實(shí)就相當(dāng)于用戶已經(jīng)登陸了,下一步就可以基于獲取到的用戶信息對(duì)用戶做一些業(yè)務(wù)相關(guān)的處理了。


整個(gè) OAuth2 流程在這里也基本完成了,文章中的表述很粗糙,比如 access_token 這個(gè)綠卡是有過(guò)期時(shí)間的,如果過(guò)期了需要使用 refresh_token 重新簽證。重點(diǎn)是讓讀者理解整個(gè)流程,細(xì)節(jié)部分可以閱讀 RFC6749 文檔

希望對(duì)你理解 OAuth 2.0 有幫助。


我的博客即將搬運(yùn)同步至騰訊云+社區(qū),邀請(qǐng)大家一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=jgq8ithnd28h

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 本文將以用戶使用 github 登錄網(wǎng)站留言為例,簡(jiǎn)述 OAuth 2.0 的運(yùn)作流程。 假如我有一個(gè)網(wǎng)站,你是...
    plusend閱讀 439評(píng)論 0 4
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,578評(píng)論 19 139
  • 以下是官網(wǎng)直譯:https://oauth.net/ 1. 首頁(yè) OAuth是一種開(kāi)放協(xié)議(注:協(xié)議是公開(kāi)的,任何...
    JacoChan閱讀 11,774評(píng)論 0 20
  • 本文以一種簡(jiǎn)化的格式描述OAuth 2.0 ,以幫助開(kāi)發(fā)人員和服務(wù)提供者實(shí)現(xiàn)該協(xié)議。 The OAuth 2 sp...
    JacoChan閱讀 4,318評(píng)論 1 11
  • 莫泊桑說(shuō):“人生活在希望之中,一個(gè)希望破滅了或?qū)崿F(xiàn)了,就會(huì)有新的希望產(chǎn)生”。回過(guò)頭審視一下自己的2017,年...
    sulie閱讀 248評(píng)論 1 0

友情鏈接更多精彩內(nèi)容