Authentification JWT pour Asp.Net Api Web

j'essaie de prendre en charge le JWT porteur token (JSON Web Token) dans mon application d'api web et je me perds.

je vois le soutien de .net core et pour OWIN des applications.

J'héberge actuellement mon application sur le IIS .

comment réaliser ce module d'authentification dans mon application? Y a-t-il un moyen d'utiliser la configuration <authentication> similaire à la façon dont j'utilise lewindows de form? l'authentification?

142
demandé sur Cuong Le 2016-10-27 12:33:29

3 réponses

j'ai répondu à cette question: ASP.NET API Web il y a 4 ans en utilisant HMAC.

maintenant, beaucoup de choses ont changé dans la sécurité, esp JWT devient populaire. Ici, je vais essayer d'expliquer comment utiliser JWT de la manière la plus simple et basique que je peux, afin que nous ne nous perdions pas de la jungle D'OWIN, Oauth2, ASP.NET identité... :).

si vous ne connaissez pas JWT token, vous devez jeter un oeil un peu à:

https://tools.ietf.org/html/rfc7519

en gros, un JWT ressemble à:

<base64-encoded header>.<base64-encoded claims>.<base64-encoded signature>

exemple:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXvcj9.eyJ1bmlxdWVfbmFtZSI6ImN1b25nIiwibmjmijoxndc3nty1nzi0lcjlehaioje0nzc1njy5mjqsimlhdci6mtq3nzu2ntcynh0.6MzD1VwA5AcOcajkFyKhLYybr3h13iZjdyhm9zysdfq

JWT token a trois sections:

  1. en-tête: format JSON qui est encodé comme une base64
  2. revendications: format JSON qui est encodé comme une base64.
  3. Signature: créée et signée sur la base d'un en-tête et de revendications codés comme une base64.

If you use the website jwt.io avec token ci-dessus, vous pouvez décoder et voir le token comme ci-dessous:

enter image description here

techniquement, JWT utilise la signature qui est signée par les en-têtes et les revendications avec l'algorithme de sécurité spécifié dans les en-têtes (exemple: HMACSHA256). Par conséquent, JWT doit être transféré sur HTTPs si vous stockez des informations sensibles dans les réclamations.

maintenant, pour utiliser l'authentification JWT, vous n'avez pas vraiment besoin D'un middleware OWIN si vous avez un ancien système D'Api Web. Le concept simple est de savoir comment fournir JWT token et comment valider token lorsque la requête arrive. C'est tout.

retour à la démo, pour garder JWT token léger, Je ne stocke que username et expiration time en JWT. Mais de cette façon, vous devez reconstruire une nouvelle identité locale (principal) pour ajouter plus d'informations comme: les rôles.. si vous voulez faire l'autorisation de rôle. Mais, si vous souhaitez ajouter davantage d'informations, JWT, c'est à vous, très souple.

au lieu d'utiliser OWIN middleware, vous pouvez simplement fournir JWT token endpoint en utilisant l'action de controller:

public class TokenController : ApiController
{
    // This is naive endpoint for demo, it should use Basic authentication to provide token or POST request
    [AllowAnonymous]
    public string Get(string username, string password)
    {
        if (CheckUser(username, password))
        {
            return JwtManager.GenerateToken(username);
        }

        throw new HttpResponseException(HttpStatusCode.Unauthorized);
    }

    public bool CheckUser(string username, string password)
    {
        // should check in the database
        return true;
    }
}

il s'agit d'une action naïve, en production vous devez utiliser le terminal POST request ou Basic Authentication pour fournir le JWT token.

comment générer le token basé sur username ?

vous pouvez utiliser le paquet NuGet appelé System.IdentityModel.Tokens.Jwt de MS pour générer le token, ou même un autre paquet si vous le souhaitez. Dans la démo, j'utilise HMACSHA256 avec SymmetricKey :

    /// <summary>
    /// Use the below code to generate symmetric Secret Key
    ///     var hmac = new HMACSHA256();
    ///     var key = Convert.ToBase64String(hmac.Key);
    /// </summary>
    private const string Secret = "db3OIsj+BXE9NZDy0t8W3TcNekrF+2d/1sFnWG4HnV8TZY30iTOdtVWJG8abWvB1GlOgJuQZdcF2Luqm/hccMw==";

    public static string GenerateToken(string username, int expireMinutes = 20)
    {
        var symmetricKey = Convert.FromBase64String(Secret);
        var tokenHandler = new JwtSecurityTokenHandler();

        var now = DateTime.UtcNow;
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(new[]
                    {
                        new Claim(ClaimTypes.Name, username)
                    }),

            Expires = now.AddMinutes(Convert.ToInt32(expireMinutes)),

            SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(symmetricKey), SecurityAlgorithms.HmacSha256Signature)
        };

        var stoken = tokenHandler.CreateToken(tokenDescriptor);
        var token = tokenHandler.WriteToken(stoken);

        return token;
    }

le point final pour fournir le JWT token est fait, maintenant, comment valider le JWT quand la demande vient, dans la démo que j'ai construit JwtAuthenticationAttribute qui hérite de IAuthenticationFilter , plus de détails sur le filtre d'authentification dans ici .

avec cet attribut, vous pouvez authentifier n'importe quelle action, vous mettez juste cet attribut sur cette action.

public class ValueController : ApiController
{
    [JwtAuthentication]
    public string Get()
    {
        return "value";
    }
}

You peut également utiliser OWIN middleware ou DelegateHander si vous voulez valider toutes les requêtes entrantes pour votre WebApi (pas spécifique sur le contrôleur ou l'action)

ci - dessous est la méthode de base du filtre d'authentification:

    private static bool ValidateToken(string token, out string username)
    {
        username = null;

        var simplePrinciple = JwtManager.GetPrincipal(token);
        var identity = simplePrinciple.Identity as ClaimsIdentity;

        if (identity == null)
            return false;

        if (!identity.IsAuthenticated)
            return false;

        var usernameClaim = identity.FindFirst(ClaimTypes.Name);
        username = usernameClaim?.Value;

        if (string.IsNullOrEmpty(username))
            return false;

        // More validate to check whether username exists in system

        return true;
    }

    protected Task<IPrincipal> AuthenticateJwtToken(string token)
    {
        string username;

        if (ValidateToken(token, out username))
        {
            // based on username to get more information from database in order to build local identity
            var claims = new List<Claim>
            {
                new Claim(ClaimTypes.Name, username)
                // Add more claims if needed: Roles, ...
            };

            var identity = new ClaimsIdentity(claims, "Jwt");
            IPrincipal user = new ClaimsPrincipal(identity);

            return Task.FromResult(user);
        }

        return Task.FromResult<IPrincipal>(null);
    }

le flux de travail est, en utilisant la bibliothèque JWT (paquet NuGet ci-dessus) pour valider JWT token et retourner ensuite ClaimsPrincipal . Vous pouvez effectuer plus de validation comme vérifier si l'utilisateur existe sur votre système et ajouter d'autres validations si vous voulez. Le code pour valider le JWT token et récupérer le principal:

   public static ClaimsPrincipal GetPrincipal(string token)
    {
        try
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var jwtToken = tokenHandler.ReadToken(token) as JwtSecurityToken;

            if (jwtToken == null)
                return null;

            var symmetricKey = Convert.FromBase64String(Secret);

            var validationParameters = new TokenValidationParameters()
            {
               RequireExpirationTime = true,
               ValidateIssuer = false,
               ValidateAudience = false,
               IssuerSigningKey = new SymmetricSecurityKey(symmetricKey)
            };

            SecurityToken securityToken;
            var principal = tokenHandler.ValidateToken(token, validationParameters, out securityToken);

            return principal;
        }

        catch (Exception)
        {
            //should write log
            return null;
        }
    }

si le JWT token est validé et que le principal est de retour, vous devez construire une nouvelle identité locale et y mettre plus d'informations pour vérifier l'autorisation de rôle.

N'oubliez pas d'ajouter config.Filters.Add(new AuthorizeAttribute()); (autorisation par défaut) à portée globale afin d'empêcher toute demande anonyme à vos ressources.

vous pouvez utiliser Postman pour tester le Démo:

demande token (naïve comme je l'ai mentionné ci-dessus, juste pour la démo):

GET http://localhost:{port}/api/token?username=cuong&password=1

mettre JWT token dans l'en-tête pour requête autorisée, exemple:

GET http://localhost:{port}/api/value

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImN1b25nIiwibmJmIjoxNDc3NTY1MjU4LCJleHAiOjE0Nzc1NjY0NTgsImlhdCI6MTQ3NzU2NTI1OH0.dSwwufd4-gztkLpttZsZ1255oEzpWCJkayR_4yvNL1s

la démo est mise ici: https://github.com/cuongle/WebApi.Jwt

378
répondu Cuong Le 2017-11-29 21:22:38

je pense que vous devriez utiliser un serveur 3d party pour prendre en charge le token JWT et il n'y a pas de support JWT dans L'API WEB 2.

cependant il y a un projet OWIN pour soutenir un certain format de jeton signé (pas JWT). Il fonctionne comme un protocole réduit de OAuth pour fournir juste une forme simple d'authentification pour un site web.

vous pouvez lire plus à ce sujet par exemple ici .

c'est assez long, mais la plupart des pièces sont des détails avec des contrôleurs et ASP.NET une identité dont vous n'avez peut-être pas besoin du tout. Les plus importants sont

Étape 9: Ajouter un support pour la génération de jetons au porteur

Etape 12: Tester le Back-end de l'API

là, vous pouvez lire comment configurer le point final (par exemple" /token") auquel vous pouvez accéder depuis frontend (et les détails sur le format de la requête).

autres étapes fournir des détails sur la façon de relier ce paramètre à la base de données, etc. et vous pouvez choisir les pièces que vous avez besoin.

1
répondu Ilya Chernomordik 2016-10-27 09:47:46

j'implémente aussi L'API Jason Web Token dans mon projet, vous pouvez télécharger à partir de ce lien JWT API Token . Vous pouvez utiliser [authorize] pour vérifier si un utilisateur est authentifié ou non?

1
répondu Brijesh Mavani 2018-06-13 05:46:41