个人认为你需要完全摆脱使用
[Authorize]
属性。很明显,您的授权要求比该方法“开箱即用”所预期的更加复杂。
此外,在关于我认为身份验证和授权被互换使用的问题上。我们在这里处理的是授权。
由于您正在使用基于身份和声明的授权。我会考虑在“即时”进行此操作。除了声明之外,您还可以利用动态策略生成以及使用
IAuthorizationRequirement
实例的基于服务的授权来构建复杂的规则和要求。
深入研究其实现是一个大话题,但有一些非常好的资源可用。最初的方法(我自己使用过)最初由
IdentityServer的Dom和Brock详细介绍。
他们在去年的NDC上做了一次全面的视频演示,您可以在
此处观看。
本文介绍的概念与 Jerrie Pelser 在其博客中讨论的实现密切相关,您可以在这里阅读。
一般组件包括:
[Authorize] 属性将被策略生成器替换,例如:
public class AuthorizationPolicyProvider : DefaultAuthorizationPolicyProvider
{
private readonly IConfiguration _configuration;
public AuthorizationPolicyProvider(IOptions<AuthorizationOptions> options, IConfiguration configuration) : base(options)
{
_configuration = configuration;
}
public override async Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
{
var policy = await base.GetPolicyAsync(policyName);
if (policy == null)
{
policy = new AuthorizationPolicyBuilder()
.AddRequirements(new HasScopeRequirement(policyName, $"https://{_configuration["Auth0:Domain"]}/"))
.Build();
}
return policy;
}
}
然后您需要编写任何IAuthorizationRequirement
的实例,以确保用户得到适当的授权,例如:
public class HasScopeRequirement : IAuthorizationRequirement
{
public string Issuer { get; }
public string Scope { get; }
public HasScopeRequirement(string scope, string issuer)
{
Scope = scope ?? throw new ArgumentNullException(nameof(scope));
Issuer = issuer ?? throw new ArgumentNullException(nameof(issuer));
}
}
Dom和Brock还详细介绍了一个客户端实现,将所有这些内容绑定在一起,可能看起来像这样:
public class AuthorisationProviderClient : IAuthorisationProviderClient
{
private readonly UserManager<ApplicationUser> userManager;
private readonly RoleManager<IdentityRole> roleManager;
public AuthorisationProviderClient(
UserManager<ApplicationUser> userManager,
RoleManager<IdentityRole> roleManager)
{
this.userManager = userManager;
this.roleManager = roleManager;
}
public async Task<bool> IsInRole(ClaimsPrincipal user, string role)
{
var appUser = await GetApplicationUser(user);
return await userManager.IsInRoleAsync(appUser, role);
}
public async Task<List<Claim>> GetAuthorisationsForUser(ClaimsPrincipal user)
{
List<Claim> claims = new List<Claim>();
var appUser = await GetApplicationUser(user);
var roles = await userManager.GetRolesAsync(appUser);
foreach (var role in roles)
{
var idrole = await roleManager.FindByNameAsync(role);
var roleClaims = await roleManager.GetClaimsAsync(idrole);
claims.AddRange(roleClaims);
}
return claims;
}
public async Task<bool> HasClaim(ClaimsPrincipal user, string claimValue)
{
Claim required = null;
var appUser = await GetApplicationUser(user);
var userRoles = await userManager.GetRolesAsync(appUser);
foreach (var userRole in userRoles)
{
var identityRole = await roleManager.FindByNameAsync(userRole);
var roleClaims = await roleManager.GetClaimsAsync(identityRole);
required = roleClaims.FirstOrDefault(x => x.Value == claimValue);
if (required != null)
{
break;
}
}
if (required == null)
{
var userClaims = await userManager.GetClaimsAsync(appUser);
required = userClaims.FirstOrDefault(x => x.Value == claimValue);
}
return required != null;
}
private async Task<ApplicationUser> GetApplicationUser(ClaimsPrincipal user)
{
return await userManager.GetUserAsync(user);
}
}
虽然此实现未完全满足您的确切要求(无论如何都很难做到),但在您提出的情况下,这几乎肯定是我会采用的方法。
AuthRepository
是自定义实现吗? - ste-fu