【问题标题】:OpenIdConnect access_token size and accessing claims server sideOpenIdConnect access_token 大小和访问声明服务器端
【发布时间】:2017-07-18 16:32:48
【问题描述】:

我试图在这里围绕几个概念展开思考,但我不希望这个问题过于宽泛——基本上我们想要做的是使用角色声明作为权限来锁定我们的 API,但我发现access_token 变得太大了。

我们在服务器端使用 OpenIddict 和 ASP.NET Identity 3。我们已经实现了默认的 AspNetRoleClaims 表来存储我们对每个角色的声明 - 将它们用作权限。

我们使用基于自定义策略的声明授权来锁定我们的 API 端点,如下所示:

Custom Policy Based Authorization

我发现的主要问题是包含我们声明的 access_token 变得非常大。我们正在尝试使数据库中的 ClaimType 和 Value 非常小,以使声明占用空间更小。我们有一个基本的 CRUD 类型权限方案,因此对于我们的 SPA 客户端应用程序中的每个“模块”或屏幕,有 4 个权限。我们添加到应用程序中的模块越多,access_token 中的声明就越多,并且我们的 Authorization Bearer 标头变得非常大。我担心随着应用程序的增长,这变得不太可扩展。

因此声明嵌入在 access_token 中,当我点击我的端点时,该端点被这样的自定义策略锁定...

[Authorize(Policy="MyModuleCanRead")]
[HttpGet]
public IEnumerable<MyViewModel> Get()

然后我可以在 AuthorizationHandler 中访问我的 ASP.NET 身份用户和 User.Claims。

如果这是一个明显的问题,请提前抱歉 - 但我想知道 - 为了让基于自定义策略的授权工作 - 是否绝对需要声明在 id_token 或 access_token 中才能调用处理程序?

如果我从 access_token 中删除声明,那么我的 AuthorizationHandler 代码不会被命中,并且我无法访问被我的自定义策略锁定的端点。

我想知道是否可以使用自定义声明策略,但在授权处理程序中拥有检查声明的实际代码,以便声明不会随每个 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);
    }
}

【问题讨论】:

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


    【解决方案1】:

    我想知道是否可以使用自定义声明策略,但在授权处理程序中拥有检查声明的实际代码,以便声明不会随每个 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));
        }
    }
    

    【讨论】:

    • 感谢 Pinpoint - 这是一个很好的例子。这将在大多数情况下适用于许多角色声明。我可以将它们从存储在 id_token 和 access_token 中删除。但是,由于我使用的是 AspNetRoleClaims 表,身份给了我“开箱即用”的说法——这些许多声明仍然存储在身份授权 cookie 中,现在我发现当我注销时,我的 HTTP 请求标头是太大了。
    • 我想知道我是否可以阻止这个角色声明被填充到身份 cookie 中,也许有一种方法可以在我点击 PermissionAuthorizationHandler 后直接查询它。也许最简单的解决方案就是根本不使用 AspNetRoleClaims 表,而是使用我自己的自定义 Permissions 表并从 PermissionAuthorizationHandler 中获取这些,如上所示。
    • @user1750537 是的,你可以。一种选择是创建自己的 IUserClaimsPrincipalFactory 并将其注册到 DI 容器中:github.com/aspnet/Identity/blob/dev/src/…。或者,您也可以调整您的 CreateTicketAsync 方法以删除角色声明。
    • 我已经通过覆盖 UserClaimsPrincipalFactory 成功地从 cookie 中删除了角色声明。在原始问题中查看我更新的 cmets。
    猜你喜欢
    • 1970-01-01
    • 2017-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-01
    • 1970-01-01
    • 2017-06-24
    • 1970-01-01
    相关资源
    最近更新 更多