JWT-Identity

JWT-Identity

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

其中包含属性:

  • Expires:令牌过期时间

  • Issuer:令牌发行者

  • Audience:令牌的受众

  • SigningCredentials:令牌的签名凭据

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

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


HttpContext.User.Identity.Name

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 配置令牌验证参数
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,

1
2
3
4
5
6
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中的所有声明

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

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

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

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

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

IHttpContextAccessor

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

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

注册IHttpContextAccessor
1
builder.Services.AddHttpContextAccessor();
注入IHttpContextAccessor
1
2
3
4
5
6
7
8
9
public class Service
{
private readonly IHttpContextAccessor _httpContextAccessor;

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

CurrentUser

在项目中,注册一个CurrentUser服务(Scope范围),在每次HTTP请求时,将httpContextAccessor中的用户信息保存到这个服务的属性中,当需要使用CurrentUser信息时,注入此服务即可



JWT-Identity
http://blog.170827.xyz/2024/12/04/JWT-Identity/
作者
XIAOBAI
发布于
2024年12月4日
许可协议