web-dev-qa-db-fra.com

Authentification d'API Web ASP.NET MVC 4 avec fournisseur d'appartenance

J'ai un projet ASP.NET MVC 4 utilisant l'API Web. Sur le contrôleur, j'ai défini la classe pour exiger une autorisation à l'aide de l'attribut [Autoriser]. Pour l'authentification, j'utilise le fournisseur d'appartenance ASP.NET et mon jeu Web.Config est configuré pour utiliser l'authentification "Formulaires". Voici où je suis coincé:

Tout fonctionne très bien jusqu'au point où j'ai fini de tester l'API et je veux sécuriser le contrôleur avec l'attribut [Autoriser] afin que je puisse commencer à tester l'authentification contre les utilisateurs de mon fournisseur d'adhésion. Donc, je lance Fiddler et fais le même appel en ajoutant l'attribut Authorization: Basic avec un nom d'utilisateur: mot de passe de mon fournisseur d'adhésion comme ceci:

enter image description here

La réponse que j'obtiens est 401 non autorisée et sous "Auth" j'obtiens "Aucun en-tête d'authentification WWW n'est présent." Ensuite, je me rends compte que l'API recherche une clé codée SHA1. Je lance donc un générateur SHA1 à partir d'une recherche et j'obtiens un hachage pour mon nom d'utilisateur: mot de passe et met à jour mon en-tête de demande comme suit:

enter image description here

Cela ne fonctionne pas non plus et j'obtiens les mêmes résultats. De plus, j'ai évidemment besoin d'une sorte de "clé secrète partagée" à utiliser avec le serveur pour décoder mon nom d'utilisateur/mot de passe.

Alors mes questions:

  1. Comment puis-je obtenir cette clé du serveur (ou dans ce cas, Virtual IIS fonctionnant avec VS 2012).
  2. Comment puis-je utiliser cela pour effectuer des appels authentifiés dans Fiddler en utilisant des noms d'utilisateur/mots de passe d'un fournisseur d'appartenance ASP.NET.
  3. Comment vais-je l'utiliser dans mon application client pour effectuer les mêmes appels (application C # WPF).
  4. Est-ce préférable lorsque combiné avec SSL sur mes appels HTTP? Si ce n'est pas le cas?

Merci d'avance!

43
INNVTV

Vous pouvez utiliser authentification de base avec SSL. Côté serveur, nous pourrions écrire un gestionnaire de délégation personnalisé qui vérifiera les informations d'identification en interrogeant le fournisseur de membres que nous avons enregistré et, s'il est valide, récupère les rôles et définit le principal actuel:

public class BasicAuthenticationMessageHandler : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var authHeader = request.Headers.Authorization;

        if (authHeader == null)
        {
            return base.SendAsync(request, cancellationToken);
        }

        if (authHeader.Scheme != "Basic")
        {
            return base.SendAsync(request, cancellationToken);
        }

        var encodedUserPass = authHeader.Parameter.Trim();
        var userPass = Encoding.ASCII.GetString(Convert.FromBase64String(encodedUserPass));
        var parts = userPass.Split(":".ToCharArray());
        var username = parts[0];
        var password = parts[1];

        if (!Membership.ValidateUser(username, password))
        {
            return base.SendAsync(request, cancellationToken);
        }

        var identity = new GenericIdentity(username, "Basic");
        string[] roles = Roles.Provider.GetRolesForUser(username);
        var principal = new GenericPrincipal(identity, roles);
        Thread.CurrentPrincipal = principal;
        if (HttpContext.Current != null)
        {
            HttpContext.Current.User = principal;
        }

        return base.SendAsync(request, cancellationToken);
    }
}

Nous enregistrons ensuite ce gestionnaire dans Application_Start:

GlobalConfiguration.Configuration.MessageHandlers.Add(
    new BasicAuthenticationMessageHandler()
);

Maintenant, nous pourrions avoir un contrôleur Api qui sera décoré avec l'attribut [Autoriser] pour garantir que seuls les utilisateurs authentifiés peuvent accéder à ses actions:

[Authorize]
public class ValuesController : ApiController
{
    public string Get()
    {
        return string.Format("Hello {0}", User.Identity.Name);
    }
}

Bon, regardons maintenant un exemple de client:

using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;

class Program
{
    static void Main()
    {
        // since for testing purposes I am using IIS Express
        // with an invalid SSL certificate I need to desactivate
        // the check for this certificate.
        ServicePointManager.ServerCertificateValidationCallback += 
            (sender, certificate, chain, sslPolicyErrors) => true;

        using (var client = new HttpClient())
        {
            var buffer = Encoding.ASCII.GetBytes("john:secret");
            var authHeader = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(buffer));
            client.DefaultRequestHeaders.Authorization = authHeader;
            var task = client.GetAsync("https://localhost:44300/api/values");
            if (task.Result.StatusCode == HttpStatusCode.Unauthorized)
            {
                Console.WriteLine("wrong credentials");
            }
            else
            {
                task.Result.EnsureSuccessStatusCode();
                Console.WriteLine(task.Result.Content.ReadAsAsync<string>().Result);
            }
        }
    }
}
68
Darin Dimitrov