OpenIdConnect access_token大小和访问声明服务器端

时间:2017-02-27 21:42:53

标签: asp.net-core claims-based-identity openid-connect asp.net-identity-3 openiddict

我试图在这里围绕几个概念,但我不希望这个问题过于宽泛 - 基本上我们要做的是使用角色声明作为锁定我们的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);
    }
}

1 个答案:

答案 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));
    }
}