4 votes

Comment définir l'identité/le nom d'utilisateur dans ServiceAuthorizationManager ?

J'ai un service REST WCF 4.0 côté serveur (hébergé dans IIS) et un client Android. Le client Android envoie un jeton de sécurité crypté dans un en-tête HTTP personnalisé afin d'authentifier l'utilisateur. J'ai implémenté une fonction personnalisée ServiceAuthorizationManager qui extrait le jeton de sécurité de l'en-tête. Le jeton contient le nom d'utilisateur que je peux lire à partir du jeton :

public class MyAuthorizationManager : ServiceAuthorizationManager
{
    protected override bool CheckAccessCore(OperationContext operationContext)
    {
        var requestMessage = operationContext.RequestContext.RequestMessage;
        var requestProperty = (HttpRequestMessageProperty)requestMessage
            .Properties[HttpRequestMessageProperty.Name];
        var token = requestProperty.Headers["X-MyCustomHeader"];
        if (!string.IsNullOrEmpty(token))
        {
            var userName = GetUserNameFromToken(token);
            if (!string.IsNullOrEmpty(userName))
            {
                // How to save userName now so that I can 
                // retrieve it in the service operations?
                return true;
            }
        }
        return false;
    }
}

Mon problème est que j'ai également besoin du nom de l'utilisateur authentifié dans diverses opérations de service (principalement pour accéder aux données du profil de l'utilisateur) et j'avais prévu de le récupérer de cette manière :

public void MyServiceOperation()
{
    string userName = OperationContext.Current
        .ServiceSecurityContext.PrimaryIdentity.Name;

    // check profile store for that userName and do something depending on
    // profile settings
}

Comment puis-je définir ce nom d'utilisateur dans CheckAccessCore ?

Un procès très naïf comme celui-ci...

operationContext.ServiceSecurityContext.PrimaryIdentity.Name = userName;

...ne fonctionne pas parce que PrimaryIdentity.Name est en lecture seule. Je suppose qu'un code plus sophistiqué est nécessaire.

3voto

Slauma Points 76561

Après quelques recherches, je n'ai pas trouvé de moyen de définir l'identité dans le fichier ServiceAuthorizationManager.CheckAccessCore . Cette méthode semble être appelée trop tard dans le pipeline de traitement, lorsque l'identité de l'utilisateur est déjà définie (éventuellement sur "Anonyme"). IsAuthenticated es false )) et ne peut plus être modifié. Le site ServiceAuthorizationManager est conçu pour l'autorisation, pas pour l'authentification, c'est donc le mauvais endroit pour implémenter une authentification personnalisée.

J'ai finalement trouvé trois solutions possibles à mon problème :

  1. Comme expliqué dans l'article lié à la réponse de @TheCodeKing, l'utilisation du kit de démarrage WCF REST offre la possibilité d'écrire un code personnalisé. RequestInterceptor qui s'insère assez tôt dans le pipeline, permet d'accéder à la requête entrante et de définir l'identité de l'utilisateur sur la base, par exemple, d'en-têtes HTTP personnalisés. Malheureusement, le WCF REST Starter Kit est ancien (basé sur WCF 3.5) et son développement a apparemment été abandonné. Certaines de ses fonctionnalités ont été incorporées dans WCF 4.0, mais d'autres non, et le kit de démarrage WCF REST a été abandonné. RequestInterceptor est l'un d'entre eux. Néanmoins, j'ai utilisé cette solution maintenant et j'ai mélangé le Microsoft.ServiceModel.Web du Starter Kit dans ma solution WCF 4.0. Cela semble fonctionner jusqu'à présent après quelques tests simples.

  2. Si l'identité n'est pas vraiment nécessaire mais seulement le nom de l'utilisateur, un simple "truc"/"hack" pour écrire le nom de l'utilisateur dans un nouvel en-tête de requête fonctionne (aussi en CheckAccessCore ) :

    // ...
    var userName = GetUserNameFromToken(token);
    if (!string.IsNullOrEmpty(userName))
    {
        requestProperty.Headers["X-UserName"] = userName;
        return true;
    }
    // ...

    Et ensuite dans les méthodes de service :

    public void MyServiceOperation()
    {
        string userName = WebOperationContext.Current.IncomingRequest
            .Headers["X-UserName"];
        // ...
    }
  3. Une autre option, de plus bas niveau, consisterait à écrire un programme personnalisé HttpModule qui intercepte la demande entrante et définit l'identité. Voici un exemple de ce que cela pourrait donner aquí de l'équipe Microsoft Pattern & Practices (voir l'exemple "Code du module HTTP" au milieu de l'article).

2voto

TheCodeKing Points 11632

Jetez un coup d'œil à ce article. Il contient des exemples de mise en place de la IAuthorizationPolicy instance. En créant votre propre implémentation, vous pouvez contrôler la création de l'instance de l IPrincipal y IIdentity qui sont transmises dans le contexte. Tout est accroché à partir d'un intercepteur de service.

internal class AuthorizationPolicyFactory
{
   public virtual IAuthorizationPolicy Create(Credentials credentials)
   {
      var genericIdentity = new GenericIdentity(credentials.UserName);
      var genericPrincipal = new GenericPrincipal(genericIdentity,
                                                  new string[] { });
      return new PrincipalAuthorizationPolicy(genericPrincipal);
   }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X