JWT-Identity

JWT-Identity

我们在签发Token时,通过SecurityTokenDescriptor对象来描述令牌的属性

其中包含属性:

  • Expires:令牌过期时间

  • Issuer:令牌发行者

  • Audience:令牌的受众

  • SigningCredentials:令牌的签名凭据

除了这四个属性外,还有ClaimsIdentity用来作为用户的标识

在这个对象中,可以包含一个Claim类型的数组,其中可以创建多个Claim对象


HttpContext.User.Identity.Name

HttpContext.User.Identity.Name中的内容默认从ClaimTypes.Name(约定大于配置)

Subject = new ClaimsIdentity(new Claim[]
{
    new Claim(ClaimTypes.Name, userId) 
}),
NameClaimType

还有一种手动配置映射的方式,就是在JWT的验证TokenValidationParameters(配置令牌参数)中,指定HttpContext.User.Identity.Name的映射Claim

// 配置令牌验证参数
options.TokenValidationParameters = new TokenValidationParameters
{
    // 是否验证签名密钥,确保令牌是由正确的密钥签名的
    ValidateIssuerSigningKey = true,

    // 提供用于验证令牌的密钥
    // 因为SymmetricSecurityKey方法需求的是一个字节数组,所以在此调用Encoding.ASCII.GetBytes将字符串转化
    IssuerSigningKey = new SymmetricSecurityKey(
        Encoding.ASCII.GetBytes(builder.Configuration["Jwt:Key"] ??
                                throw new InvalidOperationException())),

    // 是否验证发行者
    ValidateIssuer = true,

    // 发行者值
    ValidIssuer = builder.Configuration["Jwt:Issuer"],

    // 是否验证受众
    ValidateAudience = true,

    // 受众值
    ValidAudience = builder.Configuration["Jwt:Audience"],
    
    // 显式设置 NameClaimType 为 ClaimTypes.NameIdentifier
    NameClaimType = ClaimTypes.NameIdentifier
};

ClaimTypes

相同的,我们可以利用包含标准声明的常量类ClaimTypes中的一些常量来新建Claim,

Subject = new ClaimsIdentity(new Claim[]
{
    new Claim(ClaimTypes.NameIdentifier, userId),
    new Claim(ClaimTypes.Name, userName),
    new Claim(ClaimTypes.Role, role),
}),

以下是一些常用的成员

  • Name (http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name):
    • 用户的可读名称或显示名称。
  • NameIdentifier (http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier):
    • 用户的唯一标识符(类似于 sub)。
  • Role (http://schemas.microsoft.com/ws/2008/06/identity/claims/role):
    • 用户的角色。
  • Email (http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress):
    • 用户的电子邮件地址。
  • Upn (http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn):
    • 用户主体名称(User Principal Name),通常用于 Windows 环境。
  • GivenName (http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname):
    • 用户的名字(名)。
  • Surname (http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname):
    • 用户的姓氏(姓)。
  • Birthdate (http://schemas.xmlsoap.org/ws/2005/05/identity/claims/birthdate):
    • 用户的出生日期。
  • Country (http://schemas.xmlsoap.org/ws/2005/05/identity/claims/country):
    • 用户所在的国家或地区。
  • MobilePhone (http://schemas.xmlsoap.org/ws/2005/05/identity/claims/mobilephone):
    • 用户的手机号码。

HttpContext.User.Claims

解析成功后,我们通过HttpContext.User.Claims就可以遍历出JWT中的所有声明

foreach (var claim in HttpContext.User.Claims)
{
    Console.WriteLine($"{claim.Type}: {claim.Value}");
}
获取指定声明

也可以使用Linq来获取指定的Claim声明

var userRole = HttpContext.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Role)?.Value;
FindFirstValue

FindFirstValue 是一个便捷的方法,它可以直接返回指定类型声明的值

string? userId = HttpContext?.User?.FindFirstValue("sub");

IHttpContextAccessor

在 ASP.NET Core 中,直接在类中调用 HttpContext.User 属性并不是最佳实践

如果你确实需要在非控制器类(例如服务层、业务逻辑层)中访问 HttpContext.User 或其他 HttpContext 相关的信息,推荐的方式是通过 IHttpContextAccessor 进行依赖注入。这样可以确保你的代码保持解耦和可测试性。

注册IHttpContextAccessor
builder.Services.AddHttpContextAccessor();
注入IHttpContextAccessor
public class Service
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public YourService(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }
}