我阅读了无数的教程、文章和问题。
我不明白为什么
[Authorize(Roles = "SuperAdmin")]
或任何其他角色不起作用。无论指定的角色如何,每个人都会收到 403:
//[Authorize(Roles = "SuperAdmin")] - Commented out to debug roles
public async Task<IActionResult> Index()
{
var userID = User.FindFirstValue(ClaimTypes.NameIdentifier);
var user = await _userManager.FindByIdAsync(userID);
var roles = await _userManager.GetRolesAsync(user);
return View();
}
但他们仍然会在任何授权角色的控制器上收到 403。
这是我的程序.cs 的相关部分:
builder.Services.AddIdentity<TMSUser, IdentityRole>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddClaimsPrincipalFactory<MyUserClaimsPrincipalFactory>()
.AddRoleManager<RoleManager<IdentityRole>>()
.AddTokenProvider<DataProtectorTokenProvider<TMSUser>>(TokenOptions.DefaultProvider);
builder.Services.ConfigureApplicationCookie(options =>
{
options.AccessDeniedPath = new PathString("/Home/HandleError/401");
options.LoginPath = new PathString("/Home/Portal");
});
builder.Services.AddAutoMapper(typeof(Program));
builder.Services.AddControllersWithViews();
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddControllers().AddNewtonsoftJson(options =>
{
options.SerializerSettings.ContractResolver = new DefaultContractResolver();
});
builder.Services.AddTransient<IEmailSender, EmailSender>();
builder.Services.AddTransient<ITools, Tools>();
builder.Services.Configure<AuthMessageSenderOptions>(builder.Configuration);
builder.Services.AddMvc(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
});
var app = builder.Build();
app.UseStatusCodePagesWithReExecute("/Home/HandleError/{0}");
app.UseHsts();
using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
var context = services.GetRequiredService<TMSContext>();
context.Database.EnsureCreated();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.MapRazorPages();
app.Run();
在过去的几个月里,我无数次地更改了我的代码,试图让它发挥作用,但现在我只是乞求帮助。这非常令人沮丧!我错过了什么?我的代码中的其他内容是否会阻止其正常工作?
谢谢!
检查你得到了什么
User.FindAll(ClaimTypes.Role).ToList()
之后检查“超级管理员”是否出现在列表中?如果您的问题没有解决,请发布您的
GetRolesAsync
功能。
看来你必须添加
services.AddDefaultIdentity<TMSUser>()
所以完整的代码看起来像这样
services.AddDefaultIdentity<ApplicationUser>()
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ICMSDbContext>()
.AddClaimsPrincipalFactory<CustomClaimsPrincipalFactory>();
解决方案比描述的要简单得多。如果您通过 .AddRoles() 添加角色并通过 .AddClaimsPrincipalFactory() 添加自定义声明;您必须继承 UserClaimsPrincipalFactory
所以你的代码应该如下所示:
public class CustomUserClaimsPrincipalFactory: UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>
{
public CustomUserClaimsPrincipalFactory(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager, IOptions<IdentityOptions> options) : base(userManager, roleManager, options)
{
}
//and below should be part with adding custom claim(s)
}
启用会话后就可以了。
builder.Services.AddSession();
app.UseSession();
我终于想出了一个解决方案。事情本来不应该这么复杂!我在其他版本.net中使用过角色,没有这个问题。
我创建了一个自定义授权过滤器,我只是访问数据库(我假设所有助手也这样做,所以没什么大不了的):
部分内容是我从此链接
获得的首先,我用我的角色创建了一个模型:
public enum Role
{
User, Admin, SuperAdmin
}
然后是自定义授权过滤器:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AuthorizeAttribute : Attribute, IAuthorizationFilter
{
private readonly IList<Role> _roles;
public AuthorizeAttribute(params Role[] roles)
{
_roles = roles ?? new Role[] { };
}
public void OnAuthorization(AuthorizationFilterContext context)
{
// Skip authorization if action is decorated with [AllowAnonymous] attribute
var allowAnonymous = context.ActionDescriptor.EndpointMetadata
.OfType<AllowAnonymousAttribute>().Any();
if (allowAnonymous)
return;
// Authorization
var user = context.HttpContext.User;
if (user != null && _roles.Any())
{
using (var db = context.HttpContext.RequestServices
.GetService<ApplicationDbContext>())
{
var userRoles = (from ur in db.UserRoles
join r in db.Roles on ur.RoleId equals r.Id
join u in db.Users on ur.UserId equals u.Id
where u.UserName == user.Identity.Name
select new
{
r.Name
}).ToList();
var isAuthorized = false;
foreach (var role in userRoles)
{
Role claimRole;
Enum.TryParse(role.Name, out claimRole);
if (_roles.Contains(claimRole))
isAuthorized = true;
}
if (isAuthorized)
return;
}
}
context.Result = new UnauthorizedResult();
}
}
我这样使用它:
[Authorize(Role.SuperAdmin)]
public async Task<IActionResult> Index()
{
return View();
}
我确实喜欢这种方法。它将允许我在每个控制器/用户级别上做一些我想做的其他事情 - 我只是不敢相信它是如此复杂!
希望这对某人有帮助。如果这方面有任何明显的问题,请告诉我。我正式入伍了。