閱讀308 返回首頁    go 阿裏雲 go 技術社區[雲棲]


[.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; }
}

在 .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);

接下來,我們使用 WindowsPrincipal/WindowsIdentity 做一些簡單的驗證,同時為了進一步理解基於角色安全性驗證的用途。
static void 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();
}

我們還可以使用其他的方法來做到這一點。

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();
}

或者
static void 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();
}

除了使用上述方法外,某些時候我們並不希望觸發 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("您沒有執行權限!");
  }
}

總結一下,基於角色的安全檢查有三種方法。

1. 使用命令式安全檢查。該方法主要是通過 PrincipalPermission.Demand() 方法進行。
2. 使用聲明性安全檢查。通過 PrincipalPermissionAttribute 特性指定運行所需角色和用戶名。
3. 直接訪問 Principal 對象。訪問 Thread.CurrentPrincipal 屬性獲得當前主體和標識對象,並調用其相關方法和屬性進行進一步驗證。
 

最後更新:2017-04-02 00:06:27

  上一篇:go [.NET 基於角色安全性驗證] 之三:ASP.NET Forms 身份驗證
  下一篇:go Hibernate編寫通用數據庫操作代碼