設定自訂登入驗證
一、何謂自訂登入驗證?¶
自訂登入驗證就是將登入的 權柄 交給足夠信任的外部系統,藉由兩個系統之間的串接,完成登入的需求。
自訂登入具備以下功能與優點:
- 單一登入:
使用自訂登入,用戶可以使用一組認證資料(如使用者名稱和密碼、多因素驗證等)進行 UOF X 登入。這提供了便利的用戶體驗,無需為 UOF X 輸入不同的帳號和密碼。 - 可支援多因素驗證:
登入的 權柄 已經交給外部系統,如果外部系統支援多因素驗證,則可提供額外的安全層,以防止未經授權的訪問。透過結合使用者名稱和密碼以及額外的驗證因素(如簡訊驗證碼、智慧卡等),可以增強帳號的安全性。
自訂登入驗證需要提供 登入頁 、 Callback API 兩個功能,因此部份功能需要程式碼的撰寫才能完成。 您可以透過連結下載範例程式,此範例為 MVC 架構,使用的程式語言為 C#。
範例程式下載: https://github.com/
二、整合自訂登入驗證的優點¶
-
單一身份管理:
整合UOF X與自訂登入驗證後,組織可以使用一個系統作為統一的身份管理平台。用戶只需一組帳號和密碼,即可訪問UOF X系統以及其他整合的應用程式,簡化了用戶的身份管理和登入流程。 -
更多彈性的登入方式:
自訂登入驗證將登入 權柄 外放後,組織即可實作任何想要的登入方式,不管是多因素驗證或未來更安全有效的登入方式,都不再受限於 UOF X 是否支援的問題。
三、製作登入頁¶
首先,我們需要一個用於進行登入驗證的網頁,在範例中提供一個簡易的畫面如下,這是很常見的登入功能,使用者輸入帳號密碼後,按下登入按鈕進行驗證。
登入畫面 sample 位在範例中的 Views > Home > Index.cshtml
假設此登入頁網址為 https://mysystem.com.tw
,當從 UOF X 開啟 dialog 進入此登入頁時,會透過 query-string 傳遞 info 參數,這時網址實際為 https://mysystem.com.tw/uofx/login?info=xxxxxxxxxxxxx
,info 參數帶有重要的資訊,不應該做任何修改,其結構如下:
行動裝置的考量
使用者也可以透過 UOF X APP 進行自訂登入驗證,因此在登入畫面的設計上,請考量行動裝置的螢幕大小,以及操作的便利性。
1. 開啟頁面後保留 query-string info 資訊¶
在範例中 (Controllers > HomeController.cs
),當透過 dialog 開啟登入頁後,會先將 info 資訊另行保存,待後續登入成功後須回傳此資訊。
[HttpGet("uofx/login")]
public IActionResult Login()
{
// 從網址中接收 info 參數
if (!Request.Query.TryGetValue("info", out var info))
{
// 如果沒有 info 參數,返回錯誤
ViewBag.ErrorMessage = "info is required";
}
// 保存 info model 到 TempData,後續登入成功後須回傳
TempData["InfoModel"] = info.ToString();
return View("index");
}
2. 登入成功,產生 access-token¶
當使用者按下登入按鈕後,假如帳密驗證成功,這時需要產生一組 access-token 供後續流程使用,token 類型與內容沒有限制,但應該包含下列資訊與規範:
- 需含能代表登入帳號的資訊: 例如範例中把帳號辨識碼 (sid) 放入 token
- 要有時效性: 無時效性的 token 容易被拿來進行資安攻擊
public ActionResult Login(string username, string password)
{
//驗證帳號密碼
...
// 產生要登入的一次性帳號識別碼並放入 Token 中, callback 時用來識別身分
// (請依自己需求調整,但不建議把真實帳號放入 Token 中,因為 token 內容是公開的)
var sid = GetSid(username);
// 產生短時效 Token (可改成使用自己製作的 token)
var accessToken = TokenHelper.GenToken(sid, DateTimeOffset.Now.AddMinutes(5));
...
}
Note
範例中的 JWT Token 內容是公開的,只要拿到 token 的人都可以輕易取得其內容,因此請勿放敏感資料在其中,不過 token 含有 簽章 來避免被串改,所以不用擔心 token 的公開特性。
3. 返回 UOF X¶
要完成登入除了 access-token 外,還需要登入時 query-string 的 info 參數,可以透過 JsonSerializer.Deserialize<InfoModel>
轉換為 InfoModel
:
//驗證帳號密碼
...
//產生 access-token
...
//取得 info model
var modelJson = TempData["InfoModel"] as string;
byte[] bytes = Convert.FromBase64String(modelJson);
var infoString = Encoding.UTF8.GetString(bytes);
var infoModel = JsonSerializer.Deserialize<InfoModel>(infoString, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
PropertyNameCaseInsensitive = true
});
...
PostMessageModel
物件,此物件包含回傳給 UOF X 所需要資訊,接著將物件序列化成 JSON 字串。範例中回傳邏輯實作在 LoginSuccess.cshtml
,因此我們將 JSON 字串 導向此頁。
//驗證帳號密碼
...
//產生 access-token
...
//取得 info model
...
//製作 PostMessage Model
var model = new PostMessageModel
{
Info = infoModel,
Token = accessToken,
Message = "TPAD login success.",
StatusCode = 200 //必須為 200
};
// 將 model 序列化成 JSON 字串
var result = JsonSerializer.Serialize(model, new JsonSerializerOptions
{
PropertyNamingPolicy = new LowercaseFirstLetterPolicy(), // 將屬性名稱第一個字母轉為小寫
WriteIndented = true
});
return View("LoginSuccess", result); //將結果導向 LoginSuccess
LoginSuccess.cshtml 並無 UI ,僅處理回傳給 UOF X 的任務,在此分成兩種情境:
- 透過瀏覽器登入
- 透過 UOF X APP 登入
如是透過 瀏覽器登入,則使用 Post Message 的方式傳遞資料,須注意最後要自己關閉畫面 window.close()
。如果是透過 UOF X APP 登入,因為 APP 特性需透過約定的 Postback Function 來傳遞資料,請在你的系統中同時支援此兩種情境,使用者方能透過兩種管道登入。
<script>
//這是給 app 使用的 Postback function
function Postback() {
var model = @Html.Raw(Model);
return model;
}
//判斷是透過 web 或 app 連線
if (window.opener) {
//如果是 web 則使用 postMessage 回傳資料去 UOF X
var model = @Html.Raw(Model);
window.opener.postMessage(model, '*');
window.close();
}else{
//如果是 app 則使用約定的 Postback function 回傳資料
Postback();
}
</script>
資料傳遞
@Html.Raw(Model)
是 ASP.NET MVC 的 Razor 語法,用於將伺服器端的 Model 物件轉換為原始的 HTML 字串,並插入到 JavaScript 變數 model 中。此作法確保資訊能正確被解析,並避免編碼問題。
四、製作 Callback API¶
在流程中最後會透過 Callback API 來驗證 access-token 的正確性,並取得真正的使用者資訊加密回傳,此方式有一些好處:
- 避免在 post message 暴露太多登入資訊
- 二次驗證加強安全性,避免 access-token 被偽造
- 透過加密隱藏真正要登入的使用者
Note
登入頁和 Callback API 並不一定要在同一個站台
1. Callback API 規格¶
我們需要一個支援 web-api 的站台,並提供一個 API 給 UOF X 使用
項目 | 值 | 備註 |
---|---|---|
method | GET | |
URL | 自訂 |
API 回應結果
狀態碼 | 值 | 備註 |
---|---|---|
200 | 加密字串 | 成功回應 |
所有非200 | 不限 | 失敗回應 |
凡是非狀態碼 200 的回應,UOF X 皆會以 callback 異常處理,並在 log 中紀錄狀態碼和 response body。
在範例中 (Controllers > HomeController.cs
) 有實作 API ( [HttpGet("uofx/accountkey")]
),後續將以此進行說明。
2. 驗證 access-token¶
access-token 會在呼叫 API 時,透過 query-string 傳送,因此第一步要取得 token 並驗證其正確性
API: GET https://mycallback.com.tw/uofx/accountkey?t=xxxxxx
// 從網址中接收 token 參數 (UOFX 會以 query-string 't' 傳遞過來)
if (!Request.Query.TryGetValue("t", out var accessToken))
throw new Exception("Token not found");
// 驗證 Token,並從 Token 中取得 sid
if (!TokenHelper.VirtyfyAndGetData(accessToken, out var sid))
throw new Exception("Invalid Token");
在驗證 token 的過程中,也把之前放在 token 的帳號辨識碼 (sid) 取出
3. 取得使用者資訊¶
接著藉由帳號辨識碼 (sid) 取得使用者帳號 (accountKey),accountKey 會事先在 UOF X 中使用者身上個別設定。
再來產生要回傳的 model CallbackResponseModel
,此 model 有兩個屬性:
屬性 | 類型 | 備註 |
---|---|---|
AccountKey | string |
使用者帳號 |
Timestamp | long |
當下的時間戳 |
// 根據 sid 取得帳號
var accountKey = GetAccountBySid(sid);
// 產生要回傳的 model
var result = new CallbackResponseModel()
{
AccountKey = accountKey,
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds() //務必放當下時間,會依此時間來判斷是否過期
};
時間戳
時間戳是用來確保此 api 的回應內容具有 時效性 ,避免被有心人士以同樣的內容在不同時間進行非法登入。
4. 加密並回傳¶
在加密之前,我們需要先有一個 HashKey
,HashKey
是一把亂數產生的金鑰,您可以自己產生 (長度建議要有 64 字元),
請留意 HashKey
是與 UOF X 系統約定的 專用金鑰,應避免外流或共用。
API 的回應 (response) 內容 (body) 需要先透過 AES 加密,其規格如下:
項目 | 類型 | 備註 |
---|---|---|
加密演算法 | AES(Advanced Encryption Standard) | |
填充模式 | PKCS7 | |
加密模式 | CBC(Cipher Block Chaining) | |
反饋大小 | 128 位元 |
接下來要透過 HashKey
轉換成 AES 加密所需的 Key
和 IV
,轉換方式如下:
- Key: 透過 SHA256 hash
HashKey
成為 Key - IV: 取 Key 的後 16 個 byte 作為 IV
// 使用 AES 加密 model
byte[] aeskeyBytes = HashHelper.SHA256ToBytes(_HashKey);
var aesKey = aeskeyBytes; // HashKey 的 SHA256 Hash
var aesIv = aeskeyBytes.Skip(16).ToArray(); // HashKey 的 SHA256 Hash 後 16 bytes
var encodeResult = AesHelper.EncodeData(JsonSerializer.Serialize(result), aesKey, aesIv);
return Ok(encodeResult);
五、UOF X 自訂登入驗證設定¶
公司管理員 可以新增多組自訂登入驗證,以及是否要啟用此設定;在新增驗證方式的設定頁面,你可以替該組驗證方式指定一個名稱,以區分其他驗證方式,命名可以是描述性的,例如「以 XX 系統帳密登入驗證」等。
[管理者首頁>系統管理>系統設定>帳號安全和登入>自訂登入驗證]