OpenIdConnect access_token大小和访问声明服务器端 [英] OpenIdConnect access_token size and accessing claims server side

查看:82
本文介绍了OpenIdConnect access_token大小和访问声明服务器端的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在这里围绕几个概念进行探讨,但是我不希望这个问题过于笼统-基本上,我们试图做的是使用角色声明作为权限来锁定我们的API,但是我发现access_token变得太大.

I am trying to wrap my head around several concepts here but I don't want this question to be too broad - basically what we are trying to do is use role claims as permissions to lock down our API but I am finding that the access_token is becoming too big.

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

We are using OpenIddict and ASP.NET Identity 3 on the server side. We have implemented the default AspNetRoleClaims table to store our claims for each role - using them as permissions.

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

We lock down our API endpoints using custom policy based claims authorization as shown here:

基于自定义策略的授权

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

The main issue I am finding is that our access_token containing our claims is becoming very large. We are attempting to make the ClaimType and Value to be very small in the database to make the claims footprint smaller. We have a basic CRUD type permission scheme, so for each "module" or screen in our SPA client app, there are 4 permissions. The more modules we add to our application, the more the claims are growing in the access_token and our Authorization Bearer header is becoming very large. I am worried about this becoming not very scalable as the app grows.

因此,声明被嵌入到access_token中,并且当我命中被自定义策略锁定的端点时,就这样...

So the claims are embedded in the access_token and when I hit my endpoint that is locked down with a custom Policy like this...

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

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

I can then access my ASP.NET Identity User and User.Claims in the AuthorizationHandler.

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

Sorry in advance if this is an obvious question - but I am wondering - in order to get the Custom Policy Based Authorization to work - does it absolutely require the claims to be in either the id_token or the access_token in order to call the handler?

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

If I remove the claims from the access_token, then my AuthorizationHandler code does not get hit and I cannot access my endpoint that is locked down with my custom Policy.

我想知道是否可以使用自定义声明策略,但是具有在授权处理程序中检查声明的实际代码,以便声明不随每个HTTP请求传递,而是从服务器获取.授权cookie或来自数据库.

I am wondering if it is possible to use a custom claims policy but have the actual code that checks for the Claims inside the Authorization handler, so that the claims are not passed with each HTTP request, but are fetched server side from the Authorization cookie or from the database.

*更新*

Pintpoint使用授权处理程序的答案以及有关如何从cookie中删除其他角色声明的注释,实现了我一直在寻找的目标.

Pintpoint's answer using Authorization handlers along with the comment on how to remove additional role claims from the cookie achieved just what I was looking for.

以防万一,这是重写UserClaimsPrincipalFactory并防止将角色声明写入Cookie的代码. (由于权限,我有许多角色声明,并且cookie和请求标头变得太大了)

In case this helps anyone else - here is the code to override the UserClaimsPrincipalFactory and prevent the role claims from being written to the cookie. (I had many role claims as permissions and the cookie(s) and request headers were becoming too large)

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

推荐答案

我想知道是否可以使用自定义声明策略,但是具有在授权处理程序中检查声明的实际代码,以便声明不随每个HTTP请求传递,而是从服务器获取.授权cookie或来自数据库.

I am wondering if it is possible to use a custom claims policy but have the actual code that checks for the Claims inside the Authorization handler, so that the claims are not passed with each HTTP request, but are fetched server side from the Authorization cookie or from the database.

这绝对有可能.这是您可以执行的操作:

It's definitely possible. Here's how you could do that:

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

这篇关于OpenIdConnect access_token大小和访问声明服务器端的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆