You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在.NET中使用IClaimsTransformation接口添加/修改声明并持久化?

用.NET的IClaimsTransformation替代ClaimsAuthenticationManager实现声明自定义与持久化

1. 自定义IClaimsTransformation实现类

IClaimsTransformation是.NET Core及后续版本中替代ClaimsAuthenticationManager的核心接口,仅包含TransformAsync方法,用于在请求流程中处理用户声明。实现时需注意克隆原始ClaimsIdentity(默认主体可能为只读),再执行添加/修改逻辑:

public class CustomClaimsTransformer : IClaimsTransformation
{
    public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
    {
        // 克隆身份对象,避免修改原始只读实例
        var identity = (ClaimsIdentity)principal.Identity.Clone();
        bool isModified = false;

        // 示例1:添加自定义声明(仅当不存在时)
        if (!identity.HasClaim(c => c.Type == "SystemAccessLevel"))
        {
            identity.AddClaim(new Claim("SystemAccessLevel", "Full"));
            isModified = true;
        }

        // 示例2:修改现有声明
        var emailClaim = identity.FindFirst(ClaimTypes.Email);
        if (emailClaim != null && !emailClaim.Value.EndsWith("@company.com"))
        {
            identity.RemoveClaim(emailClaim);
            identity.AddClaim(new Claim(ClaimTypes.Email, $"{emailClaim.Value}@company.com"));
            isModified = true;
        }

        return new ClaimsPrincipal(identity);
    }
}

2. 注册服务到依赖注入容器

在Program.cs(.NET 6+)或Startup.cs中注册自定义实现,框架会自动在认证流程中调用该服务:

// .NET 6+ Program.cs
builder.Services.AddScoped<IClaimsTransformation, CustomClaimsTransformer>();

3. 实现声明跨请求持久化

IClaimsTransformation默认每次请求都会执行,若仅在内存中修改声明,下次请求会重新从认证票据(Cookie/JWT)读取原始数据。需根据认证方式做持久化处理:

场景1:Cookie认证

若使用Cookie认证,修改声明后需重新签发Cookie,将更新后的身份写入客户端:

public class CustomClaimsTransformer : IClaimsTransformation
{
    private readonly IAuthenticationService _authService;
    private readonly IHttpContextAccessor _httpContextAccessor;

    public CustomClaimsTransformer(IAuthenticationService authService, IHttpContextAccessor httpContextAccessor)
    {
        _authService = authService;
        _httpContextAccessor = httpContextAccessor;
    }

    public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
    {
        var identity = (ClaimsIdentity)principal.Identity.Clone();
        bool isModified = false;

        // 执行声明添加/修改逻辑...

        if (isModified)
        {
            var updatedPrincipal = new ClaimsPrincipal(identity);
            // 重新签发Cookie,持久化修改后的声明
            await _authService.SignInAsync(
                _httpContextAccessor.HttpContext,
                CookieAuthenticationDefaults.AuthenticationScheme,
                updatedPrincipal);
        }

        return new ClaimsPrincipal(identity);
    }
}

同时需注册IHttpContextAccessor以获取当前请求上下文:

builder.Services.AddHttpContextAccessor();

场景2:JWT认证

JWT为无状态令牌,无法直接修改已签发的Token。需将自定义声明存储到数据库/缓存中,每次请求时从存储读取并添加:

public class CustomClaimsTransformer : IClaimsTransformation
{
    private readonly IUserCustomClaimStore _claimStore;

    public CustomClaimsTransformer(IUserCustomClaimStore claimStore)
    {
        _claimStore = claimStore;
    }

    public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
    {
        var identity = (ClaimsIdentity)principal.Identity.Clone();
        var userId = identity.FindFirst(ClaimTypes.NameIdentifier)?.Value;

        if (!string.IsNullOrEmpty(userId))
        {
            // 从数据库读取用户最新自定义声明
            var persistedClaims = await _claimStore.GetUserClaimsAsync(userId);
            foreach (var claim in persistedClaims)
            {
                if (!identity.HasClaim(c => c.Type == claim.Type))
                {
                    identity.AddClaim(claim);
                }
            }
        }

        return new ClaimsPrincipal(identity);
    }
}

后续更新声明时,直接修改数据库中的存储即可,下次请求会自动加载最新数据。

4. 关键注意事项

  • 避免重复添加:每次处理前检查声明是否已存在,防止请求重复添加相同声明。
  • 性能优化:从数据库读取声明时,建议添加缓存(如IDistributedCache),减少数据库查询开销。
  • 只读身份处理:必须克隆原始ClaimsIdentity,直接修改默认主体会抛出只读异常。

内容的提问来源于stack exchange,提问作者Matthew Holmes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 06:27:13