如何:创建自定义授权策略

Windows Communication Foundation (WCF) 中的标识模型基础结构支持基于声明的授权模型。声明从令牌中提取,并可以选择由自定义授权策略进行处理,然后放入 AuthorizationContext 中,之后进行检查以做出授权决定。可以使用自定义策略将声明从传入令牌转换成应用程序需要的声明。这样,可以将应用程序层从 WCF 支持的不同令牌类型所提供的不同声明的详细信息中隔离出来。本主题演示如何实现自定义授权策略和如何将该策略添加到服务所使用的策略集中。

实现自定义授权策略

  1. 定义一个从 IAuthorizationPolicy 中派生的新类。

  2. 实现只读 Id 属性,方法是在该类的构造函数中生成唯一字符串并在访问此属性时返回该字符串。

  3. 通过返回表示策略颁发者的 ClaimSet,实现只读 Issuer 属性。它可能是表示应用程序的 ClaimSet,也可能是内置的 ClaimSet(例如,由静态 System 属性返回的 ClaimSet)。

  4. 按照下面过程中的说明,实现 Evaluate 方法。

实现 Evaluate 方法

  1. 需要向此方法传递两个参数:EvaluationContext 类的一个实例和一个对象引用。

  2. 如果自定义授权策略添加 ClaimSet 实例,而不考虑 EvaluationContext 的当前内容,则通过调用 AddClaimSet 方法添加每个 ClaimSet,并从 Evaluate 方法返回 true。返回 true 可向授权基础结构指示授权策略已执行其工作,不需要重新调用。

  3. 如果自定义授权策略仅在 EvaluationContext 中已经存在特定的声明时才添加声明集,则通过检查由 ClaimSet 属性返回的 ClaimSets 实例,查找这些声明。如果声明已存在,则通过调用 AddClaimSet 方法添加新声明集,如果不再需要添加其他声明集,则返回 true,向授权基础结构指示授权策略已完成其工作。如果声明不存在,则返回 false,指示如果其他授权策略向 EvaluationContext 添加更多声明集,则应该重新调用授权策略。

  4. 对于更为复杂的处理方案,可以使用 Evaluate 方法的第二个参数来存储一个状态变量,在对 Evaluate 方法的每个后续调用过程中,授权基础结构均将传回该变量以便进行特定的评估。

通过配置指定自定义授权策略

  1. serviceAuthorization 元素的 authorizationPolicies 元素的 add 元素的 policyType 属性中指定自定义授权策略的类型。

    <configuration>
     <system.serviceModel>
      <behaviors>
        <serviceAuthorization serviceAuthorizationManagerType=
                  "Samples.MyServiceAuthorizationManager" >
          <authorizationPolicies>       
            <add policyType="Samples.MyAuthorizationPolicy"
          </authorizationPolicies>
        </serviceAuthorization>
      </behaviors>
     </system.serviceModel>
    </configuration>
    

通过代码指定自定义授权策略

  1. 创建 IAuthorizationPolicy 的一个 List

  2. 创建自定义授权策略的一个实例。

  3. 将该授权策略实例添加到列表中。

  4. 对每个自定义授权策略重复步骤 2 和 3。

  5. 将列表的一个只读版本分配给 ExternalAuthorizationPolicies 属性。

    ' Add custom authorization policy to service authorization behavior.
    Dim policies As List(Of IAuthorizationPolicy) = New List(Of IAuthorizationPolicy)()
    policies.Add(New MyAuthorizationPolicy())
    serviceHost.Authorization.ExternalAuthorizationPolicies = policies.AsReadOnly()
    
    // Add a custom authorization policy to the service authorization behavior.
    List<IAuthorizationPolicy> policies = new List<IAuthorizationPolicy>();
    policies.Add(new MyAuthorizationPolicy());
    serviceHost.Authorization.ExternalAuthorizationPolicies = policies.AsReadOnly();
    

示例

下面的示例演示完整的 IAuthorizationPolicy 实现。

Public Class MyAuthorizationPolicy
    Implements IAuthorizationPolicy
    Private id_Value As String
    
    
    Public Sub New() 
        id_Value = Guid.NewGuid().ToString()
    
    End Sub 
    
    
    Public Function Evaluate(ByVal evaluationContext As EvaluationContext, ByRef state As Object) As Boolean _
        Implements IAuthorizationPolicy.Evaluate
        Dim bRet As Boolean = False
        Dim customstate As CustomAuthState = Nothing
        
        ' If the state is null, then this has not been called before, so set up
        ' our custom state.
        If state Is Nothing Then
            customstate = New CustomAuthState()
            state = customstate
        Else
            customstate = CType(state, CustomAuthState)
        End If 
        ' If claims have not been added yet...
        If Not customstate.ClaimsAdded Then
            ' Create an empty list of Claims.
            Dim claims as IList (Of Claim) = New List(Of Claim)()
            
            ' Iterate through each of the claimsets in the evaluation context.
            Dim cs As ClaimSet
            For Each cs In  evaluationContext.ClaimSets
                ' Look for Name claims in the current claimset...
                Dim c As Claim
                For Each c In  cs.FindClaims(ClaimTypes.Name, Rights.PossessProperty)
                    ' Get the list of operations that the given username is allowed to call.
                    Dim s As String
                    For Each s In  GetAllowedOpList(c.Resource.ToString())
                        ' Add claims to the list.
                        claims.Add(New Claim("http://example.org/claims/allowedoperation", s, Rights.PossessProperty))
                        Console.WriteLine("Claim added {0}", s)
                    Next s
                Next c 
            Next cs ' Add claims to the evaluation context.    
            evaluationContext.AddClaimSet(Me, New DefaultClaimSet(Me.Issuer, claims))
            
            ' Record that claims were added.
            customstate.ClaimsAdded = True
            
            ' Return true, indicating that this does not need to be called again.
            bRet = True
        Else
            ' Should never get here, but just in case...
            bRet = True
        End If
        
        
        Return bRet
    
    End Function
    
    Public ReadOnly Property Issuer() As ClaimSet Implements IAuthorizationPolicy.Issuer
        Get
            Return ClaimSet.System
        End Get
    End Property 
    
    Public ReadOnly Property Id() As String Implements IAuthorizationPolicy.Id
        Get
            Return id_Value
        End Get
    End Property 
    ' This method returns a collection of action strings thet indicate the 
    ' operations the specified username is allowed to call.
    
        ' Operations the specified username is allowed to call.
    Private Function GetAllowedOpList(ByVal userName As String) As IEnumerable(Of String)
            Dim ret As IList(Of String) = new List(Of String)()
        If username = "test1" Then
            ret.Add("http://Microsoft.ServiceModel.Samples/ICalculator/Add")
            ret.Add("http://Microsoft.ServiceModel.Samples/ICalculator/Multiply")
            ret.Add("http://Microsoft.ServiceModel.Samples/ICalculator/Subtract")
        ElseIf username = "test2" Then
            ret.Add("http://Microsoft.ServiceModel.Samples/ICalculator/Add")
            ret.Add("http://Microsoft.ServiceModel.Samples/ICalculator/Subtract")
        End If
        Return ret
     End Function
    
    ' internal class for keeping track of state
    
    Class CustomAuthState
        Private bClaimsAdded As Boolean
        
        
        Public Sub New() 
            bClaimsAdded = False
        
        End Sub 'New
        
        
        Public Property ClaimsAdded() As Boolean 
            Get
                Return bClaimsAdded
            End Get
            Set
                bClaimsAdded = value
            End Set
        End Property 
    End Class 
End Class 
public class MyAuthorizationPolicy : IAuthorizationPolicy
{
  string id;
  
  public MyAuthorizationPolicy()
  {
    id =  Guid.NewGuid().ToString();
  }

  public bool Evaluate(EvaluationContext evaluationContext, ref object state)
  {
    bool bRet = false;
    CustomAuthState customstate = null;

    // If the state is null, then this has not been called before so 
    // set up a custom state.
    if (state == null)
    {
      customstate = new CustomAuthState();
      state = customstate;
    }
    else
      customstate = (CustomAuthState)state;

    // If claims have not been added yet...
    if (!customstate.ClaimsAdded)
    {
      // Create an empty list of claims.
      IList<Claim> claims = new List<Claim>();

      // Iterate through each of the claim sets in the evaluation context.
      foreach (ClaimSet cs in evaluationContext.ClaimSets)
        // Look for Name claims in the current claimset.
        foreach (Claim c in cs.FindClaims(ClaimTypes.Name, Rights.PossessProperty))
          // Get the list of operations the given username is allowed to call.
          foreach (string s in GetAllowedOpList(c.Resource.ToString()))
          {
            // Add claims to the list.
            claims.Add(new Claim("http://example.org/claims/allowedoperation", s, Rights.PossessProperty));
            Console.WriteLine("Claim added {0}", s);
          }
      
      // Add claims to the evaluation context.
      evaluationContext.AddClaimSet(this, new DefaultClaimSet(this.Issuer,claims));
      
      // Record that claims were added.
      customstate.ClaimsAdded = true;
      
      // Return true, indicating that this method does not need to be called again.
      bRet = true;
    }
    else
    {
      // Should never get here, but just in case, return true.
      bRet = true;
    }
    
    
    return bRet;
  }

  public ClaimSet Issuer
  {
    get { return ClaimSet.System; }
  }
  
  public string Id
  {
    get { return id; }
  }

  // This method returns a collection of action strings that indicate the 
  // operations the specified username is allowed to call.
  private IEnumerable<string> GetAllowedOpList(string username)
  {
    IList<string> ret = new List<string>();
    
    if (username == "test1")
    {
      ret.Add ( "http://Microsoft.ServiceModel.Samples/ICalculator/Add");
      ret.Add ("http://Microsoft.ServiceModel.Samples/ICalculator/Multiply");
      ret.Add("http://Microsoft.ServiceModel.Samples/ICalculator/Subtract");
    }
    else if (username == "test2")
    {
      ret.Add ( "http://Microsoft.ServiceModel.Samples/ICalculator/Add");
      ret.Add ("http://Microsoft.ServiceModel.Samples/ICalculator/Subtract");
    }
    return ret;
  }

  // Internal class for keeping track of state.
  class CustomAuthState
  {
    bool bClaimsAdded;
    
    public CustomAuthState()
    {
      bClaimsAdded = false;
    }
    
    public bool ClaimsAdded { get { return bClaimsAdded; } 
    set {  bClaimsAdded = value; } }
  }
}

另请参见

任务

如何:比较声明
如何:为服务创建自定义授权管理器
授权策略

参考

ServiceAuthorizationManager