1.什麼是三方登入?
三方登入就是通過第三方應用程式的賬号密碼, 快速的擷取使用者相關的資訊實作登入
例如: QQ登入
點選QQ登入按鈕之後,就會要求使用者輸入QQ的賬号和密碼
隻要使用者輸入了QQ的賬号和密碼, 我們就可以拿到使用者的QQ資訊
我們就可以通過使用者的QQ資訊來實作注冊登入
2.三方登入存在的問題
如果讓使用者在我們的網站上輸入QQ的賬号和密碼,
那麼我們就可以竊取使用者的QQ号, 做一些見不得人的事
3.如何解決這個問題?
通過OAuth協定進行授權
4.什麼是OAuth協定?(Open Authorization)
OAuth協定為使用者資源的授權提供了一個安全的、開放而又簡易的标準。
與以往的授權方式不同之處是OAuth的授權不會使第三方觸及到使用者的帳号資訊(如使用者名與密碼),
即第三方無需使用使用者的使用者名與密碼就可以申請獲得該使用者資源的授權,
是以OAuth是安全可靠的
5.OAuth授權流程
(1)第三方應用請求資源所有者(使用者)授權。
(2)資源所有者同意給第三方應用授權。
(3)第三方應用使用步驟2中獲得的授權,向授權伺服器申請令牌。
(4)授權伺服器對第三方應用進行認證并确認無誤後,同意發放令牌。
(5)第三方應用使用步驟4中發放的令牌向資源伺服器申請擷取資源。
(6)資源伺服器确認令牌無誤後,向第三方應用開放資源通路。
OAth授權實作流程
(1)去到需要接入的網站申請接入
例如: 要實作Github登入, 就去到Github申請接入
https://github.com/settings/applications/new

注意上圖有誤:回調url應該是請求的後端伺服器,而不是前端伺服器
(2)申請完接入之後會得到一個Id和Secret
例如: 申請完Github登入, 我們會得到
Client ID:xxx
Client Secret:xxxx
(3)在自己的網站放上對應的第三方登入按鈕
<li class="iconfont icon-github" style="color: #000">
<a href="http://127.0.0.1:7001/github"></a>
</li>
(4)當使用者點選登入按鈕之後, 按照文檔要求帶着申請到的id擷取登入界面,為什麼要帶id, 因為需要知道是你有沒有接入權限,因為要告訴使用者是給誰授權
https://docs.github.com/en/developers/apps/authorizing-oauth-apps
照着官方文檔的web application flow的步驟來做;
(5)當使用者點選授權按鈕之後, 會跳轉我們之前設定好的回調頁面去,并且傳回一個code給我們,這個code就是使用者同意授權的臨時證據
(6)我們拿着這個臨時證據(code)去到授權伺服器換取長久令牌,授權伺服器會驗證使用者是否真的同意, 如果同意會傳回一個access_token給我們,這個access_token就是長久令牌
(7)我們拿着長久的令牌去到資源伺服器擷取授權使用者相關的資源
//router.ts
router.get('/github', controller.github.loginView);
router.get('/github/callback', controller.github.getAccessToken);
//controller/github.ts
import { Controller } from 'egg';
const queryString = require('querystring');
export default class GithubController extends Controller {
public async loginView() {
// 1.擷取第三方登入界面
// 發送get請求到https://github.com/login/oauth/authorize帶上一些參數即可
// client_id: Github可以根據這個client_id判斷你有沒有申請接入
// Github會根據這個client_id查詢出對應的應用程式名稱, 告訴使用者正在給哪個程式授權
// scope : 授權範圍
const baseURL = 'https://github.com/login/oauth/authorize';
const option = {
client_id: 'xxx',
scope: 'user'
}
const url = baseURL + '?' + queryString.stringify(option);
const {ctx} = this;
//這個頁面是使用者進行授權的頁面
ctx.redirect(url);
}
//上一步使用者授權登陸完成後,會傳回code,請求的是我們之前在github上設定好的回調位址
public async getAccessToken(){
const {ctx} = this;
// 1.拿到使用者同意授權之後的code
const {code} = ctx.query;
// 2.利用code換取令牌(access_token)
// 發送POST請求到https://github.com/login/oauth/access_token帶上必要的參數
const baseURL = 'https://github.com/login/oauth/access_token';
const option = {
client_id:'xxx',
client_secret:'xxxxx',
code:code
}
const result = await ctx.curl(baseURL, {
method: 'POST',
data: option,
dataType: 'json',
headers:{
'Content-Type': 'application/json',
'Accept':'application/json'
}
});
const accessToken = result.data.access_token;
console.log("accessToken",accessToken)
// 3.拿着令牌去資源伺服器擷取資料
await this.getGithubUserInfo(accessToken);
}
//擷取使用者資料
private async getGithubUserInfo(accessToken){
const {ctx} = this;
const baseURL = 'https://api.github.com/user';
const url = `${baseURL}?access_token=${accessToken}`;
const result = await ctx.curl(url, {
method: 'GET',
headers:{
Authorization:"token "+accessToken
}
});
console.log("here",JSON.parse(result.data));
ctx.body="hello"
//坑:我們希望使用者界面跳轉github回調位址的時候,不要顯示404 not found,而是顯示hello,那麼首先因為這個是異步的方法,是以調用this.getGithubUserInfo()的前面要加上await,另外,在執行所有路由對應的方法前,都要先執行中間件方法,那麼中間件next()的時候前面也要await,不然就沒等異步方法走完,就結束了,就相當于沒有處理
}
}
如何反複測試
選擇浏覽器的清除曆史記錄,這樣就能夠反複測試使用者授權功能了