我试图在这里围绕几个概念,但我不希望这个问题过于宽泛 - 基本上我们要做的是使用角色声明作为锁定我们的API的权限,但我发现access_token变得太大了。
我们在服务器端使用OpenIddict和ASP.NET Identity 3。我们已经实现了默认的AspNetRoleClaims表来存储我们对每个角色的声明 - 使用它们作为权限。
我们使用基于自定义策略的声明授权锁定我们的API端点,如下所示:
Custom Policy Based Authorization
我发现的主要问题是我们的access_token包含我们的声明变得非常大。我们试图使ClaimType和Value在数据库中非常小,以使索赔占用空间更小。我们有一个基本的CRUD类型权限方案,因此对于SPA客户端应用程序中的每个“模块”或屏幕,有4个权限。我们添加到应用程序中的模块越多,在access_token中声明越多,我们的授权承载头越来越大。随着应用程序的增长,我担心这会变得不那么可扩展。
因此声明嵌入在access_token中,当我点击使用自定义策略锁定的端点时......
[Authorize(Policy="MyModuleCanRead")]
[HttpGet]
public IEnumerable<MyViewModel> Get()
然后,我可以在AuthorizationHandler中访问我的ASP.NET身份用户和User.Claims。
如果这是一个显而易见的问题,请提前抱歉 - 但我想知道 - 为了让基于自定义策略的授权工作 - 它是否绝对要求声明在id_token或access_token中以便调用处理程序?
如果我从access_token中删除声明,那么我的AuthorizationHandler代码就不会被命中,我无法访问使用自定义策略锁定的终端。
我想知道是否可以使用自定义声明策略,但是具有在Authorization处理程序中检查声明的实际代码,以便声明不会随每个HTTP请求传递,而是从服务器端获取授权cookie或来自数据库。
*更新*
Pintpoint使用授权处理程序的答案以及关于如何从cookie中删除其他角色声明的评论实现了我正在寻找的内容。
如果这有助于其他任何人 - 这里是覆盖UserClaimsPrincipalFactory并阻止角色声明被写入cookie的代码。 (我有很多角色声明作为权限,cookie和请求标头变得太大了)
public class AppClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>
{
public AppClaimsPrincipalFactory(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager, IOptions<IdentityOptions> optionsAccessor) : base(userManager, roleManager, optionsAccessor)
{
}
public override async Task<ClaimsPrincipal> CreateAsync(ApplicationUser user)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
var userId = await UserManager.GetUserIdAsync(user);
var userName = await UserManager.GetUserNameAsync(user);
var id = new ClaimsIdentity(Options.Cookies.ApplicationCookieAuthenticationScheme,
Options.ClaimsIdentity.UserNameClaimType,
Options.ClaimsIdentity.RoleClaimType);
id.AddClaim(new Claim(Options.ClaimsIdentity.UserIdClaimType, userId));
id.AddClaim(new Claim(Options.ClaimsIdentity.UserNameClaimType, userName));
if (UserManager.SupportsUserSecurityStamp)
{
id.AddClaim(new Claim(Options.ClaimsIdentity.SecurityStampClaimType,
await UserManager.GetSecurityStampAsync(user)));
}
// code removed that adds the role claims
if (UserManager.SupportsUserClaim)
{
id.AddClaims(await UserManager.GetClaimsAsync(user));
}
return new ClaimsPrincipal(id);
}
}
答案 0 :(得分:1)
我想知道是否可以使用自定义声明策略,但是具有在Authorization处理程序中检查声明的实际代码,以便声明不会随每个HTTP请求传递,而是从服务器端获取授权cookie或来自数据库。
这绝对有可能。这是你如何做到的:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IAuthorizationHandler, PermissionAuthorizationHandler>();
services.AddAuthorization(options =>
{
options.AddPolicy("Has-Edit-User-Profiles-Permission", builder =>
{
builder.RequirePermission("Edit-User-Profiles");
});
});
}
}
public class PermissionAuthorizationRequirement : IAuthorizationRequirement
{
public PermissionAuthorizationRequirement(string permission)
{
if (string.IsNullOrEmpty(permission))
{
throw new ArgumentException("The permission cannot be null or empty.", nameof(permission));
}
Permission = permission;
}
public string Permission { get; set; }
}
public class PermissionAuthorizationHandler :
AuthorizationHandler<PermissionAuthorizationRequirement>
{
private readonly UserManager<ApplicationUser> _userManager;
public PermissionAuthorizationHandler(UserManager<ApplicationUser> userManager)
{
if (userManager == null)
{
throw new ArgumentNullException(nameof(userManager));
}
_userManager = userManager;
}
protected override async Task HandleRequirementAsync(
AuthorizationHandlerContext context,
PermissionAuthorizationRequirement requirement)
{
if (context.User == null)
{
return;
}
var user = await _userManager.GetUserAsync(context.User);
if (user == null)
{
return;
}
// Use whatever API you need to ensure the user has the requested permission.
if (await _userManager.IsInRoleAsync(user, requirement.Permission))
{
context.Succeed(requirement);
}
}
}
public static class PermissionAuthorizationExtensions
{
public static AuthorizationPolicyBuilder RequirePermission(
this AuthorizationPolicyBuilder builder, string permission)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (string.IsNullOrEmpty(permission))
{
throw new ArgumentException("The permission cannot be null or empty.", nameof(permission));
}
return builder.AddRequirements(new PermissionAuthorizationRequirement(permission));
}
}