J'ai répondu à cette question: Comment sécuriser une API Web ASP.NET il y a 4 ans en utilisant HMAC.
Maintenant, beaucoup de choses ont changé dans la sécurité, en particulier JWT devient populaire. Ici, je vais essayer d'expliquer comment utiliser JWT de la manière la plus simple et la plus simple possible, afin que nous ne nous perdions pas de la jungle de OWIN, Oauth2, ASP.NET Identity ... :).
Si vous ne connaissez pas le jeton JWT, vous devez jeter un œil à:
https://tools.ietf.org/html/rfc7519
Fondamentalement, un jeton JWT ressemble à:
<base64-encoded header>.<base64-encoded claims>.<base64-encoded signature>
Exemple:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImN1b25nIiwibmJmIjoxNDc3NTY1NzI0LCJleHAiOjE0Nzc1NjY5MjQsImlhdCI6MTQ3NzU2NTcyNH0.6MzD1VwA5AcOcajkFyKhLYybr3h13iZjDyHm9zysDFQ
Un jeton JWT comporte trois sections:
- En-tête: format JSON codé en Base64
- Revendications: format JSON codé en Base64.
- Signature: créée et signée sur la base de l'en-tête et des revendications codées en Base64.
Si vous utilisez le site Web jwt.io avec le jeton ci-dessus, vous pouvez décoder le jeton et le voir comme ci-dessous:
Techniquement, JWT utilise une signature qui est signée à partir des en-têtes et des revendications avec un algorithme de sécurité spécifié dans les en-têtes (exemple: HMACSHA256). Par conséquent, JWT doit être transféré via HTTP si vous stockez des informations sensibles dans des revendications.
Maintenant, pour utiliser l'authentification JWT, vous n'avez pas vraiment besoin d'un middleware OWIN si vous avez un système Web Api hérité. Le concept simple est de savoir comment fournir un jeton JWT et comment valider le jeton lorsque la demande arrive. C'est tout.
Retour à la démo, pour garder le token JWT léger, je stocke uniquement username
et expiration time
dans JWT. Mais de cette façon, vous devez reconstruire une nouvelle identité locale (principal) pour ajouter plus d'informations comme: rôles .. si vous voulez faire une autorisation de rôle. Mais, si vous souhaitez ajouter plus d'informations dans JWT, c'est à vous de décider: c'est très flexible.
Au lieu d'utiliser le middleware OWIN, vous pouvez simplement fournir un point de terminaison de jeton JWT en utilisant l'action du contrôleur:
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;
}
}
Ceci est une action naïve; en production, vous devez utiliser une demande POST ou un point de terminaison d'authentification de base pour fournir le jeton JWT.
Comment générer le jeton sur la base de username
?
Vous pouvez utiliser le package NuGet appelé System.IdentityModel.Tokens.Jwt
par Microsoft pour générer le jeton, ou même un autre package 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 de terminaison pour fournir le jeton JWT est terminé. Maintenant, comment valider le JWT lorsque la demande arrive? Dans la démo que j'ai construite
JwtAuthenticationAttribute
qui hérite de IAuthenticationFilter
(plus de détails sur le filtre d'authentification ici ).
Avec cet attribut, vous pouvez authentifier n'importe quelle action: il vous suffit de mettre cet attribut sur cette action.
public class ValueController : ApiController
{
[JwtAuthentication]
public string Get()
{
return "value";
}
}
Vous pouvez également utiliser le middleware OWIN ou DelegateHander si vous souhaitez valider toutes les demandes entrantes pour votre WebAPI (non spécifique au contrôleur ou à l'action)
Voici la méthode principale 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 consiste à utiliser la bibliothèque JWT (package NuGet ci-dessus) pour valider le jeton JWT, puis revenir ClaimsPrincipal
. Vous pouvez effectuer plus de validation comme vérifier si l'utilisateur existe sur votre système et ajouter d'autres validations personnalisées si vous le souhaitez. Le code pour valider le jeton JWT 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 jeton JWT est validé et que le principal est renvoyé, vous devez créer 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 la démo:
Jeton de demande (naïf comme je l'ai mentionné ci-dessus, juste pour la démo):
GET http://localhost:{port}/api/token?username=cuong&password=1
Mettez le jeton JWT dans l'en-tête de la demande autorisée, par 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
hmac = new HMACSHA256();var key = Convert.ToBase64String(hmac.Key);
J'ai réussi à y parvenir avec un minimum d'effort (tout aussi simple qu'avec ASP.NET Core).
Pour cela, j'utilise le
Startup.cs
fichier et laMicrosoft.Owin.Security.Jwt
bibliothèque OWIN .Pour que l'application frappe,
Startup.cs
nous devons modifierWeb.config
:Voici à quoi
Startup.cs
devrait ressembler:Beaucoup d'entre vous utilisent aujourd'hui ASP.NET Core, donc comme vous pouvez le voir, cela ne diffère pas beaucoup de ce que nous avons là-bas.
Cela m'a vraiment rendu perplexe en premier, j'essayais d'implémenter des fournisseurs personnalisés, etc. Mais je ne m'attendais pas à ce que ce soit aussi simple.
OWIN
juste des rochers!Juste une chose à mentionner - après avoir activé la
NSWag
bibliothèque de démarrage OWIN a cessé de fonctionner pour moi (par exemple, certains d'entre vous voudront peut-être générer automatiquement des proxys HTTP tapuscript pour l'application Angular).La solution est également très simple - je l' ai remplacé
NSWag
parSwashbuckle
et n'a pas eu d'autres problèmes.Ok, partage maintenant le
ConfigHelper
code:Un autre aspect important - j'ai envoyé un jeton JWT via l'en- tête d' autorisation , donc le code dactylographié me ressemble comme suit:
(le code ci-dessous est généré par NSWag )
Voir la partie en-têtes -
"Authorization": "Bearer " + localStorage.getItem('token')
la source
I replaced NSWag with Swashbuckle and didn't have any further issues.
Swashbuckle a-t-il la capacité de générer des fichiers dactylographiés ou est-ce quelque chose que vous y avez ajouté vous-même?Voici une implémentation très minimale et sécurisée d'une authentification basée sur les revendications utilisant un jeton JWT dans une API Web ASP.NET Core.
tout d'abord, vous devez exposer un point de terminaison qui renvoie un jeton JWT avec des revendications attribuées à un utilisateur:
Maintenant , vous devez ajouter une authentification à vos services dans votre
ConfigureServices
intérieur de votre startup.cs d'ajouter l' authentification JWT en tant que votre service d'authentification par défaut comme ceci:vous pouvez maintenant ajouter des stratégies à vos services d'autorisation comme ceci:
ALTERNATIVEMENT , vous pouvez également (pas nécessaire) remplir toutes vos revendications à partir de votre base de données car cela ne s'exécutera qu'une seule fois au démarrage de votre application et les ajoutera à des politiques comme celle-ci:
maintenant, vous pouvez mettre le filtre de stratégie sur l'une des méthodes que vous souhaitez être autorisées comme ceci:
J'espère que cela t'aides
la source
Je pense que vous devriez utiliser un serveur tiers pour prendre en charge le jeton JWT et il n'y a pas de support JWT prêt à l'emploi dans l'API WEB 2.
Cependant, il existe un projet OWIN pour prendre en charge un certain format de jeton signé (pas JWT). Il fonctionne comme un protocole OAuth réduit pour fournir simplement une forme simple d'authentification pour un site Web.
Vous pouvez en savoir plus à ce sujet, par exemple ici .
C'est assez long, mais la plupart des parties sont des détails avec des contrôleurs et une identité ASP.NET dont vous pourriez ne pas avoir besoin du tout. Les plus importants sont
Là, vous pouvez lire comment configurer le point de terminaison (par exemple "/ token") auquel vous pouvez accéder depuis le frontend (et des détails sur le format de la demande).
D'autres étapes fournissent des détails sur la façon de connecter ce point de terminaison à la base de données, etc. et vous pouvez choisir les pièces dont vous avez besoin.
la source
Dans mon cas, le JWT est créé par une API distincte, donc ASP.NET n'a qu'à le décoder et le valider. Contrairement à la réponse acceptée, nous utilisons RSA qui est un algorithme non symétrique, donc la
SymmetricSecurityKey
classe mentionnée ci-dessus ne fonctionnera pas.Voici le résultat.
la source