統一身份認證是整個 IT 架構的最基本的組成部分,而賬号則是實作統一身份認證的基礎。做好賬号的規劃和設計直接決定着企業整個資訊系統建設的便利與難易程度,決定着系統能否足夠靈活和快速賦能,也決定了在數字化轉型中的投入和效率。使用者賬号是使用者身份的一種表示,傳統統一身份認證系統往往被作為外圍系統來內建各個應用系統,而不是作為核心基礎系統被其他應用系統來內建。是以傳統統一身份認證系統的建設存在衆多的問題,使設計實作複雜化、管理複雜化、內建複雜化。
每個企業可能同時會有多套系統在運作,但每個使用者的賬号在企業中僅有一套,可以适用于各個系統當中。是以,這就涉及到我們如何将一套賬号應用到各個系統中,保證賬号的權限體系。
常見方法:
1、(最簡單但最深惡痛絕的)資料複制一份導入到每一套系統中。這樣會造成維護工作量大,資料混亂,如果是多級企業,将會發生難以想象的災難。
2、在身份內建中,自定義安全程式的開發,用一套使用者身份驗證程式,內建到各個系統中。
本文将從以下三點來介紹如何編寫自定義安全提供程式,并在項目中配置引用。
- 編寫自定義安全提供程式
- 配置自定義安全提供程式
- 注意事項
編寫自定義安全提供程式
編寫一個自定義安全提供程式的步驟如下:
(1) 建立項目
使用Microsoft Visual Studio 2017(以下簡稱VS2017),建立一個新的項目,類型選為 Visual C# - .NET Standard - 類庫(.NET Standard),輸入項目名稱,如:MySecurityProvider:
(2) 添加程式包依賴
自定義安全提供程式所實作的接口是由幾個程式包定義的,為此需要添加對這幾個程式包的依賴。方法如下:
首先将下面這兩個檔案下載下傳儲存到本地硬碟,比如C:\Temp\pkg 檔案夾下:
grapecity.enterprise.identity.externalidentityprovider.1.0.2.nupkg
GrapeCity.Enterprise.Identity.SecurityProvider.1.0.3.nupkg
單擊VS2017的“工具”菜單的“NuGet包管理器”>“程式包管理器設定”:
選中“程式包源”,再單擊加号按鈕:
單擊【...】按鈕,指定“源”的路徑為nupkg檔案所在的檔案夾,如:C:\temp\pkg
單擊“确定”按鈕儲存設定。
在右側解決方案資料總管窗格中,右鍵單擊“依賴項”,點選“管理NuGet程式包”,再點選“浏覽”,選中新添加的程式包源,将會列出兩個需要依賴的程式包:
GrapeCity.Enterprise.Identity.ExternalIdentityProvider和GrapeCity.Enterprise.Identity.SecurityProvider,如下圖:
逐個選中程式包,點選“安裝”,即可添加本項目對這兩個程式包的依賴。
(3) 實作接口
自定義安全提供程式需要實作兩個接口:ISecurityProviderFactory和ISecurityProvider。
實作第一個接口的操作步驟:
添加一個新的類檔案,如MySecurityProviderFactory.cs,以實作ISecurityProviderFactory接口。
public class MySecurityProviderFactory: ISecurityProviderFactory
該接口規定了兩個屬性和一個方法:
public string Description // 本安全提供程式的描述字串。
public IEnumerable<ConfigurationItem> SupportedSettings // 本安全提供程式支援的使用者配置項。
這些使用者配置項将出現在Wyn的管理畫面中,允許系統管理者進行設定。典型的配置項是使用者資訊資料庫的連接配接字串。通過提供這種配置項目,可以避免在安全提供程式中寫死使用者資訊資料庫連接配接字串的問題。
public Task CreateAsync(IEnumerable settings) // 本安全提供程式的執行個體建立方法。
這個方法的内容幾乎是固定的,如:
public Task<ISecurityProvider> CreateAsync(IEnumerable<ConfigurationItem> settings)
{
return Task.FromResult<ISecurityProvider>(new MySecurityProvider(settings));
}
實作ISecurityProvider接口
這個ISecurityProvider接口是安全提供程式的核心,其規定的屬性和方法如下:
成員類型 | 名稱 | 說明 |
---|---|---|
屬性 | ProviderName | 傳回本安全提供程式的名稱。 |
方法 | GenerateTokenAsync | 驗證使用者名和密碼,通過時生成Wyn的通路令牌。 |
方法 | GetUserContextAsync | 傳回使用者的上下文資訊,一般是根據使用者名,從資料庫查詢得到使用者的所屬部門和其他業務資料。 |
方法 | GetUserDescriptorAsync | 傳回使用者的說明資訊,該資訊将用于門戶頁面的目前登入使用者顯示。 |
方法 | GetUserOrganizationsAsync | 傳回使用者的所屬組織結構資訊。 |
方法 | GetUserRolesAsync | 傳回使用者的角色資訊,多個角色以字元串數組的形式傳回。 |
方法 | ValidateTokenAsync | 驗證令牌的合法性。在業務系統內建中,使用Token直接通路系統時,此方法用于檢查傳入Token的正确性。 |
除了上表所列成員,還有IExternalUserDescriptor,IExternalUserContext等接口,這些接口隻是規定了實體類的屬性,使用自定義類實作這些接口即可。
下面的檔案附件是一個自定義安全提供程式的示例代碼。
MySecurityProvider.zip
此示例代碼中的解決方案(.sln)可在Visual Studio 2017中直接打開。示例代碼檔案夾\bin\debug中也包含Build産物DLL,可直接配置為Wyn的安全提供程式。示例的使用者資訊是儲存在SQL Server資料庫中的,請将本檔案包中的db\MyUsers.bak檔案恢複為SQL Server資料庫。
有關接口的詳細說明,請參考下面的接口介紹。
接口介紹
ISecurityProviderFactory接口
定義
public interface ISecurityProviderFactory
{
string ProviderName { get; }
string Description { get; }
IEnumerable<ConfigurationItem> SupportedSettings { get; }
Task<ISecurityProvider> CreateAsync(IEnumerable<ConfigurationItem> settings);
}
接口說明
屬性和方法 | 說明 |
---|---|
ProviderName | 安全提供程式的名稱,不能為空,不能和其它的安全提供程式重名。 |
Description | 安全提供程式的描述文本,可以為空。 |
SupportedSettings | 該安全提供程式加載和運作時所必須的配置項。比如安全提供程式需要通路資料庫,那麼資料庫連接配接字元串即為一個必須的配置項,必須由管理者在安全提供程式管理頁面配置好,該安全提供程式才能正常工作。可以沒有任何必須的配置項,傳回一個空清單即可。 |
CreateAsync | 建立一個安全提供程式的執行個體。參數settings即為管理者已經配置好的配置項清單,使用者可以在這裡把配置項清單通過構造函數傳入建構的安全提供程式執行個體。 |
ISecurityProvider接口
定義
public interface ISecurityProvider
{
string ProviderName { get; }
Task DisposeTokenAsync(string token);
Task<string> GenerateTokenAsync(string username, string password, object customizedParam = null);
Task<IExternalUserContext> GetUserContextAsync(string token);
Task<IExternalUserDescriptor> GetUserDescriptorAsync(string token);
Task<string[]> GetUserOrganizationsAsync(string token);
Task<string[]> GetUserRolesAsync(string token);
Task<bool> ValidateTokenAsync(string token);
}
接口說明
屬性和方法 | 說明 |
---|---|
ProviderName | 安全提供程式的名稱,不能為空,不能和其它的安全提供程式重名。 |
DesposeTokenAsync | 使給定的token失效。 |
GenerateTokenAsync | 判斷給定的使用者名和密碼是否有效,如果有效,傳回一個唯一的token;否則傳回null或空字元串。注:該token可以是任何形式,比如使用者的id,或這個使用者資訊加密後的字元串,隻要確定安全提供程式可以根據這個token正确地傳回這個使用者的相關資訊即可。 |
GetUserContextAsync | 使用給定的token擷取使用者的上下文資訊。使用者的上下文資訊包含哪些内容可以是随意的。 |
GetUserDescriptor | 使用給定的token擷取使用者的基本資訊。基本資訊包括使用者的id,使用者名和安全提供程式的名稱,都不能為空。 |
GetUserOrganizationsAsync | 使用給定的token擷取使用者所屬的部門資訊。(該接口暫時沒有使用)。 |
GetUserRolesAsync | 使用給定的token擷取使用者的角色資訊。傳回使用者所屬角色的名稱,這些角色的名稱需要跟admin portal中列出的角色名完全比對,否則會被忽略。 |
ValidateTokenAsync | 驗證給定的token是否是該安全提出程式提供的一個合法有效的token。 |
IExternalUserDescriptor接口
定義
public interface IExternalUserDescriptor
{
string ExternalUserId { get; }
string ExternalUserName { get; }
string ExternalProvider { get; }
}
接口說明
參數 | 說明 |
---|---|
ExternalUserId | 使用者的唯一辨別符。 |
ExternalUserName | 使用者名。 |
ExternalProvider | 使用者的提供者,即為安全提供程式的名稱。 |
IExternalUserContext接口
定義
public interface IExternalUserContext
{
IEnumerable<string> Keys { get; }
Task<string> GetValueAsync(string key);
Task<IEnumerable<string>> GetValuesAsync(string key);
}
接口說明
參數 | 說明 |
---|---|
Keys | 使用者上下文資訊所包含的項目。 |
GetValueAsync | 對于給定的key,擷取其對應的使用者資訊。 |
GetValuesAsync | 對于給定的key,擷取其對應的使用者資訊,适用于多值情況。 |
注意
- 在每個接口的實作函數中,必須有try-catch異常處理,在catch的異常處理部分,不要用throw語句再次抛出異常,而應傳回Task對象,例如:return Task.FromResult(null); 其中T為接口函數規定的某個類型。
- 使用者上下文的key不要用以下字元串:sub,name,auth_time,idp,userid,email。
配置自定義安全提供程式
(1) 檔案部署
将編譯得到的安全提供程式DLL檔案,複制到Wyn安裝目錄下的SecurityProviders檔案夾下,在Windows環境下,預設路徑為:
C:\Program Files\Wyn\Server\SecurityProviders
提示
如果安全提供程式還依賴其他DLL,也請一并複制到同一目錄。
(2)重新開機服務
(3) 添加使用者安全提供程式
以管理者身份登入到系統的背景管理網站,單擊“+添加使用者提供程式”。
勾選自定義的安全提供程式後儲存。
(4) 配置安全提供程式
選中剛添加的自定義安全提供程式,右邊将會顯示可配置的設定選項。具體有哪些選項是在安全提供程式的代碼中确定的。按實際配置輸入這些選項内容即可。
輸入完畢,單擊“儲存”按鈕。
(5) 重新開機服務
為使自定義安全提供程式的設定生效,需要進入任務管理器重新開機WynService服務。
此後,就可以在登入視窗輸入業務系統的使用者名和密碼來登入Wyn門戶了。
注意事項
在編寫安全程式中,我們需要注意的幾個方法
-
MySecurityProvider.cs 檔案中的 GenerateTokenAsync 方法,此方法用于第一次登入中,驗證登入資訊的方法。是以這一步需要完成的功能就是驗證使用者名密碼,案例中所給的驗證方式為從資料庫中直接擷取使用者資訊後判斷登入。這裡可以實作自定義的驗證方式。隻需要對此方法中的 Database.GetUserInfo 這個被調用的方法進行改造即可。
【連結資料庫進行驗證】
public Task<string> GenerateTokenAsync(string username, string password, object customizedParam = null)
{
string rst = null;
try
{
var userInfo = Database.GetUserInfo(username, password);
var roles = userInfo.RoleNames.Split(',');
var tokenValues = new string[roles.Length + 1];
tokenValues[0] = userInfo.UserName;
roles.CopyTo(tokenValues, 1);
var token = string.Join(Constants.TokenDelimiter, tokenValues);
token = Convert.ToBase64String(Encoding.UTF8.GetBytes(token));
rst = token;
Database.WriteLogS("GenerateTokenAsync token=", token);
return Task.FromResult(rst);
}
catch (Exception e)
{
Database.WriteLogS("GenerateTokenAsync", e.ToString());
return null;
}
}
【自定義驗證,這裡可以使用api,可以使用加密字元串等各類操作】
public Task<string> GenerateTokenAsync(string username, string password, object customizedParam = null)
{
string rst = null;
try
{
if (customizedParam==null)
{
return null;
}
Dictionary<string, string> parameters = (Dictionary<string, string>)customizedParam;
var userInfo = RSAHelper.UserDecrypt(username, parameters["key"], keyFileName);
if (userInfo == null)
{
return null;
}
var roles = userInfo.RoleNames.Split(',');
var tokenValues = new string[roles.Length + 1];
tokenValues[0] = userInfo.UserName;
roles.CopyTo(tokenValues, 1);
var token = string.Join(Constants.TokenDelimiter, tokenValues);
token = Convert.ToBase64String(Encoding.UTF8.GetBytes(token));
rst = token;
Database.WriteLogS("GenerateTokenAsync token=", token);
return Task.FromResult(rst);
}
catch (Exception e)
{
Database.WriteLogS("GenerateTokenAsync", e.ToString());
return null;
}
}
上圖方法中,我們使用了自定義參數 其中key為我們自定義的鍵值對内容
在使用時可以這樣配置:(自定義參數部分必須以 key:value 配置)
-
MySecurityProvider.cs 檔案中的 GetUserContextAsync 方法,根據方法追蹤,最終所調用的方法為 Database.cs中的GetUserInfoByName 方法,是以過程忽略,直接改造此方法即可。
注意:這裡所傳回的使用者資訊,則直接會在wyn中登入後所用到,是以這裡注意傳回結果資訊。
(圖例3)【根據使用者名擷取使用者相關資訊】
【自定義傳回資訊】
-
若在程式中引用了其他dll,則需要在放入安全自定義程式時,将對應的dll放置到指定檔案夾中。
路徑為此(預設安裝路徑,若更改安裝路徑,則自行尋找)
C:\Program Files\Wyn\Server\SecurityProviders
- 日志列印,需要設定路徑,在C槽建立log檔案夾,否則列印不到。
- 配置界面資訊設定
在目前頁面看到的連結字元被修改為 秘鑰(Base64) 這個可以在程式中直接配置,可以在 MySecurityProviderFactory.cs 檔案中直接配置 SupportedSettings 此方法内容即可。
擷取時在:MySecurityProvider.cs 自定義擷取即可。
- 設定門戶網站
-
傳回的組織,角色如何處理?
傳回資訊中,若組織,角色在系統中沒有,則無法正常通路,可以在背景管理中設定對應的組織,角色,并且給角色配置設定響應的權限。
設定組織:
傳遞的組織内容為:"/A/B" 頂級組織資訊為 "/"
角色配置:給對應的角色設定權限
- 程序調試 ctrl+alt+p,選擇 顯示使用者所有程序,選中dotnet.exe 打中斷點。點選附加。程式中選擇斷點。
接下來就是打包測試了,将程式配置好之後,就可以正常測試使用了。
登入API用戶端管理
登入api代碼示例
通過postman調用生成token ,生成對應的安全自定義程式。
請求參數中:client_id,client_secret 為 Client Management 中所生成内容. 具内容參考 登入API用戶端管理 預設資訊有:
client_id:integration
client_secret:eunGKas3Pqd6FMwx9eUpdS7xmz
後期可以自行設定修改。
請求截圖:
②代碼請求如下:
fetch("http://localhost:51980/connect/token", {
"method": "POST",
"headers": {
"Content-type": "application/x-www-form-urlencoded"
},
"body": "grant_type=password&username=admin&password=admin&client_id=integration&client_secret=eunGKas3Pqd6FMwx9eUpdS7xmz"
}).then(function(res){
res.json()
.then(function(data){
console.log(data)
});
})
至此,已經可以擷取到token了,擷取後可以直接登入通路。
http://localhost:51980/integration?token=生成的token
到這裡已經全部實作使用者身份內建自定義開發,大家如果想了解更多商業BI行業精選模闆,可以通路:
https://www.grapecity.com.cn/solutions/wyn/demo
本文是由葡萄城技術開發團隊釋出,轉載請注明出處:葡萄城官網