[.NET 基於角色安全性驗證] 之一:基礎知識
.NET 基於角色安全性驗證的核心是主體(Principal)和標識(Identity)對象,其中主體負責角色或者組的驗證,標識對象封裝有關正在驗證的用戶或實體的信息。角色安全性驗證通過生成可供當前線程使用的主體信息來支持授權,其中主體用關聯的標識進行構造。public interface IPrincipal
{
// Methods
bool IsInRole(string role);
// Properties
IIdentity Identity { get; }
}
public interface IIdentity
{
// Properties
string AuthenticationType { get; }
bool IsAuthenticated { get; }
string Name { get; }
}
{
// Methods
bool IsInRole(string role);
// Properties
IIdentity Identity { get; }
}
public interface IIdentity
{
// Properties
string AuthenticationType { get; }
bool IsAuthenticated { get; }
string Name { get; }
}
在 .NET Framework 中提供了兩組 Principal/Identity 類型,分別是基於 Windows 操作係統賬戶的 WindowsPrincipal/WindowsIdentity,以及用來進行自定義驗證的 GenericPrincipal/GenericIdentity。
主體(Principal)對象在應用程序域(AppDomain)中綁定到調用上下文(CallContext)對象,缺省情況下,應用程序域會自動創建采取默認安全策略的 GenericPrincipal/GenericIdentity對象,同時主體(Principal)對象引用從創建線程自動複製到新線程的調用上下文(CallContext)中。我們可以通過 Thread.CurrentPrincipal 獲得缺省主體(Principal)和標識(Identity)對象信息。
Console.WriteLine(Thread.CurrentPrincipal);
Console.WriteLine(Thread.CurrentPrincipal.Identity);
Console.WriteLine(Thread.CurrentPrincipal.Identity);
接下來,我們使用 WindowsPrincipal/WindowsIdentity 做一些簡單的驗證,同時為了進一步理解基於角色安全性驗證的用途。
static void Test()
{
Console.WriteLine("Test...");
}
static void Main(string[] args)
{
Test();
}
{
Console.WriteLine("Test...");
}
static void Main(string[] args)
{
Test();
}
我們修改上麵這段代碼,要求 Test() 方法必須是擁有管理員權限才能調用。
1. 我們使用 System.Security.Permissions.PrincipalPermissionAttribute 特性為 Test 方法加上角色驗證標記,要求調用用戶(Windows 操作係統登錄用戶)必須是 "Administrators" 組成員。
2. 在 Main 方法中設置線程的主體和標識對象。WindowsIdentity.GetCurrent() 用來獲取當前登錄用戶的標識對象,如果登錄用戶屬於 Adminstrators 組,則 Test() 方法正常調用,否則會觸發 SecurityException 異常(我們可以使用 WindowsIdentity.GetAnonymous() 獲取匿名用戶標識對象來觸發該異常)。
[PrincipalPermission(SecurityAction.Demand, Role = "Administrators")]
static void Test()
{
Console.WriteLine("Test...");
}
static void Main(string[] args)
{
Thread.CurrentPrincipal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
Test();
}
static void Test()
{
Console.WriteLine("Test...");
}
static void Main(string[] args)
{
Thread.CurrentPrincipal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
Test();
}
我們還可以使用其他的方法來做到這一點。
1. 使用 PrincipalPermission 對象替換 PrincipalPermissionAttribute,我們就可以使用動態權限驗證,可以將用戶名或者角色參數寫入配置文件中。
2. 使用 AppDomain.CurrentDomain.SetThreadPrincipal 設置主體對象和 Thread.CurrentPrincipal 作用相同。我們還可以直接使用 AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal) 讓係統自動使用當前登錄用戶名創建 WindowsPrincipal/WindowsIdentity。
static void Test()
{
new PrincipalPermission(null, "Administrators").Demand();
Console.WriteLine("Test...");
}
static void Main(string[] args)
{
AppDomain.CurrentDomain.SetThreadPrincipal(new WindowsPrincipal(WindowsIdentity.GetCurrent()));
Test();
}
{
new PrincipalPermission(null, "Administrators").Demand();
Console.WriteLine("Test...");
}
static void Main(string[] args)
{
AppDomain.CurrentDomain.SetThreadPrincipal(new WindowsPrincipal(WindowsIdentity.GetCurrent()));
Test();
}
或者
static void Test()
{
new PrincipalPermission(null, "Administrators").Demand();
Console.WriteLine("Test...");
}
static void Main(string[] args)
{
AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
Test();
}
{
new PrincipalPermission(null, "Administrators").Demand();
Console.WriteLine("Test...");
}
static void Main(string[] args)
{
AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
Test();
}
上麵的例子都是基於角色或者組的驗證,當然我們還可以基於用戶進行驗證。
[PrincipalPermission(SecurityAction.Demand, Name="YUHEN//q.yuhen")]
new PrincipalPermission("YUHEN//q.yuhen", null).Demand();
當然,我們還可以同時用 role 和 name。PrincipalPermission 對象比 PrincipalPermissionAttribute 更加靈活,我們可以使用它進行多個權限的並集運算,以及進行 xml 轉換等。
下麵是使用 GenericPrincipal/GenericIdentity 改寫的代碼。
//[PrincipalPermission(SecurityAction.Demand, Name="q.yuhen", Role="admins")]
static void Test()
{
new PrincipalPermission(null, "Administrators").Demand();
Console.WriteLine("Test...");
}
static void Main(string[] args)
{
// 創建自定義用戶標識對象。
GenericIdentity identity = new GenericIdentity("q.yuhen");
// 創建主體對象,並指定所擁有的角色數組。
string[] roles = new string[] { "admins" };
GenericPrincipal principal = new GenericPrincipal(identity, roles);
// 設定主體。
AppDomain.CurrentDomain.SetThreadPrincipal(principal);
Test();
}
static void Test()
{
new PrincipalPermission(null, "Administrators").Demand();
Console.WriteLine("Test...");
}
static void Main(string[] args)
{
// 創建自定義用戶標識對象。
GenericIdentity identity = new GenericIdentity("q.yuhen");
// 創建主體對象,並指定所擁有的角色數組。
string[] roles = new string[] { "admins" };
GenericPrincipal principal = new GenericPrincipal(identity, roles);
// 設定主體。
AppDomain.CurrentDomain.SetThreadPrincipal(principal);
Test();
}
除了使用上述方法外,某些時候我們並不希望觸發 SecurityException 異常,我們希望能給出另外的執行策略,那麼可以直接使用 Principal.InRole 以及 Identity.Name 了。
static void Test()
{
if (Thread.CurrentPrincipal.IsInRole("admins") && Thread.CurrentPrincipal.Identity.Name == "q.yuhen")
{
Console.WriteLine("Test...");
}
else
{
Console.WriteLine("您沒有執行權限!");
}
}
{
if (Thread.CurrentPrincipal.IsInRole("admins") && Thread.CurrentPrincipal.Identity.Name == "q.yuhen")
{
Console.WriteLine("Test...");
}
else
{
Console.WriteLine("您沒有執行權限!");
}
}
總結一下,基於角色的安全檢查有三種方法。
1. 使用命令式安全檢查。該方法主要是通過 PrincipalPermission.Demand() 方法進行。
2. 使用聲明性安全檢查。通過 PrincipalPermissionAttribute 特性指定運行所需角色和用戶名。
3. 直接訪問 Principal 對象。訪問 Thread.CurrentPrincipal 屬性獲得當前主體和標識對象,並調用其相關方法和屬性進行進一步驗證。
最後更新:2017-04-02 00:06:27