【问题标题】:Blazor wasm get additional information and add to user claimsBlazor wasm 获取更多信息并添加到用户声明中
【发布时间】:2021-06-19 15:11:29
【问题描述】:

我正在使用 identityserver4 进行身份验证,它的布局如下:identity server4 -> Web Api -> Blazor WASM Client(Standalone)。一切都经过身份验证并且运行良好。我一直得到经过身份验证的用户声明到 wasm 客户端。 我现在正在尝试添加更多直接来自数据库的声明。我本可以将声明添加到 identityserver 令牌,但令牌变得太大(> 2kb),然后 identityserver 停止工作。显然这是一个已知问题。

所以我想建立授权并尽量保持来自身份服务器的 jwt 令牌很小。

在 program.cs 文件中,我有一个这样的 http 客户端

builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });

builder.Services.AddOidcAuthentication(options =>// 在此处配置您的身份验证提供程序选项。 // 更多信息见https://aka.ms/blazor-standalone-auth //builder.Configuration.Bind("Local", options.ProviderOptions); ...提供者选项

            options.ProviderOptions.ResponseType = "code";
            options.UserOptions.RoleClaim = "role";                
        }).AddAccountClaimsPrincipalFactory<CustomAccountClaimsPrincipalFactory>();

await builder.Build().RunAsync();

在文件 CustomAccountClaimsPrincipalFactory 我有这个

  public class CustomAccountClaimsPrincipalFactory
: AccountClaimsPrincipalFactory<RemoteUserAccount>
{
    private const string Planet = "planet";

    [Inject]
    public HttpClient Http { get; set; }

    public CustomAccountClaimsPrincipalFactory(IAccessTokenProviderAccessor accessor)
        : base(accessor) {            
    }

    public async override ValueTask<ClaimsPrincipal> CreateUserAsync(
        RemoteUserAccount account,
        RemoteAuthenticationUserOptions options)
    {
        var user = await base.CreateUserAsync(account, options);

        if (user.Identity.IsAuthenticated)
        {
            var identity = (ClaimsIdentity)user.Identity;
            var claims = identity.Claims.Where(a => a.Type == Planet);
            if (!claims.Any())
            {
                identity.AddClaim(new Claim(Planet, "mars"));
            }

            //get user roles
           
            //var url = $"/Identity/users/112b7de8-614f-40dc-a9e2-fa6e9d2bf85a/roles";                
            var dResources = await Http.GetFromJsonAsync<List<somemodel>>("/endpoint");
            foreach (var item in dResources)
            {
                identity.AddClaim(new Claim(item.Name, item.DisplayName));
            }


        }
        return user;
    }
}

这不起作用,因为调用它时 httpclient 不是 biolt 并且 http 客户端使用构建基本 http 客户端的相同构建器。

我如何让它工作?

【问题讨论】:

    标签: api blazor identityserver4 webassembly


    【解决方案1】:

    您可以创建一个IProfileService 并根据需要对其进行自定义:

    
    var builder = services.AddIdentityServer(options =>
    ...
                    .AddProfileService<IdentityProfileService>();
    
        public class IdentityProfileService : IProfileService
        {
    
            private readonly IUserClaimsPrincipalFactory<ApplicationUser> _claimsFactory;
            private readonly UserManager<ApplicationUser> _userManager;
    
            public IdentityProfileService(IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory, UserManager<ApplicationUser> userManager)
            {
                _claimsFactory = claimsFactory;
                _userManager = userManager;
            }
    
            public async Task GetProfileDataAsync(ProfileDataRequestContext context)
            {
                var sub = context.Subject.GetSubjectId();
                var user = await _userManager.FindByIdAsync(sub);
                if (user == null)
                {
                    throw new ArgumentException("");
                }
    
                var principal = await _claimsFactory.CreateAsync(user);
                var claims = principal.Claims.ToList();
    
                //Add more claims like this
                //claims.Add(new System.Security.Claims.Claim("MyProfileID", user.Id));
    
                context.IssuedClaims = claims;
            }
    
            public async Task IsActiveAsync(IsActiveContext context)
            {
                var sub = context.Subject.GetSubjectId();
                var user = await _userManager.FindByIdAsync(sub);
                context.IsActive = user != null;
            }
        }
    

    【讨论】:

    • 看起来这可能最终成为答案,只需要在将其标记为接受的答案之前进行澄清。 Brock Allen 指出,在身份验证令牌中传递的声明不应“大”。因为浏览器最终会阻止正在创建的 cookie 大小的大小。根据他的建议,我试图使身份验证 cookie 保持较小并通过最少数量的声明,但是一旦用户通过身份验证,然后进行 db 调用并获取角色声明以构建屏幕级 crud 授权。使用 Iprofileservice 会保持 auth jwt 令牌小吗?
    • @PicBuilder 我没有测试过尺寸。我必须添加自定义租户声明,我就是这样做的。有些人正在附加 base64 图像/缩略图等作为声明,我认为这是他所暗示的那种大声明。如果它不是身份验证或授权的一部分,我将使用 API 服务。
    • @PicBuilder 您使用的“Planet”代码来自我的 git 存储库。我这里有更新版本github.com/BrianLParker/AuthApp。然而,这两个 repos 都不使用 ID4。
    • 我想我并没有想到图像,比如 Brock 所暗示的。但是现在在阅读了您的回复之后,这可能是他在说什么。所以这非常有帮助,并且将其标记为已接受的答案。我确实有大约 60 个屏幕级别的授权声明,但这不会接近图像的大小。但是所有声明都由 identityserver4 加密,所以接下来我会密切注意 cookie 的大小。
    【解决方案2】:

    保持访问令牌较小,仅包含通过 JwtBearer 身份验证步骤所需的声明。

    然后在接收访问令牌的 API 中,您可以简单地创建一个授权策略来查找用户的其他声明并评估他是否具有访问权限。

    您可以在简单的策略定义或更高级的授权处理程序中执行此操作,如下面的代码:

     public class CheckIfAccountantHandler : AuthorizationHandler<CanViewReportsRequirement>
    {
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                       CanViewReportsRequirement requirement)
        {
            
          bool result = CallTheCheckIfAccountantService();
    
          if(result)
                    context.Succeed(requirement);
    
            return Task.CompletedTask;
        }
    }
    

    一个样本需求可以定义为:

    public class CanViewReportsRequirement : IAuthorizationRequirement
    {
        public int StartHour { get; }
        public int EndHour { get; }
    
        public CanViewReportsRequirement(int startHour, int endHour)
        {
            StartHour = startHour;
            EndHour = endHour;
        }
    }
    

    重要的是保持应用程序的复杂性较低,而不是试图让它变得比它必须的更难。只是为了让系统易于推理!

    【讨论】:

      猜你喜欢
      • 2021-07-20
      • 1970-01-01
      • 2020-06-30
      • 1970-01-01
      • 2021-03-11
      • 1970-01-01
      • 2012-12-24
      • 2015-04-29
      • 1970-01-01
      相关资源
      最近更新 更多