用 Authing 10分鐘實現單點登入(SSO)
單點登入(Single Sign On),簡稱為SSO,是目前比較流行的企業業務整合的解決方案之一。 SSO的定義是在多個應用系統中,使用者只需要登入一次就可以訪問所有相互信任的應用系統。
實現單點登入
開始之前
如果你不瞭解使用者池、單點登入和認證授權,建議先閱讀基礎概念。
預備知識
- 基本的 HTML 和 CSS 知識
- 中級 JavaScript 技能
所需工具
- 你喜歡的文字編輯器
- 可以在本地執行的 Web 伺服器(比如:
npm install http-server -g
)
註冊一個 Authing 賬號
如果你還沒有賬號,請點選這裡註冊 Authing 賬號
建立一個 OIDC 應用
第三方登入 -> OIDC 應用選項卡,點選藍色的「建立 OIDC 應用」按鈕。
填上你的應用名,指定此 OIDC 應用的二級域名(認證地址),回撥地址,其他引數保留預設即可。點選「確定」。
引數解釋
認證地址,一個 authing.cn 的二級域名,使用者將在此網址進行登入。
回撥 URL,OIDC 登入成功後,回撥到開發者自己業務的地址。本教程為演示,填寫的地址是 http://localhost:8080,實際場景下要填寫自己的業務地址。
在應用列表中點選剛建立好的應用,記錄下 AppID,二級域名,供以後使用。
使用 AuthingSSO SDK 整合單點登入
建立一個空白的 HTML 文件用來編寫 Authing 程式
本教程只是為了演示,因此我們沒選擇高階框架,這可以讓我們專注於 Authing 本身。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>Authing SSO Example</title> </head> <body></body> </html>
新增三個按鈕
增加三個按鈕控制元件到 body 中,目的是為了演示如何使用 SDK 管理單點登入狀態。
<button id="btn-login">login</button>
<button id="btn-track-session">trackSession</button>
<button id="btn-logout">logout</button>
引入 AuthingSSO 並初始化
從 CDN 載入 AuthingSSO 的 SDK。填入你的 OIDC 應用 ID 和 域名,進行初始化。
<script src="https://cdn.jsdelivr.net/npm/@authing/sso/dist/AuthingSSO.umd.min.js"></script>
<script>
let auth = new AuthingSSO({
appId: "YOUR_OIDC_APP_ID",
appType: "oidc",
appDomain: "OIDC_APP_DOMAIN.authing.cn"
});
</script>
為按鈕註冊點選事件
達到的效果是:
- 點選 login 按鈕,瀏覽器會跳轉到 OIDC 登入頁面,與使用者完成身份確認。
- 點選 trackSession 按鈕,會顯示當前登入狀態。
- 點選 logout 按鈕,進行單點登出。
let login = document.getElementById("btn-login");
let trackSession = document.getElementById("btn-track-session");
let logout = document.getElementById("btn-logout");
login.onclick = function() {
auth.login();
};
trackSession.onclick = async function() {
let res = await auth.trackSession();
alert(JSON.stringify(res));
};
logout.onclick = async function() {
let res = await auth.logout();
alert(JSON.stringify(res));
};
完整程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Authing SSO Example</title>
</head>
<body>
<button id="btn-login">login</button>
<button id="btn-track-session">trackSession</button>
<button id="btn-logout">logout</button>
<script src="https://cdn.jsdelivr.net/npm/@authing/sso/dist/AuthingSSO.umd.min.js"></script>
<script>
let auth = new AuthingSSO({
appId: "YOUR_OIDC_APP_ID",
appType: "oidc",
appDomain: "OIDC_APP_DOMAIN.authing.cn"
});
let login = document.getElementById("btn-login");
let trackSession = document.getElementById("btn-track-session");
let logout = document.getElementById("btn-logout");
login.onclick = function() {
auth.login();
};
trackSession.onclick = async function() {
let res = await auth.trackSession();
alert(JSON.stringify(res));
};
logout.onclick = async function() {
let res = await auth.logout();
alert(JSON.stringify(res));
};
</script>
</body>
</html>
示例程式碼可從 Github 上找到,建議將 Github 上的程式碼下載執行。
執行方法
在終端中執行以下命令
$ git clone https://github.com/Authing/authing-sso-demo
$ cd authing-sso-demo
$ npm install -g http-server
$ http-server
之後在瀏覽器訪問 http://localhost:8080。
{% hint style="warning" %} 如果本地 8080 埠已被佔用,應用可能會執行在 8081、8082 等後續埠。 {% endhint %}
執行效果
最初,我們沒有登入,因此,點選 trackSession 按鈕獲取到的登入狀態為空。
現在我們點選 login 按鈕,會跳轉到 OIDC 應用的使用者認證頁面,輸入使用者名稱密碼進行登入。
瀏覽器被重定向到我們之前設定的回撥連結,記下 code 引數,用於後面換取使用者資訊。
點選 trackSession 按鈕,此時能夠獲取到該使用者的登入狀態,包括使用者 ID,應用 ID,應用型別。
點選 logout 按鈕,輸出單點登出成功。
此時我們再點選 trackSession 按鈕,可見登入狀態為空,說明使用者已經單點登出了。
獲取使用者資訊
使用 OIDC 流程中返回的 code 換取 access_token
向以下地址傳送 POST 請求:
POST https://OIDC_APP_DOMAIN.authing.cn/oauth/oidc/token
body 引數
引數名 | 意義 |
---|---|
client_id | OIDC 應用的 app_id |
redirect_uri | 在控制檯配置的 OIDC 回撥 url 其中的一個值 |
scope | 需要請求的許可權,如果需要獲取 email 和手機號需要有 phone email,如果需要 refresh_token 需要包含 offline_access 參考 scope 表格 |
response_type | OIDC 模式,可以為 code, id_token, id_token token, code id_token, code token, code id_token token 參考 OIDC 規範 |
prompt | 可以為 none,login,consent 或 select_account,指定 AP 與 End-User 的互動方式,如需 refresh_token,必須為 consent 參考 OIDC 規範 |
state | 一個隨機字串,用於防範 CSRF 攻擊,如果 response 中的 state 值和傳送請求之前設定的 state 值不同,說明受到攻擊 |
nonce | 一個隨機字串,用於防範 Replay 攻擊 |
返回示例
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InIxTGtiQm8zOTI1UmIyWkZGckt5VTNNVmV4OVQyODE3S3gwdmJpNmlfS2MifQ.eyJqdGkiOiJ4R01uczd5cmNFckxiakNRVW9US1MiLCJzdWIiOiI1YzlmNzVjN2NjZjg3YjA1YTkyMWU5YjAiLCJpc3MiOiJodHRwczovL2F1dGhpbmcuY24iLCJpYXQiOjE1NTQ1Mzc4NjksImV4cCI6MTU1NDU0MTQ2OSwic2NvcGUiOiJvcGVuaWQgcHJvZmlsZSBvZmZsaW5lX2FjY2VzcyBwaG9uZSBlbWFpbCIsImF1ZCI6IjVjYTc2NWUzOTMxOTRkNTg5MWRiMTkyNyJ9.wX05OAgYuXeYM7zCxhrkvTO_taqxrCTG_L2ImDmQjMml6E3GXjYA9EFK0NfWquUI2mdSMAqohX-ndffN0fa5cChdcMJEm3XS9tt6-_zzhoOojK-q9MHF7huZg4O1587xhSofxs-KS7BeYxEHKn_10tAkjEIo9QtYUE7zD7JXwGUsvfMMjOqEVW6KuY3ZOmIq_ncKlB4jvbdrduxy1pbky_kvzHWlE9El_N5qveQXyuvNZVMSIEpw8_y5iSxPxKfrVwGY7hBaF40Oph-d2PO7AzKvxEVMamzLvMGBMaRAP_WttBPAUSqTU5uMXwMafryhGdIcQVsDPcGNgMX6E1jzLA",
"expires_in": 3600,
"id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InIxTGtiQm8zOTI1UmIyWkZGckt5VTNNVmV4OVQyODE3S3gwdmJpNmlfS2MifQ.eyJzdWIiOiI1YzlmNzVjN2NjZjg3YjA1YTkyMWU5YjAiLCJub25jZSI6IjIyMTIxIiwiYXRfaGFzaCI6Ik5kbW9iZVBZOEFFaWQ2T216MzIyOXciLCJzaWQiOiI1ODM2NzllNC1lYWM5LTRjNDEtOGQxMS1jZWFkMmE5OWQzZWIiLCJhdWQiOiI1Y2E3NjVlMzkzMTk0ZDU4OTFkYjE5MjciLCJleHAiOjE1NTQ1NDE0NjksImlhdCI6MTU1NDUzNzg2OSwiaXNzIjoiaHR0cHM6Ly9hdXRoaW5nLmNuIn0.IQi5FRHO756e_eAmdAs3OnFMU7QuP-XtrbwCZC1gJntevYJTltEg1CLkG7eVhdi_g5MJV1c0pNZ_xHmwS0R-E4lAXcc1QveYKptnMroKpBWs5mXwoOiqbrjKEmLMaPgRzCOdLiSdoZuQNw_z-gVhFiMNxI055TyFJdXTNtExt1O3KmwqanPNUi6XyW43bUl29v_kAvKgiOB28f3I0fB4EsiZjxp1uxHQBaDeBMSPaRVWQJcIjAJ9JLgkaDt1j7HZ2a1daWZ4HPzifDuDfi6_Ob1ZL40tWEC7xdxHlCEWJ4pUIsDjvScdQsez9aV_xMwumw3X4tgUIxFOCNVEvr73Fg",
"refresh_token": "WPsGJbvpBjqXz6IJIr1UHKyrdVF",
"scope": "openid profile offline_access phone email",
"token_type": "Bearer"
}
驗證 access_token 和 id_token 的合法性
OIDC 預設使用 OIDC 應用的 secret 對 token 進行驗證(也就是在建立應用時預設選擇 HS256 演算法)。 如果你使用 javascript 那麼可以使用 jsonwebtoken 進行驗證:
const jwt = require('jsonwebtoken');
let decoded = jwt.verify(token, <appSecret>);
如果是其他語言,那麼你在服務端需要用 app_secret 作為 HS256 簽名引數來計算簽名和 JWT 中的簽名進行對比,虛擬碼如下:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
"1133fd20c14e4cc29b6ecb71fb8eb952"// app_secret
)
如果是 RS256 等非對稱加密演算法,需要使用公鑰驗證簽名。Authing 將使用私鑰進行簽名,請使用 Authing 的公鑰來驗證簽名:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxRijj2seoesv5K0Z+ymR
K7DSDPxdsM2sGQD2ZVhLjLsxZWJtXUXh7ERdUU6OT3BqYZZf7CLIhN6yyNtTOgfg
pLG9HVJd7ZSKzuy2dS7mo8jD8YRtptAJmNFqw6z8tQp5MNG1ZHqp9isKqJmx/CFY
kRdXBmjjj8PMVSP757pkC3jCq7fsi0drSSg4lIxrSsGzL0++Ra9Du71Qe/ODQKU0
brxaI1OKILtfcVPTHTaheV+0dw4eYkSDtyaLBG3jqsQbdncNg8PCEWchNzdO6aaj
Uq4wbOzy/Ctp399mz0SGKfuC5S8gqAFABFT3DH3UD21ZztQZwFEV2AlvF+bcGEst
cwIDAQAB
-----END PUBLIC KEY-----
使用 access_token 換取使用者資訊
開發者在自己的服務中可以使用 access_token 換取使用者資訊。根據 scope 的不同,這裡的返回資訊也會有所不同,欄位符合 OIDC 規範,欄位解釋請參考使用者資訊欄位含義。 請求連結:
GET https://users.authing.cn/oauth/oidc/user/userinfo?access_token=<access_token>
返回示例:
{
"sub": "<使用者在 Authing 的唯一標識>",
"nickname": "Authing",
"name": "張三",
"locale": "en-US"
}
更多欄位解釋請參考使用者資訊欄位含義。
接下來你可能還需要
瞭解 OIDC 協議:
控制檯是你管理所有 Authing 資源的地方,瞭解 Authing 控制檯各模組包含的內容和你可以在控制檯中做的事情:
瞭解 Authing 提供的多種部署模型,以幫助你選擇該以怎樣的形式部署 Authing:
什麼是 Authing?
Authing 提供專業的身份認證和授權服務。 我們為開發者和企業提供用以保證應用程式安全所需的認證模組,這讓開發人員無需成為安全專家。 你可以將任意平臺的應用接入到 Authing(無論是新開發的應用還是老應用都可以),同時你還可以自定義應用程式的登入方式(如:郵箱/密碼、簡訊/驗證碼、掃碼登入等)。 你可以根據你使用的技術,來選擇我們的 SDK 或呼叫相關 API 來接入你的應用。當用戶發起授權請求時,Authing 會幫助你認證他們的身份和返回必要的使用者資訊到你的應用中。