У меня есть приложение .NetCore 2.2, использующее веб-токены Json для аутентификации и авторизации пользователей.
Когда я добавляю атрибут [Authorize] к своим контроллерам, я могу добавить токен на любой запрос к этим контроллерам и взаимодействовать с данными.
Когда я изменяю атрибут Auth для включения роли, например, [Authorize (Policy = "Administrator")] запросы всегда возвращают 403.
Модель User.cs содержит перечисление Role со значениями User / Administrator.
В Startup.cs я добавил RequireRole / RequireAuthenticatedUser.
См. Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.AddJsonOptions(options => { options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; });
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
#region JWT
// Configure AppSettings and add to DI
var appSettingsSection = Configuration.GetSection("AppSettings");
services.Configure<AppSettings>(appSettingsSection);
// Configure jwt authentication
var appSettings = appSettingsSection.Get<AppSettings>();
var key = Encoding.ASCII.GetBytes(appSettings.Secret);
// Add Jwt Authentication Service
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
#endregion
#region Add Transient DI
services.AddTransient<IPlayerService, PlayerService>();
#endregion
#region Add Authorization
services.AddAuthorization(options =>
{
options.AddPolicy("Administrator",
p => p.RequireAuthenticatedUser().RequireRole(Role.Administrator.ToString())
);
options.AddPolicy("User",
p => p.RequireAuthenticatedUser().RequireRole(
new[] { Role.User.ToString(), Role.User.ToString() }
)
);
});
#endregion
#region Cookies
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options => {
options.AccessDeniedPath = "/User/ErrorNotAuthorised";
options.LoginPath = "/User/ErrorNotAuthenticated";
});
#endregion
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
// seeder recreates and seeds database on each execution
new DataSeeder(new PlayerService(), new ClubService(), new TeamService(), new TeamPlayerService(), new UserService()).Seed();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseCookiePolicy();
app.UseCors(x => x
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
}
}
Пример метода контроллера:
// POST: api/Player
[Authorize(Policy="Administrator")]
[HttpPost]
[ValidateAntiForgeryToken]
public void Post([FromBody] Player player)
{
_service.AddPlayer(player);
}
Этот метод контроллера возвращает неавторизованный запрос 403 из всех взаимодействий. Я думаю, что мой токен JWT не содержит значение Role, но я не уверен, как его проверить или как его включить.
Любая помощь приветствуется.
EDIT:
Смотреть на пользователей
Класс пользователей
public enum Role
{
Administrator,
User
}
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public Team Team { get; set; }
public Role Role { get; set; }
public string Token { get; set; }
}
РЕДАКТИРОВАТЬ 2:
Таким образом, все, что действительно необходимо для того, чтобы JWT использовал роли в качестве формы аутентификации, включено в функцию ConfigureServices функции Startup.cs ниже. Я пропустил класс JWT, и также включил это ниже.
Я изменил атрибут auth на контроллерах, чтобы искать Roles = "Administrator" вместо Policies.
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
// Configure AppSettings and add to DI
var appSettingsSection = Configuration.GetSection("AppSettings");
services.Configure<AppSettings>(appSettingsSection);
// Configure jwt authentication
var appSettings = appSettingsSection.Get<AppSettings>();
var key = Encoding.ASCII.GetBytes(appSettings.Secret);
// Add Jwt Authentication Service
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
Класс JWT Helper, который я не понял ранее:
{
// generate Jwt token
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(secret);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Role, user.Role.ToString()),
new Claim(ClaimTypes.Sid, user.Id.ToString())
}),
Expires = DateTime.UtcNow.AddDays(50),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
user.Token = tokenHandler.WriteToken(token);
return user;
}
Пример контроллера с атрибутом Role:
[Authorize(Roles = "Administrator")]
[HttpPost]
public void Post([FromBody] Player player)
{
_service.AddPlayer(player);
}
Наконец, большая часть этого очевидна, и я должен был знать, прежде чем начать проект, не берите в голову этот пост - но обновление, так что любой, кто сталкивается с этим в будущем, видит более подходящий маршрут.