À partir de la fabuleuse réponse de Matt Dekrey , j'ai créé un exemple pleinement fonctionnel d'authentification basée sur des jetons, fonctionnant avec ASP.NET Core (1.0.1). Vous pouvez trouver le code complet dans ce référentiel sur GitHub (branches alternatives pour 1.0.0-rc1 , beta8 , beta7 ), mais en bref, les étapes importantes sont:
Générez une clé pour votre application
Dans mon exemple, je génère une clé aléatoire à chaque fois que l'application démarre, vous devrez en générer une et la stocker quelque part et la fournir à votre application. Consultez ce fichier pour savoir comment je génère une clé aléatoire et comment vous pouvez l'importer à partir d'un fichier .json . Comme suggéré dans les commentaires de @kspearrin, l' API de protection des données semble être un candidat idéal pour gérer les clés "correctement", mais je n'ai pas encore déterminé si c'est possible. Veuillez soumettre une demande d'extraction si vous y parvenez!
Startup.cs - ConfigureServices
Ici, nous devons charger une clé privée avec laquelle nos jetons doivent être signés, que nous utiliserons également pour vérifier les jetons tels qu'ils sont présentés. Nous stockons la clé dans une variable de niveau classe key
que nous réutiliserons dans la méthode Configure ci-dessous. TokenAuthOptions est une classe simple qui contient l'identité de signature, l'audience et l'émetteur dont nous aurons besoin dans le TokenController pour créer nos clés.
// Replace this with some sort of loading from config / file.
RSAParameters keyParams = RSAKeyUtils.GetRandomKey();
// Create the key, and a set of token options to record signing credentials
// using that key, along with the other parameters we will need in the
// token controlller.
key = new RsaSecurityKey(keyParams);
tokenOptions = new TokenAuthOptions()
{
Audience = TokenAudience,
Issuer = TokenIssuer,
SigningCredentials = new SigningCredentials(key, SecurityAlgorithms.Sha256Digest)
};
// Save the token options into an instance so they're accessible to the
// controller.
services.AddSingleton<TokenAuthOptions>(tokenOptions);
// Enable the use of an [Authorize("Bearer")] attribute on methods and
// classes to protect.
services.AddAuthorization(auth =>
{
auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser().Build());
});
Nous avons également mis en place une politique d'autorisation pour nous permettre d'utiliser [Authorize("Bearer")]
sur les points de terminaison et les classes que nous souhaitons protéger.
Startup.cs - Configurer
Ici, nous devons configurer le JwtBearerAuthentication:
app.UseJwtBearerAuthentication(new JwtBearerOptions {
TokenValidationParameters = new TokenValidationParameters {
IssuerSigningKey = key,
ValidAudience = tokenOptions.Audience,
ValidIssuer = tokenOptions.Issuer,
// When receiving a token, check that it is still valid.
ValidateLifetime = true,
// This defines the maximum allowable clock skew - i.e.
// provides a tolerance on the token expiry time
// when validating the lifetime. As we're creating the tokens
// locally and validating them on the same machines which
// should have synchronised time, this can be set to zero.
// Where external tokens are used, some leeway here could be
// useful.
ClockSkew = TimeSpan.FromMinutes(0)
}
});
TokenController
Dans le contrôleur de jetons, vous devez disposer d'une méthode pour générer des clés signées à l'aide de la clé chargée dans Startup.cs. Nous avons enregistré une instance TokenAuthOptions dans Startup, nous devons donc l'injecter dans le constructeur de TokenController:
[Route("api/[controller]")]
public class TokenController : Controller
{
private readonly TokenAuthOptions tokenOptions;
public TokenController(TokenAuthOptions tokenOptions)
{
this.tokenOptions = tokenOptions;
}
...
Ensuite, vous devrez générer le jeton dans votre gestionnaire pour le point de terminaison de connexion, dans mon exemple, je prends un nom d'utilisateur et un mot de passe et je valide ceux à l'aide d'une instruction if, mais la chose clé que vous devez faire est de créer ou de charger une revendication -based identité et générer le jeton pour cela:
public class AuthRequest
{
public string username { get; set; }
public string password { get; set; }
}
/// <summary>
/// Request a new token for a given username/password pair.
/// </summary>
/// <param name="req"></param>
/// <returns></returns>
[HttpPost]
public dynamic Post([FromBody] AuthRequest req)
{
// Obviously, at this point you need to validate the username and password against whatever system you wish.
if ((req.username == "TEST" && req.password == "TEST") || (req.username == "TEST2" && req.password == "TEST"))
{
DateTime? expires = DateTime.UtcNow.AddMinutes(2);
var token = GetToken(req.username, expires);
return new { authenticated = true, entityId = 1, token = token, tokenExpires = expires };
}
return new { authenticated = false };
}
private string GetToken(string user, DateTime? expires)
{
var handler = new JwtSecurityTokenHandler();
// Here, you should create or look up an identity for the user which is being authenticated.
// For now, just creating a simple generic identity.
ClaimsIdentity identity = new ClaimsIdentity(new GenericIdentity(user, "TokenAuth"), new[] { new Claim("EntityID", "1", ClaimValueTypes.Integer) });
var securityToken = handler.CreateToken(new Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor() {
Issuer = tokenOptions.Issuer,
Audience = tokenOptions.Audience,
SigningCredentials = tokenOptions.SigningCredentials,
Subject = identity,
Expires = expires
});
return handler.WriteToken(securityToken);
}
Et ça devrait être ça. Ajoutez simplement [Authorize("Bearer")]
à n'importe quelle méthode ou classe que vous souhaitez protéger, et vous devriez obtenir une erreur si vous essayez d'y accéder sans jeton présent. Si vous souhaitez renvoyer une erreur 401 au lieu d'une erreur 500, vous devrez enregistrer un gestionnaire d'exceptions personnalisé comme je l'ai dans mon exemple ici .