JWT on.NET ядро 2.0
Я был на довольно приключение, чтобы получить JWT работает на DotNet core 2.0 (теперь достигает окончательного выпуска Сегодня). Там есть Т документации, но весь пример кода, похоже, использует устаревшие API и приходит в свежее ядро, это положительно головокружительно, чтобы выяснить, как именно он должен быть реализован. Я пытался использовать Хосе, но приложение. UseJwtBearerAuthentication был устаревшим, и нет никакой документации о том, что делать дальше.
кто-нибудь есть проект с открытым исходным кодом, который использует dotnet core 2.0, который может просто проанализировать JWT из заголовка авторизации и позволить мне авторизовать запросы для маркера JWT с кодировкой HS256?
класс ниже не бросает никаких исключений, но никакие запросы не разрешены, и я не получаю никаких указаний почему они являются несанкционированными. Ответы пустые 401, так что для меня это означает, что не было никакого исключения, но что секрет не соответствует.
одна странная вещь заключается в том, что мой токены зашифрованы с помощью алгоритма HS256, но я не вижу индикатора, чтобы заставить его использовать этот алгоритм в любом месте.
вот класс, который у меня есть до сих пор:
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 SymmetricSecurityKey(Encoding.ASCII.GetBytes("SECRET_KEY"));
var tokenValidationParameters = new TokenValidationParameters
{
// The signing key must match!
ValidateIssuerSigningKey = true,
ValidateAudience = false,
ValidateIssuer = false,
IssuerSigningKeys = new List<SecurityKey>{ 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.ToString());
}
};
});
return services;
}
}
}
6 ответов:
вот полный минимальный пример работы с контроллером. Я надеюсь, что вы можете проверить это с помощью Postman или JavaScript вызова.
appsettings.json, appsettings.Развитие.формат JSON. Добавьте раздел. Обратите внимание, ключ должен быть достаточно длинным и эмитентом является адрес сервиса:
... ,"Tokens": { "Key": "Rather_very_long_key", "Issuer": "http://localhost:56268/" } ...
!!! В реальном проекте, не держите ключ в appsettings.файл json. Он должен храниться в переменной окружения и принимать его следующим образом:
Environment.GetEnvironmentVariable("JWT_KEY");
обновление: видя, как работают настройки .net core, вам не нужно брать его именно из среды. Вы можете использовать настройки. Однако вместо этого мы можем записать эту переменную в переменные среды в производстве, тогда наш код предпочтет переменные среды вместо конфигурации.
AuthRequest.cs: Dto сохранение значений для передачи логина и пароль:
public class AuthRequest { public string UserName { get; set; } public string Password { get; set; } }
Автозагрузка.cs в методе Configure () перед приложением.UseMvc ():
app.UseAuthentication();
Автозагрузка.cs в 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 SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Tokens:Key"])) }; });
добавить контроллер:
[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.ToString()), }; var key = new SymmetricSecurityKey(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"); }}
это все люди! Ура!
обновление: люди спрашивают, Как получить текущего пользователя. Todo:
В Автозагрузку.cs в ConfigureServices() добавить
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
в контроллере добавить в конструктор:
private readonly int _currentUser; public MyController(IHttpContextAccessor httpContextAccessor) { _currentUser = httpContextAccessor.CurrentUser(); }
добавьте где-нибудь расширение и используйте его в своем контроллере (using ....)
public static class IHttpContextAccessorExtension { public static int CurrentUser(this IHttpContextAccessor httpContextAccessor) { var stringId = httpContextAccessor?.HttpContext?.User?.FindFirst(JwtRegisteredClaimNames.Jti)?.Value; int.TryParse(stringId ?? "0", out int userId); return userId; } }
это все люди.
мой
tokenValidationParameters
работает, когда они выглядят так:var tokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = GetSignInKey(), ValidateIssuer = true, ValidIssuer = GetIssuer(), ValidateAudience = true, ValidAudience = GetAudience(), ValidateLifetime = true, ClockSkew = TimeSpan.Zero };
и
static private SymmetricSecurityKey GetSignInKey() { const string secretKey = "very_long_very_secret_secret"; var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey)); return signingKey; } static private string GetIssuer() { return "issuer"; } static private string GetAudience() { return "audience"; }
кроме того, добавить параметры.RequireHttpsMetadata = false вот так:
.AddJwtBearer(options => { options.TokenValidationParameters =tokenValidationParameters options.RequireHttpsMetadata = false; });
EDIT:
Не забудьте позвонить
app.UseAuthentication();
в автозагрузку.cs - > настроить метод до приложение.UseMvc ();
вот решение для вас.
в свой стартап.cs, во-первых, конфигурирует его как сервисы:
services.AddAuthentication().AddJwtBearer(cfg => { cfg.RequireHttpsMetadata = false; cfg.SaveToken = true; cfg.TokenValidationParameters = new TokenValidationParameters() { IssuerSigningKey = "somethong", ValidAudience = "something", : }; });
во-вторых, вызовите эту службу в config
app.UseAuthentication();
Теперь вы можете использовать его в своем контроллере, добавив атрибут
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] [HttpGet] public IActionResult GetUserInfo() {
для получения полной информации исходный код, который использует angular в качестве Frond-end см. здесь
вот моя реализация для .Net Core 2.0 API:
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" }, ... }
приведенный выше код включает аутентификацию на всех контроллерах. Чтобы разрешить анонимный доступ, вы можете украсить весь контроллер:
[Route("api/[controller]")] [AllowAnonymous] public class AnonymousController : Controller { ... }
или просто украсить способ, чтобы разрешить одну конечную точку:
[AllowAnonymous] [HttpPost("anonymousmethod")] public async Task<IActionResult> MyAnonymousMethod() { ... }
Примечания:
- это моя первая попытка объявления auth-если что-то не так, пожалуйста, дайте мне знать!
Audience
должен соответствовать идентификатор ресурса по запросу клиента. В нашем случае наш клиент (Угловое веб-приложение) был зарегистрирован отдельно в Azure AD, и он использовал свой идентификатор клиента, который мы зарегистрировали как аудиторию в APIClientId
называется идентификатор приложения на портале Azure (почему??), идентификатор приложения регистрации приложения для API.TenantId
называется код каталог на портале Azure (почему??), найденный под Azure Active Directory > Свойствапри развертывании API в качестве размещенного веб-приложения Azure убедитесь, что вы задали параметры приложения:
например. AzureAD: аудитория / xxxxxxxx-xxxx-xxxx-xxxx-XXXXXXXXXX
Asp.net реализация аутентификации токена носителя Core 2.0 JWT с демонстрацией Web Api
Добавить Пакет "Microsoft.AspNetCore.Идентификация.JwtBearer"
Автозагрузка.cs ConfigureServices()
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(cfg => { cfg.RequireHttpsMetadata = false; cfg.SaveToken = true; cfg.TokenValidationParameters = new TokenValidationParameters() { ValidIssuer = "me", ValidAudience = "you", IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("rlyaKithdrYVl6Z80ODU350md")) //Secret }; });
Автозагрузка.cs настроить()
// ===== Use Authentication ====== app.UseAuthentication();
пользователей.cs // это класс модели только для примера. Это может быть что угодно.
public class User { public Int32 Id { get; set; } public string Username { get; set; } public string Country { get; set; } public string Password { get; set; } }
UserContext.cs // это просто класс контекста. Это может быть что угодно.
public class UserContext : DbContext { public UserContext(DbContextOptions<UserContext> options) : base(options) { this.Database.EnsureCreated(); } public DbSet<User> 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<IActionResult> 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().ToString()), }; var key = new SymmetricSecurityKey(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<User> 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(); } }
передать TokenType и AccessToken в заголовке в других веб-сервисах.
удачи! Я всего лишь новичок. Я только провел одну неделю, чтобы начните учиться asp.net ядро.
просто чтобы обновить отличный ответ от @alerya мне пришлось изменить вспомогательный класс, чтобы выглядеть так;
public static class IHttpContextAccessorExtension { public static string CurrentUser(this IHttpContextAccessor httpContextAccessor) { var userId = httpContextAccessor?.HttpContext?.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; return userId; } }
тогда я мог бы получить идентификатор пользователя в моем слое сервиса. Я знаю, что это легко в контроллере, но вызов дальше вниз.