Я был в большом приключении, чтобы заставить JWT работать над ядром DotNet 2.0 (сегодня выходит финальная версия). Существует тонна документации, но все примеры код , как представляется, используя устаревший API , и приходя в свежем Ядро, Это положительно головокружительным , чтобы выяснить , как именно это должно быть реализовано. Я пробовал использовать Jose, но app. 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;
}
}
}
Ответы:
Вот полный рабочий минимальный образец с контроллером. Надеюсь, вы сможете проверить это с помощью Postman или вызова JavaScript.
appsettings.json, appsettings.Development.json. Добавьте раздел. Обратите внимание: ключ должен быть достаточно длинным, а эмитент - это адрес службы:
... ,"Tokens": { "Key": "Rather_very_long_key", "Issuer": "http://localhost:56268/" } ...
!!! В реальном проекте не храните ключ в файле appsettings.json. Его нужно сохранить в переменной Environment и принять его так:
Environment.GetEnvironmentVariable("JWT_KEY");
ОБНОВЛЕНИЕ : Видя, как работают настройки ядра .net, не нужно брать именно из Environment. Вы можете использовать настройки. Однако вместо этого мы можем записать эту переменную в переменные среды в производственной среде, тогда наш код предпочтет переменные среды вместо конфигурации.
AuthRequest.cs: Для сохранения значений для передачи логина и пароля:
public class AuthRequest { public string UserName { get; set; } public string Password { get; set; } }
Startup.cs в методе Configure () ПЕРЕД app.UseMvc ():
Startup.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"); }}
Вот и все, ребята! Ура!
ОБНОВЛЕНИЕ: люди спрашивают, как получить текущего пользователя. Делать:
В Startup.cs в ConfigureServices () добавьте
В контроллере добавьте в конструктор:
private readonly int _currentUser; public MyController(IHttpContextAccessor httpContextAccessor) { _currentUser = httpContextAccessor.CurrentUser(); }
Добавьте где-нибудь расширение и используйте его в своем контроллере (используя ....)
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"; }
Более того, добавьте options.RequireHttpsMetadata = false вот так:
.AddJwtBearer(options => { options.TokenValidationParameters =tokenValidationParameters options.RequireHttpsMetadata = false; });
ИЗМЕНИТЬ :
Не забудь позвонить
в Startup.cs -> Настроить метод перед app.UseMvc ();
источник
app.UseAuthentication();
заметку, которую вызывали раньше,app.UseMvc();
если вы этого не сделаете, вы получите 401, даже если токен успешно авторизован - я потратил около 2 дней, работая над этим!Реализация аутентификации токена носителя JWT в Asp.net Core 2.0 с демонстрацией веб-API
Добавить пакет « 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 SymmetricSecurityKey(Encoding.UTF8.GetBytes("rlyaKithdrYVl6Z80ODU350md")) //Secret }; });
Startup.cs Настроить ()
// ===== Use Authentication ====== app.UseAuthentication();
User.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(); } }
Тест на PostMan:
Передайте TokenType и AccessToken в заголовке в других веб-сервисах.
Удачи! Я только новичок. Я потратил всего одну неделю, чтобы начать изучать ядро asp.net.
источник
Вот решение для вас.
В файле startup.cs сначала настройте его как services:
services.AddAuthentication().AddJwtBearer(cfg => { cfg.RequireHttpsMetadata = false; cfg.SaveToken = true; cfg.TokenValidationParameters = new TokenValidationParameters() { IssuerSigningKey = "somethong", ValidAudience = "something", : }; });
во-вторых, вызовите эту службу в конфигурации
теперь вы можете использовать его в своем контроллере, добавив атрибут
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] [HttpGet] public IActionResult GetUserInfo() {
Для получения полной информации об исходном коде, который использует angular в качестве Frond-end, см. Здесь
источник
services.AddAuthorization
функции при запуске.Вот моя реализация 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" }, ... }
Приведенный выше код включает аутентификацию на всех контроллерах. Чтобы разрешить анонимный доступ, вы можете украсить весь контроллер:
[Route("api/[controller]")] [AllowAnonymous] public class AnonymousController : Controller { ... }
или просто украсить метод, чтобы разрешить единственную конечную точку:
[AllowAnonymous] [HttpPost("anonymousmethod")] public async Task<IActionResult> MyAnonymousMethod() { ... }
Ноты:
Это моя первая попытка авторизации AD - если что-то не так, дайте мне знать!
Audience
должен соответствовать идентификатору ресурса, запрошенному клиентом. В нашем случае наш клиент (веб-приложение Angular) был зарегистрирован отдельно в Azure AD, и он использовал свой идентификатор клиента, который мы зарегистрировали как аудиторию в API.ClientId
называется Application ID на портале Azure (почему ??), ID приложения для регистрации приложения для API.TenantId
называется Идентификатором каталога на портале Azure (почему ??), находится в Azure Active Directory> СвойстваПри развертывании API как веб-приложения, размещенного в Azure, убедитесь, что вы установили параметры приложения:
например. AzureAD: Аудитория / xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
источник
Чтобы обновить отличный ответ @alerya, мне пришлось изменить вспомогательный класс, чтобы он выглядел так;
public static class IHttpContextAccessorExtension { public static string CurrentUser(this IHttpContextAccessor httpContextAccessor) { var userId = httpContextAccessor?.HttpContext?.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; return userId; } }
Тогда я мог бы получить userId на своем уровне обслуживания. Я знаю, что с контроллером это легко, но сложнее.
источник