web-dev-qa-db-fra.com

Comment obtenir le contexte utilisateur lors d'appels Web Api?

J'ai un serveur Web appelant un ASP Web Api 2. L'authentification est gérée avec ASP Identity. Pour certains des contrôleurs que je crée, j'ai besoin de connaître l'utilisateur qui passe l'appel. Je ne veux pas créer un modèle étrange à transmettre, y compris l'identité de l'utilisateur (que je ne stocke même pas dans le client).

Tous les appels à l'API sont autorisés à l'aide d'un jeton porteur. Je pensais que le contrôleur devrait pouvoir déterminer le contexte de l'utilisateur en fonction de cela, mais je ne sais pas comment le mettre en œuvre. J'ai cherché mais je ne sais pas exactement ce que je cherche et je n'ai rien trouvé de pertinent. Je vais pour quelque chose comme ...

public async Task<IHttpActionResult> Post(ApplicationIdentity identity, WalkthroughModel data)

Mettre à jour

J'ai trouvé le dessous qui semblait très prometteur ... mais la valeur est toujours nulle! Mon contrôleur hérite de ApiController et possède un en-tête Autoriser.

var userid = User.Identity.GetUserId();

Mise à jour 2

J'ai également essayé toutes les solutions de Obtenir l'utilisateur actuel, au sein d'une action ApiController, sans transmettre l'ID utilisateur en tant que paramètre mais aucune ne fonctionne. Peu importe ce que je reçois, une identité valide et autorisée, mais avec un identifiant utilisateur nul

Mise à jour 3

Voici où je suis maintenant.

    [Authorize]
    [Route("Email")]
    public async Task<IHttpActionResult> Get()
    {
        var testa = User.Identity.GetType();
        var testb = User.Identity.GetUserId();
        var testc = User.Identity.AuthenticationType;
        var testd = User.Identity.IsAuthenticated;


        return Ok();
    }
testa = Name: ClaimsIdentity,
testb = null,
testc = Bearer,
testd = true

L'utilisateur est évidemment authentifié mais je ne parviens pas à récupérer son ID utilisateur.

Mise à jour 4

J'ai trouvé une réponse, mais je suis vraiment mécontent de ça ...

ClaimsIdentity identity = (ClaimsIdentity)User.Identity;
string username = identity.Claims.First().Value;

Cela me donne le nom d'utilisateur sans aucun appel à la base de données, mais il semble très janky et une douleur à supporter à l'avenir. J'adorerais si quelqu'un avait une meilleure réponse.

Que se passe-t-il si je dois modifier les revendications émises ultérieurement? De plus, chaque fois que j'ai réellement besoin de l'identifiant de l'utilisateur, je dois effectuer un appel à la base de données pour convertir le nom d'utilisateur en identifiant.

14
Joshua Ohana

Une approche courante consiste à créer une classe de base pour vos ApiControllers et à tirer parti de ApplicationUserManager pour récupérer les informations dont vous avez besoin. Avec cette approche, vous pouvez conserver la logique d'accès aux informations de l'utilisateur dans un emplacement unique et la réutiliser sur vos contrôleurs. 

public class BaseApiController : ApiController
{
        private ApplicationUser _member;

        public ApplicationUserManager UserManager
        {
            get { return HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>(); }
        }

        public string UserIdentityId
        {
            get
            {
                var user = UserManager.FindByName(User.Identity.Name);
                return user.Id; 
            }
        }

        public ApplicationUser UserRecord
        {
            get
            {
                if (_member != null)
                {
                    return _member ; 
                }
                _member = UserManager.FindByEmail(Thread.CurrentPrincipal.Identity.Name); 
                return _member ;
            }
            set { _member = value; }
        }
}
18
jake

J'utilise une authentification d'utilisateur personnalisée (je n'utilise pas AspIdentity car mes champs de table d'utilisateurs existants étaient très différents des propriétés d'IdentityUser) et je crée ClaimsIdentity en passant mon ID utilisateur et mon nom d'utilisateur pour valider mon jeton porteur lors d'appels d'API.

    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        User user;
        try
        {
            var scope = Autofac.Integration.Owin.OwinContextExtensions.GetAutofacLifetimeScope(context.OwinContext);
            _service = scope.Resolve<IUserService>();

            user = await _service.FindUserAsync(context.UserName);

            if (user?.HashedPassword != Helpers.CustomPasswordHasher.GetHashedPassword(context.Password, user?.Salt))
            {
                context.SetError("invalid_grant", "The user name or password is incorrect.");
                return;
            }
        }
        catch (Exception ex)
        {
            context.SetError("invalid_grant", ex.Message);
            return;
        }

        var properties = new Dictionary<string, string>()
        {
            { ClaimTypes.NameIdentifier, user.UserID.ToString() },
            { ClaimTypes.Name, context.UserName }
        };

        var identity = new ClaimsIdentity(context.Options.AuthenticationType);
        properties.ToList().ForEach(c => identity.AddClaim(new Claim(c.Key, c.Value)));

        var ticket = new AuthenticationTicket(identity, new AuthenticationProperties(properties));

        context.Validated(ticket);
        context.Request.Context.Authentication.SignIn(identity);
    }

Et comment j'utilise ClaimsIdentity pour récupérer les détails de ma table utilisateur lors de l'appel de l'utilisateur ApiController Details.

    [HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
    [Route("Details")]
    public async Task<IHttpActionResult> Details()
    {
        var user = await _service.GetAsync(RequestContext.Principal.Identity.GetUserId<int>());
        var basicDetails = Mapper.Map<User, BasicUserModel>(user);

        return Ok(basicDetails);
    }

Notez le ClaimTypes.NameIdentifier = GetUserId () et ClaimTypes.Name = GetUserName ()

1
pampi