JWT sur .NET Core 2.0

Je me suis vraiment lancé dans l’aventure pour que JWT travaille sur DotNet Core 2.0 (maintenant disponible en version finale aujourd’hui). Il y a une tonne de documentation, mais tout l’exemple de code semble utiliser des API obsolètes et arriver à nouveau dans Core, il est absolument étonnant de savoir comment il est censé être implémenté. J’ai essayé d’utiliser Jose, mais app. UseJwtBearerAuthentication est devenu obsolète et il n’y a aucune documentation sur ce qu’il faut faire ensuite.

Quelqu’un at-il un projet open source qui utilise dotnet core 2.0 qui peut simplement parsingr un JWT à partir de l’en-tête d’autorisation et me permettre d’autoriser les requêtes pour un jeton JWT codé HS256?

La classe ci-dessous ne jette aucune exception, mais aucune demande n’est autorisée et je ne vois aucune raison pour laquelle elles ne sont pas autorisées. Les réponses sont vides 401, donc pour moi, cela indique qu’il n’y avait pas d’exception, mais que le secret ne correspond pas.

Une chose étrange est que mes jetons sont cryptés avec l’algorithme HS256, mais je ne vois aucun indicateur pour le forcer à utiliser cet algorithme où que ce soit.

Voici la classe que j’ai jusqu’à présent:

using System; using System.Collections.Generic; using System.IO; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Net.Http.Headers; using Newtonsoft.Json.Linq; using Microsoft.IdentityModel.Tokens; using System.Text; namespace Site.Authorization { public static class SiteAuthorizationExtensions { public static IServiceCollection AddSiteAuthorization(this IServiceCollection services) { var signingKey = new SymmesortingcSecurityKey(Encoding.ASCII.GetBytes("SECRET_KEY")); var tokenValidationParameters = new TokenValidationParameters { // The signing key must match! ValidateIssuerSigningKey = true, ValidateAudience = false, ValidateIssuer = false, IssuerSigningKeys = new List{ signingKey }, // Validate the token expiry ValidateLifetime = true, }; services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(o => { o.IncludeErrorDetails = true; o.TokenValidationParameters = tokenValidationParameters; o.Events = new JwtBearerEvents() { OnAuthenticationFailed = c => { c.NoResult(); c.Response.StatusCode = 401; c.Response.ContentType = "text/plain"; return c.Response.WriteAsync(c.Exception.ToSsortingng()); } }; }); return services; } } } 

Voici un exemple minimal de travail complet avec un contrôleur. J’espère que vous pourrez le vérifier en utilisant un appel Postman ou JavaScript.

  1. appsettings.json, appsettings.Development.json. Ajouter une section Remarque: la clé doit être assez longue et l’émetteur est une adresse du service:

     ... ,"Tokens": { "Key": "Rather_very_long_key", "Issuer": "http://localhost:56268/" } ... 

    !!! Dans un projet réel, ne conservez pas le fichier appsettings.json dans Key. Il devrait être conservé dans la variable d’environnement et le prendre comme ceci:

     Environment.GetEnvironmentVariable("JWT_KEY"); 

MISE À JOUR : Voyez comment les parameters de base de .net fonctionnent, vous n’avez pas besoin de le prendre exactement dans l’environnement. Vous pouvez utiliser le réglage. Cependant, au lieu de cela, nous pouvons écrire cette variable dans les variables d’environnement en production, alors notre code préférera les variables d’environnement au lieu de la configuration.

  1. AuthRequest.cs: Pour conserver les valeurs de connexion et de mot de passe:

     public class AuthRequest { public ssortingng UserName { get; set; } public ssortingng Password { get; set; } } 
  2. Startup.cs dans la méthode Configure () AVANT app.UseMvc ():

     app.UseAuthentication(); 
  3. Startup.cs dans ConfigureServices ():

     services.AddAuthentication() .AddJwtBearer(cfg => { cfg.RequireHttpsMetadata = false; cfg.SaveToken = true; cfg.TokenValidationParameters = new TokenValidationParameters() { ValidIssuer = Configuration["Tokens:Issuer"], ValidAudience = Configuration["Tokens:Issuer"], IssuerSigningKey = new SymmesortingcSecurityKey(Encoding.UTF8.GetBytes(Configuration["Tokens:Key"])) }; }); 
  4. Ajouter un contrôleur:

      [Route("api/[controller]")] public class TokenController : Controller { private readonly IConfiguration _config; private readonly IUserManager _userManager; public TokenController(IConfiguration configuration, IUserManager userManager) { _config = configuration; _userManager = userManager; } [HttpPost("")] [AllowAnonymous] public IActionResult Login([FromBody] AuthRequest authUserRequest) { var user = _userManager.FindByEmail(model.UserName); if (user != null) { var checkPwd = _signInManager.CheckPasswordSignIn(user, model.authUserRequest); if (checkPwd) { var claims = new[] { new Claim(JwtRegisteredClaimNames.Sub, user.UserName), new Claim(JwtRegisteredClaimNames.Jti, user.Id.ToSsortingng()), }; var key = new SymmesortingcSecurityKey(Encoding.UTF8.GetBytes(_config["Tokens:Key"])); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken(_config["Tokens:Issuer"], _config["Tokens:Issuer"], claims, expires: DateTime.Now.AddMinutes(30), signingCredentials: creds); return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token) }); } } return BadRequest("Could not create token"); }} 

C’est tout! À votre santé!

MISE À JOUR: Les gens demandent comment obtenir l’utilisateur actuel. Faire:

  1. Dans Startup.cs dans ConfigureServices (), ajoutez

     services.AddSingleton(); 
  2. Dans un contrôleur append au constructeur:

     private readonly int _currentUser; public MyController(IHttpContextAccessor httpContextAccessor) { _currentUser = httpContextAccessor.CurrentUser(); } 
  3. Ajoutez quelque part une extension et utilisez-la dans votre contrôleur (en utilisant ….)

     public static class IHttpContextAccessorExtension { public static int CurrentUser(this IHttpContextAccessor httpContextAccessor) { var ssortingngId = httpContextAccessor?.HttpContext?.User?.FindFirst(JwtRegisteredClaimNames.Jti)?.Value; int.TryParse(ssortingngId ?? "0", out int userId); return userId; } } 

C’est tout.

Mes tokenValidationParameters fonctionnent quand ils ressemblent à ceci:

  var tokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = GetSignInKey(), ValidateIssuer = true, ValidIssuer = GetIssuer(), ValidateAudience = true, ValidAudience = GetAudience(), ValidateLifetime = true, ClockSkew = TimeSpan.Zero }; 

et

  static private SymmesortingcSecurityKey GetSignInKey() { const ssortingng secretKey = "very_long_very_secret_secret"; var signingKey = new SymmesortingcSecurityKey(Encoding.UTF8.GetBytes(secretKey)); return signingKey; } static private ssortingng GetIssuer() { return "issuer"; } static private ssortingng GetAudience() { return "audience"; } 

De plus, ajoutez options.RequireHttpsMetadata = false comme ceci:

  .AddJwtBearer(options => { options.TokenValidationParameters =tokenValidationParameters options.RequireHttpsMetadata = false; }); 

EDIT :

N’oubliez pas d’appeler

  app.UseAuthentication(); 

dans Startup.cs -> Méthode de configuration avant app.UseMvc ();

Voici une solution pour vous.

Dans votre startup.cs, tout d’abord, configurez-le en tant que services:

  services.AddAuthentication().AddJwtBearer(cfg => { cfg.RequireHttpsMetadata = false; cfg.SaveToken = true; cfg.TokenValidationParameters = new TokenValidationParameters() { IssuerSigningKey = "somethong", ValidAudience = "something", : }; }); 

deuxièmement, appelez ce service en configuration

  app.UseAuthentication(); 

maintenant, vous pouvez l’utiliser dans votre contrôleur en ajoutant l’atsortingbut

  [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] [HttpGet] public IActionResult GetUserInfo() { 

Pour plus de détails sur le code source qui utilise angular comme Frond-end, voir ici

Voici mon implémentation pour une API .Net Core 2.0:

  public IConfigurationRoot Configuration { get; } public void ConfigureServices(IServiceCollection services) { // Add framework services services.AddMvc( config => { // This enables the AuthorizeFilter on all endpoints var policy = new AuthorizationPolicyBuilder() .RequireAuthenticatedUser() .Build(); config.Filters.Add(new AuthorizeFilter(policy)); } ).AddJsonOptions(opt => { opt.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore; }); services.AddLogging(); services.AddAuthentication(sharedOptions => { sharedOptions.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; sharedOptions.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.Audience = Configuration["AzureAD:Audience"]; options.Authority = Configuration["AzureAD:AADInstance"] + Configuration["AzureAD:TenantId"]; }); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { app.UseAuthentication(); // THIS METHOD MUST COME BEFORE UseMvc...() !! app.UseMvcWithDefaultRoute(); } 

appsettings.json:

 { "AzureAD": { "AADInstance": "https://login.microsoftonline.com/", "Audience": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "ClientId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "Domain": "mydomain.com", "TenantId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" }, ... } 

Le code ci-dessus permet l’authentification sur tous les contrôleurs. Pour autoriser l’access anonyme, vous pouvez décorer un contrôleur entier:

 [Route("api/[controller]")] [AllowAnonymous] public class AnonymousController : Controller { ... } 

ou simplement décorer une méthode pour autoriser un seul sharepoint terminaison:

  [AllowAnonymous] [HttpPost("anonymousmethod")] public async Task MyAnonymousMethod() { ... } 

Remarques:

  • Ceci est ma première tentative d’autorisation AD – si quelque chose ne va pas, faites-le moi savoir!
  • Audience doit correspondre à l’ ID de ressource demandé par le client. Dans notre cas, notre client (une application Web Angular) a été enregistré séparément dans Azure AD, et il a utilisé son identifiant client, que nous avons enregistré en tant qu’audience dans l’API.
  • ClientId s’appelle Application ID dans le portail Azure (pourquoi?), L’ID d’application de l’enregistrement d’application pour l’API.
  • TenantId est appelé ID d’annuaire dans le portail Azure (pourquoi?), TenantId sous Azure Active Directory> Propriétés
  • Si vous déployez l’API en tant qu’application Web hébergée Azure, veillez à définir les parameters de l’application:

    par exemple. AzureAD: Audience / xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

Implémentation de l’authentification par jeton au porteur Asp.net Core 2.0 JWT avec la démo Web Api

Ajouter un package ” Microsoft.AspNetCore.Authentication.JwtBearer

Startup.cs ConfigureServices ()

 services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(cfg => { cfg.RequireHttpsMetadata = false; cfg.SaveToken = true; cfg.TokenValidationParameters = new TokenValidationParameters() { ValidIssuer = "me", ValidAudience = "you", IssuerSigningKey = new SymmesortingcSecurityKey(Encoding.UTF8.GetBytes("rlyaKithdrYVl6Z80ODU350md")) //Secret }; }); 

Startup.cs Configure ()

 // ===== Use Authentication ====== app.UseAuthentication(); 

User.cs // C’est une classe de modèle par exemple. Cela peut être n’importe quoi.

 public class User { public Int32 Id { get; set; } public ssortingng Username { get; set; } public ssortingng Country { get; set; } public ssortingng Password { get; set; } } 

UserContext.cs // C’est juste une classe de contexte. Cela peut être n’importe quoi.

 public class UserContext : DbContext { public UserContext(DbContextOptions options) : base(options) { this.Database.EnsureCreated(); } public DbSet Users { get; set; } } 

AccountController.cs

 [Route("[controller]")] public class AccountController : Controller { private readonly UserContext _context; public AccountController(UserContext context) { _context = context; } [AllowAnonymous] [Route("api/token")] [HttpPost] public async Task Token([FromBody]User user) { if (!ModelState.IsValid) return BadRequest("Token failed to generate"); var userIdentified = _context.Users.FirstOrDefault(u => u.Username == user.Username); if (userIdentified == null) { return Unauthorized(); } user = userIdentified; //Add Claims var claims = new[] { new Claim(JwtRegisteredClaimNames.UniqueName, "data"), new Claim(JwtRegisteredClaimNames.Sub, "data"), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToSsortingng()), }; var key = new SymmesortingcSecurityKey(Encoding.UTF8.GetBytes("rlyaKithdrYVl6Z80ODU350md")); //Secret var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken("me", "you", claims, expires: DateTime.Now.AddMinutes(30), signingCredentials: creds); return Ok(new { access_token = new JwtSecurityTokenHandler().WriteToken(token), expires_in = DateTime.Now.AddMinutes(30), token_type = "bearer" }); } } 

UserController.cs

 [Authorize] [Route("api/[controller]")] public class UserController : ControllerBase { private readonly UserContext _context; public UserController(UserContext context) { _context = context; if(_context.Users.Count() == 0 ) { _context.Users.Add(new User { Id = 0, Username = "Abdul Hameed Abdul Sattar", Country = "Indian", Password = "123456" }); _context.SaveChanges(); } } [HttpGet("[action]")] public IEnumerable GetList() { return _context.Users.ToList(); } [HttpGet("[action]/{id}", Name = "GetUser")] public IActionResult GetById(long id) { var user = _context.Users.FirstOrDefault(u => u.Id == id); if(user == null) { return NotFound(); } return new ObjectResult(user); } [HttpPost("[action]")] public IActionResult Create([FromBody] User user) { if(user == null) { return BadRequest(); } _context.Users.Add(user); _context.SaveChanges(); return CreatedAtRoute("GetUser", new { id = user.Id }, user); } [HttpPut("[action]/{id}")] public IActionResult Update(long id, [FromBody] User user) { if (user == null) { return BadRequest(); } var userIdentified = _context.Users.FirstOrDefault(u => u.Id == id); if (userIdentified == null) { return NotFound(); } userIdentified.Country = user.Country; userIdentified.Username = user.Username; _context.Users.Update(userIdentified); _context.SaveChanges(); return new NoContentResult(); } [HttpDelete("[action]/{id}")] public IActionResult Delete(long id) { var user = _context.Users.FirstOrDefault(u => u.Id == id); if (user == null) { return NotFound(); } _context.Users.Remove(user); _context.SaveChanges(); return new NoContentResult(); } } 

Test sur PostMan: Vous recevrez un jeton en réponse.

Transmettez TokenType et AccessToken dans l’en-tête dans d’autres services Web. entrer la description de l'image ici

Bonne chance! Je suis juste débutant. Je n’ai passé qu’une semaine pour commencer à apprendre l’asp.net core.

Juste pour mettre à jour l’excellente réponse de @alerya, j’ai dû modifier la classe d’assistance pour ressembler à ceci:

 public static class IHttpContextAccessorExtension { public static ssortingng CurrentUser(this IHttpContextAccessor httpContextAccessor) { var userId = httpContextAccessor?.HttpContext?.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; return userId; } } 

Ensuite, je pourrais obtenir l’ID utilisateur dans ma couche de service. Je sais que c’est facile dans le contrôleur, mais un défi plus loin.