生成token#
包安装#
Microsoft.AspNetCore.Authentication.JwtBearer
Microsoft.IdentityModel.Tokens
System.IdentityModel.Tokens.Jwt
先创建下面几个文件
PolicyContstants用于JWT的Policy常量
AuthenticationData用于用户登录
UserData用于用户登录后存储信息
namespace ContactSMS.WebAPI.Constants
{
public static class PolicyContstants
{
public const string MustHaveEmployeeId = "MustHaveEmployeeId";
public const string MustBeTheOwner = "MustBeTheOwner";
}
}
public record AuthenticationData(string? UserName, string Password);
public record UserData(long UserId, string UserName, string Title, string EmployeeId);
新建AuthenticationController控制器
这里只是示例,没有和数据交互
namespace ContactSMS.WebAPI.Controllers;
[Route("api/[controller]")]
[ApiController]
public class AuthenticationController : ControllerBase
{
private readonly IConfiguration _configuration;
public record AuthenticationData(string? UserName, string Password);
public record UserData(long UserId, string UserName, string Title, string EmployeeId);
public AuthenticationController(IConfiguration configuration)
{
this._configuration = configuration;
}
//[HttpPost("token")]: 指定这是一个处理 HTTP POST 请求的方法,路径为 api/authentication/token。
//[AllowAnonymous]: 该属性允许未经身份验证的请求访问此方法。
//请求体绑定: 使用 [FromBody] 绑定请求体中的数据到 AuthenticationData 类型的对象。
//调用 ValidateCredentials 方法: 验证用户的凭据。
//生成令牌: 如果验证成功,调用 GenerateToken 方法生成 JWT 令牌。
//返回结果: 返回一个包含 JWT 令牌的 Ok 响应。如果验证失败,则调用 Unauthorized() 方法返回未授权状态。
[HttpPost("token")]
[AllowAnonymous]
public ActionResult<string> Authentication([FromBody] AuthenticationData data)
{
var user = ValidateCredentials(data);
if (user is null)
{
Unauthorized();
}
var token = GenerateToken(user!);
return Ok(token);
}
//创建密钥: 使用从配置文件中读取的密钥创建一个对称密钥。
//创建签名凭证: 创建一个签名凭证,用于生成 JWT 令牌。
//创建声明列表: 为 JWT 令牌添加声明(claims),包括用户 ID、用户名、标题和员工 ID。
//创建 JWT 令牌: 使用 JwtSecurityToken 类创建一个 JWT 令牌,指定发行人(issuer)、受众(audience)、声明列表、有效期和签名凭证。
//序列化令牌: 使用 JwtSecurityTokenHandler 类将 JwtSecurityToken 序列化为字符串。
private string GenerateToken(UserData user)
{
var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(
this._configuration.GetSection("Authentication:SecretKey").Value!));
var signingCredentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha512Signature);
List<Claim> claims = new();
claims.Add(new(JwtRegisteredClaimNames.Sub, user.UserId.ToString()));
claims.Add(new(JwtRegisteredClaimNames.UniqueName, user.UserName));
claims.Add(new("title", user.Title));
claims.Add(new("employeeId", user.EmployeeId));
var token = new JwtSecurityToken(
this._configuration.GetSection("Authentication:Issuer").Value!,
this._configuration.GetSection("Authentication:Audience").Value!,
claims,
DateTime.UtcNow,
DateTime.UtcNow.AddMinutes(1),
signingCredentials);
return new JwtSecurityTokenHandler().WriteToken(token);
}
private UserData? ValidateCredentials(AuthenticationData data)
{
//示例
if (CompareValues(data.UserName, "xuzizheng") &&
CompareValues(data.Password, "123456"))
{
return new UserData(1, data.UserName!, "Business owner", "E001");
}
if (CompareValues(data.UserName, "zizheng") &&
CompareValues(data.Password, "123456"))
{
return new UserData(1, data.UserName!, "rider owner", "E002");
}
return null;
}
private bool CompareValues(string? actual, string expected)
{
if (actual is not null)
{
if (actual.Equals(expected, StringComparison.InvariantCultureIgnoreCase))
{
return true;
}
}
return false;
}
}
Program.cs配置JWT相关
builder.Services.AddAuthorization(options =>
{
// 定义一个授权策略,名为 MustHaveEmployeeId
options.AddPolicy(PolicyContstants.MustHaveEmployeeId, policy =>
{
// 该策略要求用户必须有一个名为 "employeeId" 的声明(claim)
policy.RequireClaim("employeeId");
});
// 定义另一个授权策略,名为 MustBeTheOwner
options.AddPolicy(PolicyContstants.MustBeTheOwner, policy =>
{
// 该策略要求用户必须有一个名为 "title" 的声明,并且该声明的值必须是 "Business owner"
policy.RequireClaim("title", "Business owner");
});
// 设置一个全局的回退策略
// 如果没有特定的策略被应用,那么将使用这个默认策略
// 这个策略要求用户必须通过身份验证(即必须是经过身份验证的用户)
options.FallbackPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
});
各部分详细说明#
builder.Services.AddAuthorization(options => { ... });:这是向依赖注入容器中添加授权配置的入口。它提供一个
options参数来定义授权策略。
options.AddPolicy(PolicyContstants.MustHaveEmployeeId, policy => { ... });:这里定义了一个名为
MustHaveEmployeeId的自定义授权策略。这个策略要求用户必须有一个声明(Claim),声明的键为
"employeeId"。如果用户没有这个声明,就无法通过授权。
options.AddPolicy(PolicyContstants.MustBeTheOwner, policy => { ... });:这里定义了另一个名为
MustBeTheOwner的自定义授权策略。这个策略要求用户必须有一个
"title"声明,并且这个声明的值必须是"Business owner"。只有满足这个条件的用户才能通过授权。
options.FallbackPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();:这是一个全局的回退策略,称为 Fallback Policy。
如果没有在特定路由或控制器上明确应用授权策略,ASP.NET Core 将使用这个回退策略。
这个回退策略要求用户必须是经过身份验证的(即,用户必须登录)。如果用户未认证,他们将无法访问。
主要用途
自定义授权策略:通过
AddPolicy方法,开发者可以定义复杂的访问控制规则,例如要求用户拥有某些特定的声明(claims)或者角色。回退策略:通过设置
FallbackPolicy,可以确保所有未明确指定策略的请求都要求用户身份验证。
builder.Services.AddAuthentication("Bearer")
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new()
{
// 是否验证令牌的签发者
ValidateIssuer = true,
// 是否验证令牌的接收者(受众)
ValidateAudience = true,
// 是否验证签名密钥
ValidateIssuerSigningKey = true,
// 期望的签发者 (Issuer)
ValidIssuer = builder.Configuration.GetSection("Authentication:Issuer").Value,
// 期望的受众 (Audience)
ValidAudience = builder.Configuration.GetSection("Authentication:Audience").Value,
// 用于验证签名密钥的 SymmetricSecurityKey
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration.GetSection("Authentication:SecretKey").Value!))
};
});
各部分详细说明#
builder.Services.AddAuthentication("Bearer"):这是添加身份验证服务的入口,参数
"Bearer"表示使用 Bearer Token 作为认证方案。Bearer Token 是 HTTP 认证的一种方式,通常用于 JWT。这里指明 ASP.NET Core 应用程序将在请求中查找 Bearer Token 来认证用户。
AddJwtBearer(options => { ... }):这个方法为应用程序配置 JWT Bearer Token 身份验证。
options是配置 JWT 验证的参数。
TokenValidationParameters:这是 JWT 验证时使用的参数配置,用于指定验证规则。以下是各个参数的作用:
ValidateIssuer = true:该项表示是否要验证 JWT 令牌的签发者(Issuer)。签发者是指生成令牌的服务器。
在验证过程中,令牌的签发者必须与配置中的
ValidIssuer相匹配。
ValidateAudience = true:该项表示是否要验证令牌的接收者(受众,Audience)。受众是指谁可以接收和使用该令牌。
在验证过程中,令牌的受众必须与配置中的
ValidAudience相匹配。
ValidateIssuerSigningKey = true:该项表示是否要验证签名密钥,确保令牌是由合法方签署的,并且没有被篡改。
签名密钥使用
IssuerSigningKey参数进行验证。
ValidIssuer:期望的 JWT 令牌签发者。它从应用程序的配置文件(例如
appsettings.json)中获取"Authentication:Issuer"的值。这是生成令牌的服务器的标识,令牌的
iss字段必须与此值匹配。
ValidAudience:期望的 JWT 令牌受众,指定哪些客户端可以使用该令牌。它从配置文件中获取
"Authentication:Audience"的值。令牌的
aud字段必须与此值匹配。
IssuerSigningKey:用于验证 JWT 令牌签名的密钥。这里使用对称加密算法,通过
SymmetricSecurityKey和SecretKey生成。SecretKey是一个用于生成签名密钥的字符串,它通常存储在配置文件中。
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration.GetSection("Authentication:SecretKey").Value!))
这行代码从配置中读取
"Authentication:SecretKey",并将其转化为字节数组来构建加密密钥。这个密钥用于签名和验证 JWT。
主要用途#
身份验证:这段代码配置了 JWT 令牌验证规则,用于验证客户端请求中的 JWT 令牌是否合法。
安全性:通过验证 Issuer、Audience 和签名密钥,确保 JWT 令牌的完整性,防止令牌被伪造或篡改。
Bearer Token 认证方案:整个配置为 API 提供了基于 JWT 的 Bearer Token 身份验证,常用于无状态的 Web 应用程序中。
新建UserController控制器如何使用JWT特性
namespace ContactSMS.WebAPI.Controllers;
[Route("api/[controller]")]
[ApiController]
public class UserController : ControllerBase
{
//[Authorize(Policy = PolicyContstants.MustHaveEmployeeId)]
//这个属性用于授权访问,确保用户必须拥有特定的 employeeId 声明(Claim)。只有具有这个声明的用户才被允许访问此 API。
//这里使用了自定义的授权策略 PolicyContstants.MustHaveEmployeeId,这个策略在应用程序中定义,要求 employeeId 声明。
//[Authorize(Policy = PolicyContstants.MustBeTheOwner)]
//这也是一个授权属性,确保用户必须拥有一个特定的 title 声明(比如,用户的职位是“Business owner”)。这里使用了另一个自定义的授权策略 PolicyContstants.MustBeTheOwner。
//用户只有同时具备这两个策略的要求才能访问这个方法。
[HttpGet("{id}")]
[Authorize(Policy = PolicyContstants.MustHaveEmployeeId)]
[Authorize(Policy = PolicyContstants.MustBeTheOwner)]
public string Get(int id)
{
return id;
}
}