假如上面的網站A,可以通過GitHub賬號登錄;
下面以OAuth其中一種方式,授權碼(authorization code)方式為例。
一、第三方登錄的原理
所謂第三方登錄,實質就是 OAuth 授權。
數據的所有者告訴系統,同意授權第三方應用進入系統,獲取數據。系統從而產生一個短期的進入令牌(token),用來代替密碼,供第三方應用使用。
用戶想要登錄 A 網站,A 網站讓用戶提供第三方網站的數據,證明自己的身份。獲取第三方網站的身份數據,就需要 OAuth 授權。
這里利用OAuth authorization code的形式。
舉例來說,A 網站允許 通過GitHub 登錄,背后就是下面的流程。
- A 網站前端讓用戶帶着client_id, response_type(如code授權碼模式), redirect_uri, scope跳轉到 GitHub。
- GitHub 要求用戶登錄,然后詢問用戶"A 網站要求獲得 xx 權限,你是否同意?", 同意后會返回授權碼code同時重定向URL
- 用戶同意,GitHub 就會重定向回 A 網站的后端API URL,同時發回一個授權碼,如
http://localhost:8080/oauth/redirect?
code=859310e7cecc9196f4af
- A 網站后端帶着授權碼code參數,向 GitHub發送請求,想獲得令牌。
- GitHub 返回令牌,這里是一個字符串.
- A 網站使用令牌,請求GitHub API如
https://api.github.com/user
,請求的時候必須在 HTTP 頭信息里面帶上令牌Authorization: token 361507da,就可以
獲得用戶數據。
下面就是這個流程的代碼實現。
二、應用登記
一個應用要求 OAuth 授權,必須先到對方網站登記,讓對方知道是誰在請求。
所以,你要先去 GitHub 登記一下。當然,我已經登記過了,你使用我的登記信息也可以,但為了完整走一遍流程,還是建議大家自己登記。這是免費的。
訪問這個網址,填寫登記表。
應用的名稱隨便填,主頁 URL 填寫http://localhost:8080
,跳轉網址填寫 http://localhost:8080/oauth/redirect
。
提交表單以后,GitHub 應該會返回客戶端 ID(client ID)和客戶端密鑰(client secret),這就是應用的身份識別碼。
三、示例倉庫
我寫了一個代碼倉庫,請將它克隆到本地。
$ git clone git@github.com:ruanyf/node-oauth-demo.git $ cd node-oauth-demo
兩個配置項要改一下,寫入上一步的身份識別碼。
index.js
:改掉變量clientID
andclientSecret
public/index.html
:改掉變量client_id
然后,安裝依賴。
$ npm install
啟動服務。
$ node index.js
瀏覽器訪問http://localhost:8080
,就可以看到這個示例了。
四、瀏覽器跳轉 GitHub
示例的首頁很簡單,就是一個鏈接,讓用戶跳轉到 GitHub。
跳轉的 URL 如下。
https://github.com/login/oauth/authorize? client_id=7e015d8ce32370079895& redirect_uri=http://localhost:8080/oauth/redirect
這個 URL 指向 GitHub 的 OAuth 授權網址,帶有兩個參數:client_id
告訴 GitHub 誰在請求,redirect_uri
是稍后跳轉回來的網址。
用戶點擊到了 GitHub,GitHub 會要求用戶登錄,確保是本人在操作。
五、授權碼
登錄后,GitHub 詢問用戶,該應用正在請求數據,你是否同意授權。
用戶同意授權, GitHub 就會跳轉到redirect_uri
指定的跳轉網址,並且帶上授權碼,跳轉回來的 URL 就是下面的樣子。
http://localhost:8080/oauth/redirect? code=859310e7cecc9196f4af
后端收到這個請求以后,就拿到了授權碼(code
參數)。
六、后端實現
這里的關鍵是針對/oauth/redirect
的請求,編寫一個路由,完成 OAuth 認證。
const oauth = async ctx => { // ... }; app.use(route.get('/oauth/redirect', oauth));
上面代碼中,oauth
函數就是路由的處理函數。下面的代碼都寫在這個函數里面。
路由函數的第一件事,是從 URL 取出授權碼。
const requestToken = ctx.request.query.code;
七、令牌
后端使用這個授權碼,向 GitHub 請求令牌。
const tokenResponse = await axios({ method: 'post', url: 'https://github.com/login/oauth/access_token?' + `client_id=${clientID}&` + `client_secret=${clientSecret}&` + `code=${requestToken}`, headers: { accept: 'application/json' } });
上面代碼中,GitHub 的令牌接口https://github.com/login/oauth/access_token
需要提供三個參數。
client_id
:客戶端的 IDclient_secret
:客戶端的密鑰code
:授權碼
作為回應,GitHub 會返回一段 JSON 數據,里面包含了令牌accessToken
。
const accessToken = tokenResponse.data.access_token;
八、API 數據
有了令牌以后,就可以向 API 請求數據了。
const result = await axios({ method: 'get', url: `https://api.github.com/user`, headers: { accept: 'application/json', Authorization: `token ${accessToken}` } });
上面代碼中,GitHub API 的地址是https://api.github.com/user
,請求的時候必須在 HTTP 頭信息里面帶上令牌Authorization: token 361507da
。
然后,就可以拿到用戶數據,得到用戶的身份。
const name = result.data.name; ctx.response.redirect(`/welcome.html?name=${name}`);
(完)
來源: http://www.ruanyifeng.com/blog/2019/04/github-oauth.html
OAuth 2.0 的四種方式: http://www.ruanyifeng.com/blog/2019/04/oauth-grant-types.html
OAuth 2.0 的一個簡單解釋: http://www.ruanyifeng.com/blog/2019/04/oauth_design.html
理解OAuth 2.0: http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html