1. 程式人生 > >用 Authing 10分鐘實現單點登入(SSO)

用 Authing 10分鐘實現單點登入(SSO)

單點登入(Single Sign On),簡稱為SSO,是目前比較流行的企業業務整合的解決方案之一。 SSO的定義是在多個應用系統中,使用者只需要登入一次就可以訪問所有相互信任的應用系統。

實現單點登入

開始之前

如果你不瞭解使用者池、單點登入和認證授權,建議先閱讀基礎概念

預備知識

  1. 基本的 HTML 和 CSS 知識
  2. 中級 JavaScript 技能

所需工具

  1. 你喜歡的文字編輯器
  2. 可以在本地執行的 Web 伺服器(比如:npm install http-server -g

註冊一個 Authing 賬號

如果你還沒有賬號,請點選這裡註冊 Authing 賬號

,註冊完成後請進入控制檯並建立一個使用者池。

建立使用者池

選擇應用型別

建立一個 OIDC 應用

第三方登入 -> OIDC 應用選項卡,點選藍色的「建立 OIDC 應用」按鈕。

建立 ODC 應用

填上你的應用名,指定此 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 會幫助你認證他們的身份和返回必要的使用者資訊到你的應用中。