Blazor wasm 获取额外信息并添加到用户声明中
Blazor wasm get additional information and add to user claims
我正在使用 identityserver4 进行身份验证,它的布局如下:identity server4 -> Web Api -> Blazor WASM 客户端(独立)。一切都经过验证并且运行良好。我一直将经过身份验证的用户声明发送到 wasm 客户端。
我现在正在尝试添加更多直接来自数据库的声明。我本可以将声明添加到 identityserver 令牌,但令牌变得太大(> 2kb),然后 identityserver 停止工作。显然这是一个已知问题。
所以我想建立授权并试图保持来自 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("本地", 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 客户端相同的构建器。
我如何让它工作?
您可以创建 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;
}
}
保持访问令牌较小,并且只包含通过 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;
}
}
重要的是保持应用程序的低复杂性,而不是试图让它变得比必须的更难。只为让系统更容易推理!
我正在使用 identityserver4 进行身份验证,它的布局如下:identity server4 -> Web Api -> Blazor WASM 客户端(独立)。一切都经过验证并且运行良好。我一直将经过身份验证的用户声明发送到 wasm 客户端。 我现在正在尝试添加更多直接来自数据库的声明。我本可以将声明添加到 identityserver 令牌,但令牌变得太大(> 2kb),然后 identityserver 停止工作。显然这是一个已知问题。
所以我想建立授权并试图保持来自 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("本地", 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 客户端相同的构建器。
我如何让它工作?
您可以创建 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;
}
}
保持访问令牌较小,并且只包含通过 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;
}
}
重要的是保持应用程序的低复杂性,而不是试图让它变得比必须的更难。只为让系统更容易推理!