.Net Core 2 JWT, Angular 2 Авторизация через роли не работает
У меня есть следующая полезная загрузка в токене, сгенерированном с помощью JWT
{"sub": "flamelsoft@gmail.com", "jti": "0bca1034-f3ce-4f72-bd91-65c1a61924c4", " http://schemas.microsoft.com/ws/2008/06/identity/claims/role":" Администратор "," exp ": 1509480891," iss ":" http://localhost:40528/"," aud ":" http://localhost:40528/" }
с этим кодом Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DBContextSCM>(options =>
options.UseMySql(Configuration.GetConnectionString("DefaultConnection"), b =>
b.MigrationsAssembly("FlamelsoftSCM")));
services.AddIdentity<User, Role>()
.AddEntityFrameworkStores<DBContextSCM>()
.AddDefaultTokenProviders();
services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
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"]))
};
});
services.AddMvc();
}
AccountController.cs
[HttpPost]
[Authorize(Roles="Administrator")]
public async Task<IActionResult> Register([FromBody]RegisterModel model)
{
try
{
var user = new User { UserName = model.Email, Email = model.Email };
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
var role = await _roleManager.FindByIdAsync(model.Role);
result = await _userManager.AddToRoleAsync(user, role.Name);
if (result.Succeeded)
return View(model);
}
return BadRequest($"Error: Could not create user");
}
catch (Exception ex)
{
return BadRequest($"Error: {ex.Message}");
}
}
user.service.ts
export class UserService {
constructor(private http: Http, private config: AppConfig, private currentUser: User) { }
create(user: User) {
return this.http.post(this.config.apiUrl + 'Account/Register', user, this.jwt());
}
private jwt() {
const userJson = localStorage.getItem('currentUser');
this.currentUser = userJson !== null ? JSON.parse(userJson) : new User();
if (this.currentUser && this.currentUser.token) {
let headers = new Headers({ 'Authorization': 'Bearer ' + this.currentUser.token });
return new RequestOptions({ headers: headers });
}
}}
Проблема в том, что проверка роли не работает, запрос поступает в контроллер и возвращает код 200 в заголовке, но никогда не входит в класс. Когда я удаляю [Authorize (Roles = "Administrator")], он правильно вводит мой код. Есть что-то плохо определенное? Или какая альтернатива для определения полномочий через роли.
1 ответ
TL;DR
Как уже упоминалось в комментариях к исходному вопросу, меняется:
[HttpPost]
[Authorize(Roles = "Administrator")]
public async Task<IActionResult> Register([FromBody]RegisterModel model)
{
// Code
}
в
[HttpPost]
[Authorize(AuthenticationSchemes = "Bearer", Roles = "Administrator")]
public async Task<IActionResult> Register([FromBody]RegisterModel model)
{
// Code
}
решил проблему.
Bearer
имя схемы аутентификации по умолчанию при использовании аутентификации канала-носителя JWT в ASP.NET Core.
Но почему мы должны указать AuthenticationSchemes
собственность на [Authorize]
атрибут?
Это потому, что настройка схем аутентификации не означает, что они будут выполняться при каждом HTTP-запросе. Если определенное действие доступно для анонимных пользователей, зачем беспокоиться о том, чтобы извлекать информацию о пользователе из файла cookie или токена? MVC хорошо разбирается в этом и будет запускать обработчики аутентификации только тогда, когда это необходимо, то есть во время запросов, которые каким-то образом защищены.
В нашем случае MVC обнаруживает [Authorize]
атрибут, следовательно, знает, что он должен запустить аутентификацию и авторизацию, чтобы определить, авторизован запрос или нет. Хитрость заключается в том, что он будет запускать только те обработчики схем аутентификации, которые были указаны. Здесь у нас их не было, поэтому аутентификация не выполнялась, что означало, что авторизация не удалась, поскольку запрос считался анонимным.
Добавление схемы аутентификации к атрибуту дало MVC команду запустить этот обработчик, который извлекал информацию пользователя из токена в HTTP-запросе, что приводило к Administrator
роль выясняется, и запрос был разрешен.
Как примечание стороны, есть другой способ достигнуть этого, не прибегая к использованию AuthenticationSchemes
собственность [Authorize]
приписывать.
Представьте, что в вашем приложении настроена только одна схема аутентификации, и было бы больно указывать, что AuthenticationSchemes
недвижимость на каждом [Authorize]
приписывать.
С помощью ASP.NET Core вы можете настроить схему аутентификации по умолчанию. Это подразумевает, что связанный обработчик будет выполняться для каждого HTTP-запроса, независимо от того, защищен ресурс или нет.
Настройка этого состоит из двух частей:
public class Startup
{
public void ConfiguresServices(IServiceCollection services)
{
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme /* this sets the default authentication scheme */)
.AddJwtBearer(options =>
{
// Configure options here
});
}
public void Configure(IApplicationBuilder app)
{
// This inserts the middleware that will execute the
// default authentication scheme handler on every request
app.UseAuthentication();
app.UseMvc();
}
}
Это означает, что к тому времени, когда MVC оценит, авторизован ли запрос, аутентификация уже будет иметь место, поэтому не указывать никакого значения для AuthenticationSchemes
собственность [Authorize]
Атрибут не будет проблемой.
Часть процесса авторизации все еще будет запускаться и проверять пользователя, прошедшего проверку подлинности, являются ли они частью Administrator
группа или нет.
Я знаю, что на этот вопрос уже есть ответ, но здесь упущено кое-что важное. Вам нужно убедиться, что вы действительно устанавливаете заявки для вошедшего в систему пользователя. В моем случае я использую аутентификацию JWT, поэтому этот шаг очень важен:
var claims = new ClaimsIdentity(new[] { new Claim(ClaimTypes.NameIdentifier, user.UserName) });
var roles = await _userManager.GetRolesAsync(user);
if (roles.Count > 0)
{
foreach (var role in roles) { claims.AddClaim(new Claim(ClaimTypes.Role, role)); }
}
var token = new JwtSecurityToken(
issuer: _configuration["JWT:Issuer"],
audience: _configuration["JWT:Audience"],
expires: DateTime.UtcNow.AddMinutes(15),
signingCredentials: signingCredentials,
claims: claims.Claims);
Я бился головой, пытаясь понять, почему HttpContext.User
не включил то, что я ожидал, пытаясь сузить [Authroization(Roles="Admin")]
вопрос. Оказывается, если вы используете JWT Auth, вам нужно не забыть установитьClaims[]
к личности. Может быть, в другихdotnet
пути, но jwt
похоже, требует, чтобы вы установили это вручную.
После того, как я установил требования для пользователя, [Authorize(Roles = "Whatever")]
работал как положено.