50 votes

Comment définir en toute sécurité le principal de l'utilisateur dans un WebAPI HttpMessageHandler personnalisé?

Pour l'authentification de base, j'ai implémenté une coutume HttpMessageHandler basé sur l'exemple présenté dans le Darin Dimitrov la réponse ici: http://stackoverflow.com/a/11536349/270591

Le code crée une instance principal de type GenericPrincipal avec nom d'utilisateur et de rôles et jeux de ce principe à l'actuelle directrice de la discussion:

Thread.CurrentPrincipal = principal;

Plus tard, en ApiController méthode, le principal peut être lu par l'accès à l'contrôleurs User de la propriété:

public class ValuesController : ApiController
{
    public void Post(TestModel model)
    {
        var user = User; // this should be the principal set in the handler
        //...
    }
}

Tout semblait aller bien jusqu'à ce que j'ai récemment ajouté une coutume MediaTypeFormatter qui utilise l' Task bibliothèque de la sorte:

public override Task<object> ReadFromStreamAsync(Type type, Stream readStream,
    HttpContent content, IFormatterLogger formatterLogger)
{
    var task = Task.Factory.StartNew(() =>
    {
        // some formatting happens and finally a TestModel is returned,
        // simulated here by just an empty model
        return (object)new TestModel();
    });
    return task;
}

(J'ai cette approche pour commencer une tâche avec des Task.Factory.StartNew en ReadFromStreamAsync à partir d'un exemple de code. Est-il erroné et peut-être la seule raison du problème?)

Maintenant, "parfois" et pour moi, il semble être aléatoire - l' User principal dans la méthode de contrôleur n'est pas le principal plus j'ai mis dans l'MessageHandler, c'est à dire le nom d'utilisateur, Authenticated drapeau et les rôles sont tous perdus. La raison semble être que la coutume MediaTypeFormatter provoque un changement du fil entre MessageHandler et la méthode du contrôleur. J'ai confirmé cela en comparant les valeurs de Thread.CurrentThread.ManagedThreadId dans le MessageHandler et de la méthode du contrôleur. "Parfois," ils sont différents et puis le principal est "perdu".

J'ai regardé maintenant pour une alternative à la mise Thread.CurrentPrincipal quelque sorte à transférer le capital en toute sécurité à partir de la coutume MessageHandler à la méthode de contrôleur et dans ce blog propriétés de la demande sont utilisés:

request.Properties.Add(HttpPropertyKeys.UserPrincipalKey,
    new GenericPrincipal(identity, new string[0]));

J'ai voulu tester, mais il semble que l' HttpPropertyKeys de la classe (qui est dans l'espace de noms System.Web.Http.Hosting) n'a pas d' UserPrincipalKey des biens plus récents WebApi versions (release candidate et à la sortie de la version finale de la semaine dernière ainsi).

Ma question est: Comment puis-je modifier le dernier extrait de code ci-dessus afin que est travaille avec les WebAPI version? Ou plus généralement: Comment puis-je régler l'utilisateur principal personnalisé dans un MessageHandler et d'y accéder de manière fiable dans une méthode de contrôleur?

Modifier

Il est mentionné ici que "HttpPropertyKeys.UserPrincipalKey ... résout "MS_UserPrincipal"", j'ai donc essayé d'utiliser:

request.Properties.Add("MS_UserPrincipal",
    new GenericPrincipal(identity, new string[0]));

Mais il ne fonctionne pas comme je m'y attendais: L' ApiController.User propriété ne contient pas la principale ajouté à l' Properties de la collection ci-dessus.

75voto

Slauma Points 76561

Le problème de la perte de la principale sur un nouveau thread est mentionné ici:

http://leastprivilege.com/2012/06/25/important-setting-the-client-principal-in-asp-net-web-api/

Important: le Réglage de la Client Principal dans ASP.NET Web API

En raison d'un malheureux mécanismes enfoui au plus profond dans ASP.NET, réglage Fil de discussion.CurrentPrincipal dans l'API Web d'hébergement web n'est pas suffisant.

En cas d'hébergement dans ASP.NET, Fil.CurrentPrincipal pourrait obtenir substituée avec HttpContext.Actuel.Utilisateur lors de la création de nouveaux threads. Cela signifie vous devez définir les principaux sur le fil et le contexte HTTP.

Et ici: http://aspnetwebstack.codeplex.com/workitem/264

Aujourd'hui, vous aurez besoin de mettre les deux conditions suivantes pour l'utilisateur principal si vous utilisez un gestionnaire de messages personnalisés pour effectuer l'authentification dans le web hébergé scénario.

IPrincipal principal = new GenericPrincipal(
    new GenericIdentity("myuser"), new string[] { "myrole" });
Thread.CurrentPrincipal = principal;
HttpContext.Current.User = principal;

J'ai ajouté la dernière ligne, HttpContext.Current.User = principal (besoins en using System.Web;) pour le gestionnaire de messages et de l' User propriété dans l' ApiController n'ayant pas toujours le bon principal maintenant, même si le fil a changé en raison de la tâche dans le MediaTypeFormatter.

Modifier

Juste pour le mettre en valeur: Réglage du courant de l'utilisateur principal de l' HttpContext n'est nécessaire que lorsque la WebApi est hébergé dans ASP.NET/IIS. Pour l'auto-hébergement, il n'est pas nécessaire (et pas possible car HttpContext est un ASP.NET construire et n'existe pas lors de l'auto-hébergé).

5voto

gmoody1979 Points 66

En utilisant votre MessageHandler personnalisé, vous pouvez ajouter la propriété MS_UserPrincipal en appelant la méthode d’extension HttpRequestMessageExtensionMethods.SetUserPrincipal définie dans System.ServiceModel.Channels :

 protected override Task<HttpResponseMessage> SendAsync(
    HttpRequestMessage request, CancellationToken cancellationToken)
{
    var user = new GenericPrincipal(new GenericIdentity("UserID"), null);
    request.SetUserPrincipal(user);
    return base.SendAsync(request, cancellationToken);
}
 

Notez que ceci ajoute seulement cette propriété à la collection Properties de la demande, cela ne change pas l'utilisateur attaché à ApiController.

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