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

使用RefreshToken生成新AccessToken后ClaimTypes.NameIdentifier返回null的解决方法

问题解决思路及代码修改

问题根源在于刷新Access Token后,重新生成的ClaimsPrincipal仅包含了令牌相关的Claim,缺少用户ID对应的ClaimTypes.NameIdentifier,导致后续获取用户ID时返回null。以下是两种可行的解决方式:

方式一:从新生成的Access Token中解析用户ID

利用JWT解析工具从新的Access Token中提取用户ID,添加到Claims列表中:

  1. 确保项目已引入System.IdentityModel.Tokens.Jwt包
  2. 修改AuthorizedHttpClientHandler中刷新成功后的Claims构建逻辑:
if (newToken != null)
{
    // 解析新的Access Token获取用户ID
    var jwtHandler = new JwtSecurityTokenHandler();
    var jwtToken = jwtHandler.ReadJwtToken(newToken.AccessToken);
    var userId = jwtToken.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value 
                 // 如果你的JWT用的是标准sub字段,替换成下面的判断
                 // ?? jwtToken.Claims.FirstOrDefault(c => c.Type == JwtRegisteredClaimNames.Sub)?.Value;

    if (string.IsNullOrWhiteSpace(userId))
    {
        // 处理用户ID为空的异常情况,比如登出用户
        await _httpContextAccessor.HttpContext.SignOutAsync(JwtBearerDefaults.AuthenticationScheme);
        _httpContextAccessor.HttpContext.Response.Redirect("/Login/Index");
        await _httpContextAccessor.HttpContext.Response.CompleteAsync();
        return response;
    }

    var claims = new List<Claim>
    {
        new Claim("DirimOpsManagement", newToken.AccessToken),
        new Claim("RefreshToken", newToken.RefreshToken),
        new Claim(ClaimTypes.NameIdentifier, userId) // 添加用户ID Claim
    };

    // 后续SignIn逻辑保持不变
    var claimsIdentity = new ClaimsIdentity(claims, JwtBearerDefaults.AuthenticationScheme);
    var authProperties = new AuthenticationProperties
    {
        IsPersistent = true
    };

    var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);

    await _httpContextAccessor.HttpContext.SignInAsync(JwtBearerDefaults.AuthenticationScheme, claimsPrincipal, authProperties);
    _httpContextAccessor.HttpContext.User = claimsPrincipal;

    request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", newToken.AccessToken);
    response = await base.SendAsync(request, cancellationToken);
}

方式二:修改Refresh Token接口,直接返回用户ID

这种方式无需解析JWT,更高效,需要调整接口返回模型和逻辑:

  1. 更新JwtResponseModel,添加用户ID字段:
public class JwtResponseModel
{
    public string AccessToken { get; set; }
    public string RefreshToken { get; set; }
    public string UserId { get; set; } // 新增用户ID字段
    // 其他已有字段...
}
  1. 修改Refresh Token命令处理逻辑,返回用户ID:
public async Task<RefreshTokenResponse> Handle(UpdateRefreshTokenCommand request, CancellationToken cancellationToken)
{
    // 原有逻辑保持不变...

    var user = await _userManager.FindByIdAsync(existingToken.UserID);
    if (user == null)
    {
        throw new Exception("User not found.");
    }

    var accessToken = _jwtTokenGenerator.GenerateToken(new JwtTokenRequest
    {
        Id = user.Id,
        Username = user.UserName,
        Email = user.Email
    });

    existingToken.Token = _jwtTokenGenerator.GenerateRefreshToken();
    await _unitOfWork.RefreshTokens.UpdateAsync(existingToken);
    await _unitOfWork.SaveChangesAsync(cancellationToken);

    // 返回时包含用户ID
    return new RefreshTokenResponse
    {
        AccessToken = accessToken.AccessToken,
        AccessTokenExpires = accessToken.Expires,
        RefreshToken = existingToken.Token,
        RefreshTokenExpires = existingToken.Expires,
        UserId = user.Id // 新增返回用户ID
    };
}
  1. 修改AuthorizedHttpClientHandler中的逻辑,直接使用返回的用户ID:
if (newToken != null)
{
    if (string.IsNullOrWhiteSpace(newToken.UserId))
    {
        // 异常处理
        await _httpContextAccessor.HttpContext.SignOutAsync(JwtBearerDefaults.AuthenticationScheme);
        _httpContextAccessor.HttpContext.Response.Redirect("/Login/Index");
        await _httpContextAccessor.HttpContext.Response.CompleteAsync();
        return response;
    }

    var claims = new List<Claim>
    {
        new Claim("DirimOpsManagement", newToken.AccessToken),
        new Claim("RefreshToken", newToken.RefreshToken),
        new Claim(ClaimTypes.NameIdentifier, newToken.UserId) // 添加用户ID Claim
    };

    // 后续SignIn逻辑保持不变...
}

注意事项

  • 确保ClaimTypes.NameIdentifier与你初始登录时生成用户ID Claim的类型一致,如果初始用的是JWT标准字段sub(即JwtRegisteredClaimNames.Sub),则需要对应调整解析或添加Claim时的类型。
  • 添加异常处理逻辑,避免用户ID为空时出现空引用异常。

内容的提问来源于stack exchange,提问作者Gökmen Ada

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 15:00:03