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;
}
}