OAuth2 對于我來說是一個神秘的東西,我想初步的弄懂中間的整個流程,于是就去google搜索相關的文檔資料。
在瀏覽了參差不齊的各種文章后,簡述 OAuth 2.0 的運作流程 基本對于小白來說是最淺顯明了的。
這篇文章以用戶使用 github 登錄網站留言為例,詳述 OAuth 2.0 的運作流程。
整個OAuth2 的流程分為三個階段:
- 網站和 Github 之間的協商
- 用戶和 Github 之間的協商
- 網站和 Github 用戶數據之間的協商
由于這篇文章是簡述,所以并不涉及代碼相關的東西,我在原來的文章基礎上添加了代碼相關的具體實現和一些關鍵網絡交互截圖說明方便理解。對于一些文字,由于原文已經寫的很流暢嚴謹,我直接就從原來的博文中復制過來了。
假如我有一個網站,你是我網站上的訪客,看了文章想留言表示「朕已閱」,留言時發現有這個網站的帳號才能夠留言,此時給了你兩個選擇:一個是在我的網站上注冊擁有一個新賬戶,然后用注冊的用戶名來留言;一個是使用 github 帳號登錄,使用你的 github 用戶名來留言。前者你覺得過于繁瑣,于是慣性地點擊了 github 登錄按鈕,此時 OAuth 認證流程就開始了。
需要明確的是,即使用戶剛登錄過 github,我的網站也不可能向 github 發一個什么請求便能夠拿到訪客信息,這顯然是不安全的。就算用戶允許你獲取他在 github 上的信息,github 為了保障用戶信息安全,也不會讓你隨意獲取。所以操作之前,我的網站與 github 之間需要要有一個協商。
1. 網站和 Github 之間的協商
Github 會對用戶的權限做分類,比如讀取倉庫信息的權限、寫入倉庫的權限、讀取用戶信息的權限、修改用戶信息的權限等等。如果我想獲取用戶的信息,Github 會要求我,先在它的平臺上注冊一個應用,在申請的時候標明需要獲取用戶信息的哪些權限,用多少就申請多少,并且在申請的時候填寫你的網站域名,Github 只允許在這個域名中獲取用戶信息。
此時我的網站已經和 Github 之間達成了共識,Github 也給我發了兩張門票,一張門票叫做 Client Id,另一張門票叫做 Client Secret。
我先去閱讀了一下github上相關OAuth2的資料,然后在這里注冊了一個應用。
其中最后一個callback URL表示用戶授權之后github默認要跳轉的url地址,在代碼中需要添加一個路由來處理針對這個地址的請求。
創建好之后就會顯示在OAuth Apps的列表中。
這一步非常簡單,github生成了兩個鑰匙,Client ID和Client Secret。現在我的網站就可以使用合法的使用github提供的OAuth登陸機制了。
2. 用戶和 Github 之間的協商
用戶進入我的網站,點擊 github 登錄按鈕的時候,我的網站會把上面拿到的 Client Id 交給用戶,讓他進入到 Github 的授權頁面,Github 看到了用戶手中的門票,就知道這是我的網站讓他過來的,于是它就把我的網站想要獲取的權限擺出來,并詢問用戶是否允許我獲取這些權限。
如果用戶覺得我的網站要的權限太多,或者壓根就不想我知道他這些信息,選擇了拒絕的話,整個 OAuth 2.0 的認證就結束了,認證也以失敗告終。如果用戶覺得 OK,在授權頁面點擊了確認授權后,頁面會跳轉到我預先設定的 redirect_uri
并附帶一個蓋了章的門票 code。
這個時候,用戶和 Github 之間的協商就已經完成,Github 也會在自己的系統中記錄這次協商,表示該用戶已經允許在我的網站訪問上直接操作和使用他的部分資源。
這個中間會涉及到非常多的流程,我選擇使用python基于flask來演示整個流程。
# 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,
# 如果不填寫redirect_uri那么默認跳轉到oauth中配置的callback url。
# 'redirect_uri': 'http://dig404.com/oauth2/github/callback',
'scope': 'read:user',
# 隨機字符串,防止csrf攻擊
'state': 'An unguessable random string.',
'allow_signup': 'true'
}
url = furl(url).set(params)
return redirect(url, 302)
當用戶在瀏覽器中訪問127.0.0.1:5000的時候,flask會將請求重定向到github的oauth服務頁面,重定向的url會攜帶上兩個主要的參數,一個是client_id,一個是scope,這兩個參數可以讓github知道這個請求是從哪里過來的,并且想要獲取的權限。
其中最終重定向url中的code參數就是github分配的針對當前登陸用戶的授權碼,也就是一張門票。在github的后臺,這個code和client_id,user是對應的。
到這里用戶和github之間的協商就完成了,剩下的事情就是網站和github之間的事情了。
3. 從 Github 獲取用戶的信息
第二步中,已經拿到了蓋過章的門票 code,但這個 code 只能表明,用戶允許我的網站從 github 上獲取該用戶的數據,如果我直接拿這個 code 去 github 訪問數據一定會被拒絕,因為任何人都可以持有 code,github 并不知道 code 持有方就是我本人。
還記得之前申請應用的時候 github 給我的兩張門票么,Client Id 在上一步中已經用過了,接下來輪到另一張門票 Client Secret。
創建一個處理callback路由的處理函數,首先是獲取github返回的code。
@app.route('/oauth2/<service>/callback')
def oauth2_callback(service):
print(service)
code = request.args.get('code')
# 根據返回的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)
})
拿著用戶蓋過章的 code 和能夠標識個人身份的 client_id、client_secret 去拜訪 github,拿到最后的綠卡 access_token。
有了access_token之后就可以讀取用戶授權的信息了,最后為了演示我把讀取到的信息回顯到了網頁上。
這其中的過程對于用戶來說不可見的,用戶最終在瀏覽器中的url還是第二步重定向的url。
拿到用戶信息后其實就相當于用戶已經登陸了,下一步就可以基于獲取到的用戶信息對用戶做一些業務相關的處理了。
整個 OAuth2 流程在這里也基本完成了,文章中的表述很粗糙,比如 access_token 這個綠卡是有過期時間的,如果過期了需要使用 refresh_token 重新簽證。重點是讓讀者理解整個流程,細節部分可以閱讀 RFC6749 文檔。
希望對你理解 OAuth 2.0 有幫助。
我的博客即將搬運同步至騰訊云+社區,邀請大家一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=jgq8ithnd28h