Como: criar gerenciador de autorização personalizado para um serviço

A infraestrutura do Modelo de Identidade no Windows Communication Foundation (WCF) dá suporte a um modelo extensível de autorização baseado em declaração. As declarações são extraídas de tokens e, opcionalmente, processadas por políticas de autorização personalizadas e, em seguida, colocadas em um AuthorizationContext. Um gerente de autorização examina as declarações na AuthorizationContext para tomar decisões de autorização.

Por padrão, as decisões de autorização são tomadas pela classe ServiceAuthorizationManager; no entanto, essas decisões podem ser substituídas criando um gerenciador de autorização personalizado. Para criar um gerenciador de autorização personalizado, crie uma classe derivada de ServiceAuthorizationManager e implemente o método CheckAccessCore. As decisões de autorização são tomadas no método CheckAccessCore, que retorna true quando o acesso é concedido e false quando o acesso é negado.

Se a decisão de autorização depender do conteúdo do corpo da mensagem, use o método CheckAccess.

Devido a problemas de desempenho, se possível, você deverá reprojetar seu aplicativo para que a decisão de autorização não exija acesso ao corpo da mensagem.

O registro do gerenciador de autorização personalizado para um serviço pode ser feito em código ou na configuração.

Para criar um gerenciador de autorização personalizado

  1. Derivar uma classe da classe ServiceAuthorizationManager.

    public class MyServiceAuthorizationManager : ServiceAuthorizationManager
    {
    
    
    Public Class MyServiceAuthorizationManager
        Inherits ServiceAuthorizationManager
    
    
  2. Substitua o método CheckAccessCore(OperationContext).

    Use o OperationContext que é passado para o método CheckAccessCore(OperationContext) para tomar decisões de autorização.

    O exemplo de código a seguir usa o método FindClaims(String, String) para localizar a declaração personalizada http://www.contoso.com/claims/allowedoperation para tomar uma decisão de autorização.

    protected override bool CheckAccessCore(OperationContext operationContext)
    {
      // Extract the action URI from the OperationContext. Match this against the claims
      // in the AuthorizationContext.
      string action = operationContext.RequestContext.RequestMessage.Headers.Action;
    
      // Iterate through the various claim sets in the AuthorizationContext.
      foreach(ClaimSet cs in operationContext.ServiceSecurityContext.AuthorizationContext.ClaimSets)
      {
        // Examine only those claim sets issued by System.
        if (cs.Issuer == ClaimSet.System)
        {
          // Iterate through claims of type "http://www.contoso.com/claims/allowedoperation".
            foreach (Claim c in cs.FindClaims("http://www.contoso.com/claims/allowedoperation", Rights.PossessProperty))
          {
            // If the Claim resource matches the action URI then return true to allow access.
            if (action == c.Resource.ToString())
              return true;
          }
        }
      }
    
      // If this point is reached, return false to deny access.
      return false;
    }
    
    Protected Overrides Function CheckAccessCore(ByVal operationContext As OperationContext) As Boolean
        ' Extract the action URI from the OperationContext. Match this against the claims.
        ' in the AuthorizationContext.
        Dim action As String = operationContext.RequestContext.RequestMessage.Headers.Action
    
        ' Iterate through the various claimsets in the AuthorizationContext.
        Dim cs As ClaimSet
        For Each cs In operationContext.ServiceSecurityContext.AuthorizationContext.ClaimSets
            ' Examine only those claim sets issued by System.
            If cs.Issuer Is ClaimSet.System Then
                ' Iterate through claims of type "http://www.contoso.com/claims/allowedoperation".
                Dim c As Claim
                For Each c In cs.FindClaims("http://www.contoso.com/claims/allowedoperation", _
                     Rights.PossessProperty)
                    ' If the Claim resource matches the action URI then return true to allow access.
                    If action = c.Resource.ToString() Then
                        Return True
                    End If
                Next c
            End If
        Next cs
        ' If this point is reached, return false to deny access.
        Return False
    
    End Function
    

Para registrar um gerenciador de autorização personalizado usando código

  1. Crie uma instância do gerenciador de autorização personalizado e atribua-a à propriedade ServiceAuthorizationManager.

    A ServiceAuthorizationBehavior pode ser acessada usando a propriedade Authorization.

    O exemplo de código a seguir registra o gerenciador de autorização personalizado MyServiceAuthorizationManager.

    // Add a custom authorization manager to the service authorization behavior.
    serviceHost.Authorization.ServiceAuthorizationManager =
               new MyServiceAuthorizationManager();
    
    ' Add a custom authorization manager to the service authorization behavior.
    serviceHost.Authorization.ServiceAuthorizationManager = _
        New MyServiceAuthorizationManager()
    
    

Para registrar um gerenciador de autorização personalizado usando configuração

  1. Abra o arquivo de configuração para o serviço.

  2. Adicione um <serviceAuthorization> aos <comportamentos>.

    Para o <serviceAuthorization>, adicione um atributo serviceAuthorizationManagerType e defina seu valor como o tipo que representa o gerenciador de autorização personalizado.

  3. Adicione uma associação que proteja a comunicação entre o cliente e o serviço.

    A associação escolhida para essa comunicação determina as declarações que são adicionadas ao AuthorizationContext, que o gerenciador de autorização personalizado usa para tomar decisões de autorização. Para obter mais detalhes sobre as associações fornecidas pelo sistema, consulte Associações fornecidas pelo sistema.

  4. Associe o comportamento a um ponto de extremidade de serviço adicionando um elemento de <serviço> e defina o valor do atributo behaviorConfiguration ao valor do atributo de nome para o elemento de <comportamento>.

    Para obter mais informações sobre como configurar um ponto de extremidade de serviço, consulte Como criar um ponto de extremidade de serviço na configuração.

    O exemplo de código a seguir registra o gerenciador de autorização personalizado Samples.MyServiceAuthorizationManager.

    <configuration>
      <system.serviceModel>
        <services>
          <service
              name="Microsoft.ServiceModel.Samples.CalculatorService"
              behaviorConfiguration="CalculatorServiceBehavior">
            <host>
              <baseAddresses>
                <add baseAddress="http://localhost:8000/ServiceModelSamples/service"/>
              </baseAddresses>
            </host>
            <endpoint address=""
                      binding="wsHttpBinding_Calculator"
                      contract="Microsoft.ServiceModel.Samples.ICalculator" />
          </service>
        </services>
        <bindings>
          <WSHttpBinding>
           <binding name = "wsHttpBinding_Calculator">
             <security mode="Message">
               <message clientCredentialType="Windows"/>
             </security>
            </binding>
          </WSHttpBinding>
        </bindings>
        <behaviors>
          <serviceBehaviors>
            <behavior name="CalculatorServiceBehavior">
              <serviceAuthorization serviceAuthorizationManagerType="Samples.MyServiceAuthorizationManager,MyAssembly" />
             </behavior>
         </serviceBehaviors>
       </behaviors>
      </system.serviceModel>
    </configuration>
    

    Aviso

    Observe que quando você especifica o serviceAuthorizationManagerType, a cadeia de caracteres deve conter o nome de tipo totalmente qualificado. uma vírgula e o nome do assembly no qual o tipo é definido. Se você deixar de fora o nome do assembly, o WCF tentará carregar o tipo de System.ServiceModel.dll.

Exemplo

O exemplo de código a seguir demonstra uma implementação básica de uma classe ServiceAuthorizationManager que inclui substituir o método CheckAccessCore. O código de exemplo examina a AuthorizationContext para uma declaração personalizada e retorna true quando o recurso dessa declaração personalizada corresponde ao valor da ação do OperationContext. Para obter uma implementação mais completa de uma classe ServiceAuthorizationManager, consulte a Política de Autorização.

public class MyServiceAuthorizationManager : ServiceAuthorizationManager
{
  protected override bool CheckAccessCore(OperationContext operationContext)
  {
    // Extract the action URI from the OperationContext. Match this against the claims
    // in the AuthorizationContext.
    string action = operationContext.RequestContext.RequestMessage.Headers.Action;
  
    // Iterate through the various claim sets in the AuthorizationContext.
    foreach(ClaimSet cs in operationContext.ServiceSecurityContext.AuthorizationContext.ClaimSets)
    {
      // Examine only those claim sets issued by System.
      if (cs.Issuer == ClaimSet.System)
      {
        // Iterate through claims of type "http://www.contoso.com/claims/allowedoperation".
          foreach (Claim c in cs.FindClaims("http://www.contoso.com/claims/allowedoperation", Rights.PossessProperty))
        {
          // If the Claim resource matches the action URI then return true to allow access.
          if (action == c.Resource.ToString())
            return true;
        }
      }
    }
  
    // If this point is reached, return false to deny access.
    return false;
  }
}

Public Class MyServiceAuthorizationManager
    Inherits ServiceAuthorizationManager

    Protected Overrides Function CheckAccessCore(ByVal operationContext As OperationContext) As Boolean
        ' Extract the action URI from the OperationContext. Match this against the claims.
        ' in the AuthorizationContext.
        Dim action As String = operationContext.RequestContext.RequestMessage.Headers.Action

        ' Iterate through the various claimsets in the AuthorizationContext.
        Dim cs As ClaimSet
        For Each cs In operationContext.ServiceSecurityContext.AuthorizationContext.ClaimSets
            ' Examine only those claim sets issued by System.
            If cs.Issuer Is ClaimSet.System Then
                ' Iterate through claims of type "http://www.contoso.com/claims/allowedoperation".
                Dim c As Claim
                For Each c In cs.FindClaims("http://www.contoso.com/claims/allowedoperation", _
                     Rights.PossessProperty)
                    ' If the Claim resource matches the action URI then return true to allow access.
                    If action = c.Resource.ToString() Then
                        Return True
                    End If
                Next c
            End If
        Next cs
        ' If this point is reached, return false to deny access.
        Return False

    End Function
End Class

Confira também