ASP.NET Core Web API 和角色授权
ASP.NET Core Web API and roles authorization
我编写了一个应用程序,其中 ASP.NET Core Identity 负责服务器上的身份验证和授权。为此,我还使用了 JWT 令牌。除了基于角色的授权外,一切正常。简单的 [Authorize] 属性有效,我需要登录才能获取资源,但是当我使用 [Authorize(Roles="Administrator")] none 时获得授权。
private static async Task CreateUsers(
RoleManager<IdentityRole> roleManager,
UserManager<ApplicationUser> userManager)
{
string role_Administrator = "Administrator";
string role_RegisteredUser = "RegisteredUser";
await roleManager.CreateAsync(new IdentityRole(role_Administrator));
await roleManager.CreateAsync(new IdentityRole(role_RegisteredUser));
var user_Admin = new ApplicationUser()
{
SecurityStamp = Guid.NewGuid().ToString(),
UserName = "Admin",
Email = "admin@test.com",
};
await userManager.CreateAsync(user_Admin, "Pass4Admin");
await userManager.AddToRoleAsync(user_Admin, role_RegisteredUser);
await userManager.AddToRoleAsync(user_Admin, role_Administrator);
}
以及来自控制器的示例方法
[HttpGet("{id}")]
[Authorize(Roles = "Administrator")]
public IActionResult Get(int id)
{
return Ok();
}
我认为当请求来自客户端应用程序时,Identity 能够根据创建的表(如 aspnetroles 和 aspnetuserroles)中的数据检查分配给经过身份验证的用户的角色,但显然我不了解这种机制,我应该以某种方式配置它。
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ApplicationUser>().ToTable("AppUsers");
}
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddEntityFrameworkMySql();
services.AddDbContext<ApplicationDbContext>(options =>
options.UseMySql(Configuration.GetConnectionString("DefaultConnection"))
);
services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
options.Password.RequireNonAlphanumeric = false;
options.Password.RequiredLength = 7;
})
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddAuthentication(opts =>
{
opts.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
opts.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
opts.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(cfg =>
{
cfg.RequireHttpsMetadata = true;
cfg.SaveToken = true;
cfg.TokenValidationParameters = new TokenValidationParameters()
{
ValidIssuer = Configuration["Auth:Jwt:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Auth:Jwt:Key"])),
ValidAudience = Configuration["Auth:Jwt:Audience"],
ClockSkew = TimeSpan.Zero,
RequireExpirationTime = true,
ValidateIssuer = true,
ValidateIssuerSigningKey = true,
ValidateAudience = true
};
cfg.IncludeErrorDetails = true;
});
services.AddAuthorization(options =>
{
options.AddPolicy("RequireAdministratorRole",
policy => policy.RequireRole("Administrator"));
});
services.AddAutoMapper(typeof(WebMappingProfile));
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var dbContext = serviceScope.ServiceProvider.GetService<ApplicationDbContext>();
var roleManager = serviceScope.ServiceProvider.GetService<RoleManager<IdentityRole>>();
var userManager = serviceScope.ServiceProvider.GetService<UserManager<ApplicationUser>>();
dbContext.Database.Migrate();
DbSeeder.Seed(dbContext, roleManager, userManager);
}
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
if (!env.IsDevelopment())
{
app.UseSpaStaticFiles();
}
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{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");
}
});
}
如果您使用自定义 jwt 中间件,请将角色添加到您的令牌中作为声明
登录操作和令牌生成器:
var role = await _userManager.GetRolesAsync(user);
IdentityOptions _options = new IdentityOptions();
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[] {
new Claim("UserID",user.Id.ToString()),
new Claim(_options.ClaimsIdentity.RoleClaimType,role.FirstOrDefault())
}),
Expires = DateTime.UtcNow.AddDays(_applicationSettings.Token_Expire_Day),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(_applicationSettings.JWT_Secret)),
SecurityAlgorithms.HmacSha256Signature)
};
var tokenHandler = new JwtSecurityTokenHandler();
var securityToken = tokenHandler.CreateToken(tokenDescriptor);
var token = tokenHandler.WriteToken(securityToken);
我编写了一个应用程序,其中 ASP.NET Core Identity 负责服务器上的身份验证和授权。为此,我还使用了 JWT 令牌。除了基于角色的授权外,一切正常。简单的 [Authorize] 属性有效,我需要登录才能获取资源,但是当我使用 [Authorize(Roles="Administrator")] none 时获得授权。
private static async Task CreateUsers(
RoleManager<IdentityRole> roleManager,
UserManager<ApplicationUser> userManager)
{
string role_Administrator = "Administrator";
string role_RegisteredUser = "RegisteredUser";
await roleManager.CreateAsync(new IdentityRole(role_Administrator));
await roleManager.CreateAsync(new IdentityRole(role_RegisteredUser));
var user_Admin = new ApplicationUser()
{
SecurityStamp = Guid.NewGuid().ToString(),
UserName = "Admin",
Email = "admin@test.com",
};
await userManager.CreateAsync(user_Admin, "Pass4Admin");
await userManager.AddToRoleAsync(user_Admin, role_RegisteredUser);
await userManager.AddToRoleAsync(user_Admin, role_Administrator);
}
以及来自控制器的示例方法
[HttpGet("{id}")]
[Authorize(Roles = "Administrator")]
public IActionResult Get(int id)
{
return Ok();
}
我认为当请求来自客户端应用程序时,Identity 能够根据创建的表(如 aspnetroles 和 aspnetuserroles)中的数据检查分配给经过身份验证的用户的角色,但显然我不了解这种机制,我应该以某种方式配置它。
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ApplicationUser>().ToTable("AppUsers");
}
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddEntityFrameworkMySql();
services.AddDbContext<ApplicationDbContext>(options =>
options.UseMySql(Configuration.GetConnectionString("DefaultConnection"))
);
services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
options.Password.RequireNonAlphanumeric = false;
options.Password.RequiredLength = 7;
})
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddAuthentication(opts =>
{
opts.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
opts.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
opts.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(cfg =>
{
cfg.RequireHttpsMetadata = true;
cfg.SaveToken = true;
cfg.TokenValidationParameters = new TokenValidationParameters()
{
ValidIssuer = Configuration["Auth:Jwt:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Auth:Jwt:Key"])),
ValidAudience = Configuration["Auth:Jwt:Audience"],
ClockSkew = TimeSpan.Zero,
RequireExpirationTime = true,
ValidateIssuer = true,
ValidateIssuerSigningKey = true,
ValidateAudience = true
};
cfg.IncludeErrorDetails = true;
});
services.AddAuthorization(options =>
{
options.AddPolicy("RequireAdministratorRole",
policy => policy.RequireRole("Administrator"));
});
services.AddAutoMapper(typeof(WebMappingProfile));
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var dbContext = serviceScope.ServiceProvider.GetService<ApplicationDbContext>();
var roleManager = serviceScope.ServiceProvider.GetService<RoleManager<IdentityRole>>();
var userManager = serviceScope.ServiceProvider.GetService<UserManager<ApplicationUser>>();
dbContext.Database.Migrate();
DbSeeder.Seed(dbContext, roleManager, userManager);
}
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
if (!env.IsDevelopment())
{
app.UseSpaStaticFiles();
}
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{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");
}
});
}
如果您使用自定义 jwt 中间件,请将角色添加到您的令牌中作为声明
登录操作和令牌生成器:
var role = await _userManager.GetRolesAsync(user);
IdentityOptions _options = new IdentityOptions();
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[] {
new Claim("UserID",user.Id.ToString()),
new Claim(_options.ClaimsIdentity.RoleClaimType,role.FirstOrDefault())
}),
Expires = DateTime.UtcNow.AddDays(_applicationSettings.Token_Expire_Day),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(_applicationSettings.JWT_Secret)),
SecurityAlgorithms.HmacSha256Signature)
};
var tokenHandler = new JwtSecurityTokenHandler();
var securityToken = tokenHandler.CreateToken(tokenDescriptor);
var token = tokenHandler.WriteToken(securityToken);