程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 服務站 - 基於WCF服務中的授權

服務站 - 基於WCF服務中的授權

編輯:關於.NET

目錄

基於角色的授權

ASP.NET 角色提供程序

自定義主 體

集中化授權邏輯

基於聲明的授權

聲明

聲明集

授權上下文t

聲明轉換

安全令牌服務

當分布式應 用程序開始采用面向服務這一主體時,安全性挑戰將變得略有不同。您會突然意 識到所調用的每個服務都將跨越安全邊界。

雖然並不絕對必要,但通常 情況下服務調用的發送方和接收方之間應該存在網絡。盡管通常情況下通信框架 會自動處理身份驗證,但您仍需要建立自己的授權策略和基礎結構。

Windows Communication Foundation (WCF) 能夠為實現服務授權提供強 大的工具。您可以選擇易於使用、基於角色的系統,或者更為強大但相對更為復 雜、基於聲明的 API。本文的其余部分將比較這兩種系統,並介紹如何使用它們 實現可靠的服務授權。

基於角色的授權

基於角色授權的基本原理 是為用戶分配一組角色。在運行時,服務代碼查詢相關列表以做出與安全相關的 決策。這些角色既可以來自 Windows 安全系統(也稱為組),或者也可以來自 一些自定義存儲,例如數據庫。

Microsoft .NET Framework 中的角色 API 基於名為 IIdentity(標識信息)和 IPrincipal(角色信息)的兩個接口 。IIdentity 派生類包含諸如用戶名、該用戶是否經過驗證以及驗證方式之類的 信息。

IPrincipal 派生類必須實現一個名為 IsInRole 的方法,您可以 傳入角色名並獲得布爾型響應。此外,主體還包含對它所封裝的標識類的引用。

.NET Framework 附帶幾個此類接口的實現。WindowsIdentity 和 WindowsPrincipal 封裝了有關 Windows 用戶的詳細信息。GenericIdentity 和 GenericPrincipal 可用於代表經過驗證的自定義用戶。

此外它還包含用 於存儲主體的位置,以便將其與應用程序中當前執行的線程(例如,WCF 操作請 求)相關聯。該存儲位置即是 System.Threading.Thread 類中的 CurrentPrincipal 線程靜態屬性。典型事件過程是應用程序的一部分填充 Thread.CurrentPrincipal,以便另一部分能夠訪問該屬性以獲取其中存儲的 IPrincipal 實現。隨後可以對該類調用 IsInRole 方法查詢當前用戶的角色列 表,以便確保調用程序已經授權能夠執行該操作。

這正是它在 WCF 中的 工作方式。根據已配置的驗證和憑據類型,WCF 可以創建相應的 IIdentity 實 現(WindowsIdentity 用於 Windows 驗證,GenericIdentity 用於大多數其他 情況)。然後,根據 WCF 服務行為 (ServiceAuthorization) 的配置創建封裝 該標識並提供角色信息的 IPrincipal 實現。然後在 Thread.CurrentPrincipal 中設置該 IPrincipalset,以便服務操作可以訪問到它。

除編程調用 IsInRole 執行基於角色的安全檢查外,還存在名為 Principal­PermissionAttribute 的屬性,可以通過它使用角色要求注釋服 務操作。PrincipalPermissionAttribute 將通過在執行服務操作之前拋出 SecurityException 通知客戶端授權失敗。WCF 將捕獲該異常,並將其轉換成返 回的“拒絕訪問”失敗消息。WCF 客戶端管道將會把該特殊失敗消息 轉換為 .NET 異常類型 SecurityAccessDeniedException。圖 1 顯示進行角色 檢查和執行錯誤處理的各種方法。

圖 1 使用錯誤處理進行角色檢查

服務

class Service : IService {
 // only 'users' role member can call this method
 [PrincipalPermission(SecurityAction.Demand, Role = 'users')]
 public string[] GetRoles(string username) {
  // only administrators can retrieve the role information for other users
  if (ServiceSecurityContext.Current.PrimaryIdentity.Name != username) {
   if (Thread.CurrentPrincipal.IsInRole ('administrators')) {
    ...
   }
    else {
    // access denied
    throw new SecurityException();
   }
  }
 }
}

客 戶端

var factory = new ChannelFactory<IService>('*');
factory.Credentials.UserName.UserName = 'bob';
factory.Credentials.UserName.Password = 'bob';
var proxy = factory.CreateChannel();
try {
Console.WriteLine('\nBob: roles for Bob --');
proxy.GetRoles('bob').ToList().ForEach(i => Console.WriteLine(i));
Console.WriteLine('\nBob: roles for Alice --');
proxy.GetRoles('alice').ToList().ForEach(i => Console.WriteLine(i));
}
catch (SecurityAccessDeniedException) {
Console.WriteLine('Access Denied\n');
}

ServiceAuthorization 服務行為控制將要與請求線程相關聯的 IPrincipal 實例的創建。默認情況下,WCF 假定使用 Windows 驗證並嘗試使用 WindowsPrincipal 填充 Thread.CurrentPrincipal。

當客戶端不是 Windows 用戶時,可以選擇從 ASP.NET 角色提供程序獲得角色,或者實現自定 義授權策略以便從自定義數據庫中獲得角色。另一種替代方法是實現自定義 IPrincipal 類型,然後完全控制整個 IsInRole 實現。

ASP.NET 角色提 供程序

WCF 可以使用 ASP.NET 角色提供程序檢索用戶角色。您可以使用 內置提供程序之一(針對 SQL Server 或 Microsoft 授權管理器),或者通過 從 System.Web.Security.RoleProvider 派生編寫自己的提供程序。

為 連接到角色提供程序,WCF 將 RoleProviderPrincipal 附加到正在執行的線程 ,該線程將所有對 IsInRole 的調用都轉發給角色提供程序的 IsUserInRole 方 法。該方法接受所要查詢的用戶名和角色,並在該用戶是指定角色的成員時返回 真,反之返回假。

圖 2 顯示示例自定義角色提供程序及所需配置條目。 WCF 將為服務中的每次角色檢查調用角色提供程序。如果需要多次檢查角色,實 現某種緩存策略以避免對角色存儲的頻繁輪詢將大有裨益。

圖 2 示例角色提供程序和配置條目

角色提供程序

class CustomRoleProvider : RoleProvider {
 public override string[] GetRolesForUser(string username) {
  if (username == 'administrator') {
   return new string[] { 'administrators', 'users' };
  }
  else {
   return new string[] { 'sales', 'marketing', 'users' };
  }
 }
  public override bool IsUserInRole(string username, string roleName)  {
  return GetRolesForUser(username).Contains(roleName);
 }
 ...
}

配置 <behaviors> <serviceBehaviors> <behavior name='security'> <serviceAuthorization principalPermissionMode='UseAspNetRoles' roleProviderName='CustomProvider' </serviceAuthorization> </behavior> </serviceBehaviors> </behaviors> <system.web> <roleManager enabled='true' defaultProvider='CustomProvider'> <providers> <add name='CustomProvider' type='Service.CustomRoleProvider, Service' /> </providers> </roleManager> </system.web>

類似緩存之類的自定義應該在角色提供程序自身中 實現,或者通過自定義 IPrincipal 實現。下面我們將進一步探究此選項。

自定義主體

另一種選項是將自定義 IPrincipal 實現提供給 WCF 。這為您提供了在每次請求的驗證階段之後隱式運行代碼的機會。為此您必須創 建自己的自定義主體,並將其返回給 WCF 管道。自定義主體將隨後在從 Thread.CurrentPrincipal 傳給服務代碼。自定義主體允許完全自定義基於角色 的安全,並公開特殊的安全邏輯供服務開發人員使用。

編寫自定義主體 非常簡單。只需簡單地實現 IPrincipal 接口並添加自定義功能。要將主體與 WCF 集成,您必須將 ServiceAuthorization 元素中的 PrincipalPermissionMode 屬性設置為 "custom" 並提供負責創建主 體並將其返回給 WCF 的授權策略。

授權策略只是實現 System.IdentityModel.Policy.IAuthorizationPolicy 接口的類。該接口的實 現必須采用名為 Evaluate 的方法,該方法將在每次請求時調用。通過該方法可 以訪問 WCF 服務安全上下文並設置自定義主體。圖 3 顯示自定義主體、授權策 略及其相應的配置條目。

圖 3 自定義主體和授權策略

主體

class CustomPrincipal : IPrincipal {
 IIdentity _identity;
 string[] _roles;
 Cache _cache = HttpRuntime.Cache;
 public CustomPrincipal (IIdentity identity) {
  _identity = identity;
 }
  // helper method for easy access (without casting)
 public static CustomPrincipal Current {
  get {
   return Thread.CurrentPrincipal as CustomPrincipal;
  }
 }
 public IIdentity Identity {
  get { return _identity; }
 }
 // return all roles (custom property)
 public string [] Roles {
  get {
   EnsureRoles();
   return _roles;
  }
 }
 // IPrincipal role check
  public bool IsInRole(string role) {
  EnsureRoles();
   return _roles.Contains(role);
 }
 // cache roles for subsequent requests
 protected virtual void EnsureRoles() {
  // caching logic omitted – see the sample download
  }
}
授權策略 class AuthorizationPolicy : IAuthorizationPolicy { // called after the authentication stage public bool Evaluate(EvaluationContext evaluationContext, ref object state) { // get the authenticated client identity from the evaluation context IIdentity client = GetClientIdentity(evaluationContext); // set the custom principal evaluationContext.Properties['Principal'] = new CustomPrincipal(client); return true; } // rest omitted } 配置 <behaviors> <serviceBehaviors> <behavior name='security'> <serviceAuthorization principalPermissionMode='Custom'> <authorizationPolicies> <add policyType='Service.AuthorizationPolicy, Service' /> </authorizationPolicies> </serviceAuthorization> </behavior> </serviceBehaviors> </behaviors>

集中化授權邏輯

到目前為止,您已了解到如何從服務操作內部訪問由 WCF 安全系統提供的角色信息。但有時,如果有檢查每次傳入請求的集中化邏輯 ,並且不用將該邏輯傳遞到所有服務操作就能做出授權決策,這一點也很有用。 深入了解服務授權管理器。

服務授權管理器是從 System.ServiceModel.ServiceAuthorizationManager 派生的類。可以重寫 CheckAccessCore 方法以便為每次請求運行自定義代碼。在 CheckAccess 中, 可以訪問到當前安全上下文及傳入消息,包括消息頭。當從 CheckAccess 返回 false 時,WCF 將創建“拒絕訪問”錯誤消息並將其發送回客戶端。 返回值為 true 時將授予對服務操作的訪問權限。

您可以找到客戶端嘗 試在 WS-Addressing action 標頭中調用的操作的唯一標識符。該值可以從傳入 CheckAccess 的操作上下文的 IncomingMessageHeader.Action 屬性中獲得。 Action 值的格式如下:

ServiceNamespace/ContractName/OperationName

例如,您可能會看到如下內容:

urn:msdnmag/ServiceContract/GetRoles

WCF 將 完整的請求消息作為 ref 參數傳入。這允許在消息到達服務操作(或因為授權 失敗而彈回)之前檢查甚至更改它。

請注意在 WCF 中消息始終只能讀取 一次。這意味著在查看消息正文之前必須先創建消息的副本。可以使用以下代碼 通過創建消息緩存完成此操作(大型或流式消息需要特別注意):

MessageBuffer buffer =
  operationContext.RequestContext.RequestMessage.CreateBufferedCopy(
 int.MaxValue);

創建消息副本後,可以使用標准 API 訪問 其內容。在圖 4 中,您可以看到一個簡單服務授權管理器實現示例,它將授權 邏輯從服務操作移動到中心位置。

圖 4 服務授權管理器

代碼

class AuthorizationManager : ServiceAuthorizationManager {
 public override bool CheckAccess (
  OperationContext operationContext, ref Message message) {
  base.CheckAccess(operationContext, ref message);
   string action = operationContext.IncomingMessageHeaders.Action;
  if (action == 'urn:msdnmag/IService/GetRoles') {
    // messags in WCF are always read-once
   // we create one copy to work with, and one copy for WCF
   MessageBuffer buffer = operationContext.RequestContext.RequestMessage.
     CreateBufferedCopy(int.MaxValue);
   message = buffer.CreateMessage();
   // get the username value using XPath
   XPathNavigator nav = buffer.CreateNavigator();
   StandardNamespaceManager nsm = new
     StandardNamespaceManager(nav.NameTable);
   nsm.AddNamespace ('msdn', 'urn:msdnmag');
   XPathNavigator node = nav.SelectSingleNode
     ('s:Envelope/s:Body/msdn:GetRoles/msdn:username', nsm);
   string parameter = node.InnerXml;
   // check authorization
   if (operationContext.ServiceSecurityContext.PrimaryIdentity.Name ==
    parameter) {
    return true;
   }
    else {
    return (GetPrincipal(operationContext).IsInRole (
     'administrators'));
   }
  }
 return true;
 }
 // rest omitted
}
  
配置
  
<serviceAuthorization
  serviceAuthorizationManagerType='Service.AuthorizationManager, Service'>

基於聲明的授權

當服務非常多且非常 復雜時,基於角色的安全性可能會變得不夠強大或靈活。而且它容易使面向服務 的企業級應用程序變得極為復雜。各種使用不同客戶端框架和憑據類型的客戶端 需要與各種不同的後端服務傳遞消息。這些後端服務又會調用其他服務。有時, 這種操作以僅授權直接調用程序的受信子系統方式完成。有時也需要將最初調用 程序的標識在整個調用鏈轉發。當外部客戶端(例如合作伙伴或客戶)需要訪問 某些內部服務時,情況將開始變得更為復雜。

很明顯在更復雜的系統中 ,IIdentity 和 IPrincipal 的功能可能不足以建模標識和授權數據。基於角色 的安全僅限用於二進制決定,並且不允許任意數據都能與主體相關聯。因此需要 一種與技術無關的格式,它必須能夠說明參與分布式系統的實體標識。

.NET Framework(從版本 3.0 開始)之所以包含新標識和訪問控制 API (能夠使用基於聲明的方法處理這些需求),這就是原因。您可以在 System.IdentityModel 程序集和命名空間中找到此新 API。另外,Microsoft 最近剛剛發布其名為“Zermatt”的新標識框架預覽版。新框架以 System.IdentityModel 中的類和概念為基礎,它使在服務和應用程序中構建基 於聲明的安全變得更加簡單。需要特別注意的是 System.IdentityModel 和 Zermatt 並沒有以任何形式綁定到 WCF,它可以用於聲明啟用任何 .NET 應用程 序。只不過這些 API 已經深入集成到 WCF 當中。

System.IdentityModel 命名空間中最重要的結構類包括 Claim、 ClaimSet、AuthorizationContext 和 AuthorizationPolicy,您需要對它們有 所理解。我們將進一步介紹其中每個類並探究它們如何集成到 WCF 安全系統當 中。

聲明是能夠與系統中實體相關聯的一條信息。實體通常是用戶,但 也可以是服務或某些資源。聲明由三部分信息組成:聲明類型、聲明內容和聲明 是否說明主體標識或主體功能。該數據結構由名為 System.IdentityModel.Claims.Claim 的類表示。此外,Claim 是 DataContract,這使其能夠方便地進行序列化(當計劃跨服務邊界傳輸聲明時, 這一點非常重要):

[DataContract(Namespace =
  'http://schemas.xmlsoap.org/ws/2005/05/identity')]
public class Claim {
 [DataMember(Name = 'ClaimType')]
  public string ClaimType;
 [DataMember(Name = 'Resource')]
 public object Resource;
  [DataMember(Name = 'Right')]
 public string Right;
}

ClaimType 是標識聲明類型的 URI。雖然您可以建立自己的 類型 URI,但 ClaimTypes 類提供幾種標准聲明類型。代表名稱的聲明可以使用 ClaimTypes.Name 中保存的 URI 值:

http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nam e

Resource 屬性包含實際聲明值。請注意 Resource 是類型對象 ,這表示您能夠將任意復雜的信息與聲明相關聯(從簡單的字符串到完整的圖形 )。但請記住:當涉及序列化時它可能會出現問題,如果對此比較擔心,則應該 堅持使用基元類型。當我們討論有關聲明集的內容後,Right 屬性的作用將變得 更加清晰。

您可以通過使用 Claim 構造函數或利用 Claim 類的靜態方 法之一(對於標准聲明類型)創建新的聲明:

Claim purchaseLimitClaim = new Claim(
  'http://www.leastprivilege.com/claims/purchaselimit',
  5000,
 Rights.PossessProperty);
Claim nameClaim = Claim.CreateNameClaim('Alice');

聲明通常不能僅憑 自身完成。它通常包含在聲明集中。ClaimSet 類中包含聲明列表以及對這些聲 明發行方的引用:

[DataContract (Namespace='http://schemas.xmlsoap.org/ws/2005/05/identity')]
public abstract class ClaimSet : IEnumerable<Claim>, IEnumerable {
 public abstract ClaimSet Issuer { get; }
  public abstract Claim this[int index] { get; }
 public static ClaimSet System { get; }
 public static ClaimSet Windows { get; }
}

發行方是分布式系統變得更加復雜時的一個重要概念。 聲明可以來自各種不同的源,例如當前服務、調用服務或安全令牌服務。發行方 與聲明集相關聯使服務能夠辨別不同的聲明來源(這也有可能影響信任決策)。

發行方還可作為聲明集描述,System.IdentityModel 附帶兩個預定義的 發行方聲明集,名為 System(對於來自系統的聲明)和 Windows(對於來自 Windows 安全子系統的聲明)。兩者都作為 ClaimSet 類的靜態屬性提供。

另一個重要的概念是聲明集的標識。聲明集通常需要一個能夠唯一標識 其描述主體的聲明。這正是 Claim 類中 Right 屬性的作用。聲明集必須包含一 個 Right 值為 Identity 的聲明(它是唯一標識符)和一些 Right 值為 PossessProperty 的聲明(說明主體的其他聲明)。

您可以通過創建 DefaultClaimSet 實例創建自己的聲明,或者當需要更多控制時從抽象類 ClaimSet 派生創建。

DefaultClaimSet myClaimSet =
  new DefaultClaimSet(ClaimSet.System, new List<Claim> {
  new Claim(ClaimTypes.Name, 'Alice', Rights.Identity),
  new Claim(purchaseLimitClaimType, 5000, Rights.PossessProperty),
 Claim.CreateMailAddressClaim(new MailAddress ('[email protected]'))
 });

System.IdentityModel 附帶兩個用於將 Windows 令牌和 X.509 證書轉換為聲明的聲明集,分別是 WindowsClaimSet 和 X509CertificateClaimSet。WCF 可以將這兩個聲明集用於 Windows 和 Certificate 客戶端憑據類型。

ClaimSet 提供兩個用於查詢聲明的處理 基元:FindClaims 和 ContainsClaim。FindClaims 返回指定聲明類型的聲明集 合,而 ContainsClaim 為您提供有關特定聲明存在性的信息。

鑒於聲明 集的本質,您通常最終還是需要編寫自己的特定領域擴展,讓它使用聲明集數據 結構完成工作。在 C# 3.0 中的 Extension 方法是使用自定義功能擴展這些類 型的簡便方法。(我已為 Claim、ClaimSet 和相關類型編寫了擴展方法庫,您 可以從 leastprivilege.com/IdentityModel 下載。)

授權上下文

在使用聲明完成一些實際工作之前,您還需要了解 IdentityModel 難題 的最後一部分內容,即授權上下文,它充當聲明集和轉換策略(稍後將對此進行 詳細介紹)的容器。WCF 通過線程靜態 ServiceSecurityContext 提供授權上下 文。您可以使用圖 5 中所示的代碼段轉儲聲明集以及與當前服務操作請求相關 聯的聲明。

圖 5 查找聲明和聲明集

public void ShowAuthorizationContext() {
  AuthorizationContext context =
    ServiceSecurityContext.Current.AuthorizationContext;
  foreach (ClaimSet set in context.ClaimSets) {
    Console.WriteLine('\nIssuer:\n');
    Console.WriteLine(set.Issuer.GetType().Name);
    foreach (Claim claim in set.Issuer) {
      ShowClaim(claim);
    }
    Console.WriteLine('\nIssued:\n');
    Console.WriteLine(set.GetType().Name);
    foreach (Claim claim in set) {
      ShowClaim(claim);
    }
  }
}
private void ShowClaim(Claim claim) {
  Console.WriteLine('{0}\n{1}\n{2}\n',
    claim.ClaimType,
    claim.Resource,
    claim.Right);
}

當把這段代碼放入 WCF 服務時,根據所 配置的客戶端憑據類型將得到不同的輸出。如果啟用 Windows 驗證,則 WCF 生 成的聲明集將包含用戶的 SID(標識聲明)、組 SID 和用戶名。對於使用客戶 端證書驗證的請求,聲明集將包含描述主體名、公鑰、指紋(標識聲明)、過期 日期等的聲明。對於使用簡單用戶名/密碼對進行驗證的用戶,將僅包含一個用 戶名標識聲明。

因此,可以看出 WCF 集成的聲明層將把特定技術的標識 信息(例如 Windows 令牌或證書)轉換成可以使用標准 API 查詢的通用數據結 構。這使得編寫支持多憑據類型的服務變得更加簡單,無需再硬編碼到任何特定 技術的 API。如果向 WCF 添加新的憑據類型,則相應的管道將負責把專用格式 轉換為聲明。

聲明轉換

服務操作通常不關心用戶的 SID 或證書 指紋,但它們需要關注特定域的標識信息,例如用戶標識符、電子郵件地址或購 買限額。聲明轉換是將特定技術的標識詳細信息轉換為特定應用程序標識詳細信 息的過程。

由於現在所有的標識信息都為通用格式,所以解析和分析聲 明信息以創建在應用程序上下文中更有意義的新聲明變得非常簡單。根據 WCF 生成的聲明集標識聲明,可以將請求映射為應用程序用戶 ID,並將相關標識和 授權信息添加到新的聲明集。服務操作將隨後方便地訪問 WCF 授權上下文檢索 感興趣的信息,如圖 6 所示:

圖 6 聲明轉換

聲明轉換在授權策略中完成。我們已經使用授 權策略創建了自定義主體,但這次的目的不同。授權策略可以參與聲明生成過程 ,並在 WCF 內部聲明生成完成後運行。這意味著您已經可以訪問與調用程序憑 據相關聯的聲明。

您可以查詢現有聲明獲得標識信息,並根據這些信息 創建建模特定域聲明的新聲明集。然後可以將其添加到聲明集列表。該列表將用 於在請求到達服務操作之前創建授權上下文。

IAuthorizationPolicy 接 口有三個必須實現的成員。Id 返回策略的唯一標識符(通常是 GUID),Issuer 返回描述該策略所建聲明的發行方的聲明集。而最重要的方法是 Evaluate。該 方法接收評估上下文,當仍處於創建過程中時它基本代表授權上下文。

您可以通過 EvaluationContext.ClaimSets 訪問所有當前生成的聲明。請勿嘗 試從 Evaluate 方法中訪問 ServiceSecurityContext,因為這將再次觸發授權 策略並最終導致無限循環。您可以通過調用 EvaluationContext.AddClaimSet 將新的聲明集添加到評估上下文,該聲明集稍後將成為授權上下文的一部分。

圖 7 顯示實現通用模式的授權策略示例。它首先檢索第一個聲明集的標 識聲明。該聲明發送到用於檢查聲明並返回應用程序用戶 ID 的映射組件。隨後 該策略使用 ID 操作數據存儲,檢索聲明集所包含的信息。該信息隨後作為新聲 明集包裝並添加到評估上下文。

圖 7 授權策略

class CustomerAuthorizationPolicy : IAuthorizationPolicy {
 Guid _id = Guid.NewGuid();
 // custom issuer claim set
 ApplicationIssuerClaimSet _issuer = new ApplicationIssuerClaimSet();
 public bool Evaluate (EvaluationContext evaluationContext,
  ref object state) {
  Claim id = evaluationContext.ClaimSets.FindIdentityClaim();
  string userId = Map(id);
  evaluationContext.AddClaimSet (this, new CustomerClaimSet(userId));
  return true;
 }
 public ClaimSet Issuer {
  get { return _issuer; }
 }
 public string Id {
  get { return 'CustomerAuthorizationPolicy: ' + _id.ToString(); }
 }
}

最後一步是將授權策略添加到 serviceAuthorization 行為配置。您可以添加多個策略,它們將按照添加的順序調用。這意味著您可以 編寫多步驟授權策略,其中的單個策略依賴於前一個策略添加的值。這非常適合 更為復雜的方案:

<serviceAuthorization>
  <authorizationPolicies>
  <add policyType='LeastPrivilege.CustomerAuthorizationPolicy, Service' />
  <add policyType='some_other_policy' />
  </authorizationPolicies>
</serviceAuthorization>

如果再次運行以前的代碼檢 查授權上下文,您將得到新的聲明集和聲明。服務操作將使用相似的代碼檢查聲 明以進行授權(請參閱圖 8)。

圖 8 檢查聲明授權

public void PlaceOrder(Order order) {
  int purchaseLimit = GetPurchaseLimit();
 if (Order.Total > purchaseLimit) {
  // do appropriate action
 }
}
private int GetPurchaseLimit() {
 AuthorizationContext context =
  ServiceSecurityContext.Current.AuthorizationContext;
 foreach (ClaimSet set in context.ClaimSets) {
  foreach (Claim claim in set.FindClaims(
    Constants.PurchaseLimitClaimType,
   Rights.PossessProperty)) {
   return int.Parse(claim.Resource.ToString());
  }
 }
 throw new Exception('Claim not found');
}

服務授權管理器也能與基於聲明的授權進行協作。WCF 將操 作上下文傳遞到 CheckAccessCore 方法中。從那裡可以到達提供對授權上下文 進行訪問的服務安全上下文。這使得您可以在一個位置集中某些授權決策。

安全令牌服務

安全令牌服務 (STS) 是允許進一步合並安全邏輯 的工具。STS 的典型任務是驗證用戶,隨後創建可以包含聲明的安全令牌。客戶 端必須首先使用 STS 進行驗證,然後將返回的令牌轉發到客戶端希望進行通信 的服務。

因為 STS 了解服務(該信息是令牌請求的一部分),所以它可 以進行集中授權並預先生成服務所需的聲明。這樣聲明轉換完全不需要在服務端 點進行,它可以通過 STS 集中完成。當系統復雜到一定程度時,它可以大幅簡 化安全基礎結構。

當多個信任域聯合時,安全令牌服務也是重要的基礎 結構組件。通過在多個令牌服務間建立信任,您可以跨越服務所能使用的信任邊 界交換安全令牌。

WCF 具備支持前述方案的自動客戶端/服務端,以及編 寫 STS 所需的所有基類。但正確實現所有 WS-* 相關規范是一項非常復雜的任 務。您應該購買商業 STS 或使用更高級別的工具包(例如,Zermatt)編寫自定 義規范。即將面世的 Microsoft Active Directory 聯合身份驗證服務版本旨在 為 WCF 提供功能完備的 STS。

請將您想詢問的問題和提出的意見發送至 [email protected]

Dominick Baier 是德國 thinktecture (www.thinktecture.com) 的安全咨詢師。此外,他還是 DevelopMentor (develop.com) 的安全和 WCF 課程負責人和開發人員安全 MVP,同時也是 《Developing More-Secure Microsoft ASP.NET 2.0 Applications》一書的作 者。您可以訪問他在 leastprivilege.com 開設的博客。

Christian Weyer 是 thinktecture 的共同創始人之一,也是一位首席架構師,曾用 Java 、COM、DCOM、COM+、Web Services、WCF 和其他技術進行分布式應用程序的建 模和實現。如果要與他聯系,請訪問 www.thinktecture.com/staff/christian 。

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved