Web API 的 Token 验证是一种用于保护 API 端点的认证和授权机制,它主要通过生成和验证令牌来确保只有授权用户可以访问特定资源。
在 API 项目中安装 JwtBearer 库:Microsoft.AspNetCore.Authentication.JwtBearer
appsettings.json 中添加 JwtConfig 的配置
"JwtConfig": {
"SecretKey": "123456789123456789123456789", // 密钥可以是 guid, 也可以是随便一个字符串
"Issuer": "张三", // 颁发者
"Audience": "张三", // 接收者
"Expired": 30 // 过期时间(30min)
}
在 API 项目中,创建 Jwt 文件夹,同时创建 JwtConfig 类。
public class JwtConfig
{
/// <summary>
/// 密钥
/// </summary>
public string SecretKey { get; set; }
/// <summary>
/// 发布者
/// </summary>
public string Issuer { get; set; }
/// <summary>
/// 接受者
/// </summary>
public string Audience { get; set; }
/// <summary>
/// 过期时间(min)
/// </summary>
public int Expired { get; set; }
/// <summary>
/// 生效时间
/// </summary>
public DateTime NotBefore => DateTime.Now;
/// <summary>
/// 过期时间
/// </summary>
public DateTime Expiration => DateTime.Now.AddMinutes(Expired);
/// <summary>
/// 密钥Bytes
/// </summary>
private SecurityKey SigningKey => new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey));
/// <summary>
/// 加密后的密钥,使用HmacSha256加密
/// </summary>
public SigningCredentials SigningCredentials =>
new SigningCredentials(SigningKey, SecurityAlgorithms.HmacSha256);
/// <summary>
/// 认证使用的密钥
/// </summary>
public SymmetricSecurityKey SymmetricSecurityKey => new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey));
}
在 Jwt 文件夹中继续创建 JwtHelper 类。
public class JwtHelper
{
public JwtConfig JwtConfig { get; set; }
public JwtHelper()
{
}
/// <summary>
/// 添加Jwt服务
/// </summary>
public void AddJwtService(IServiceCollection services)
{
services.AddAuthentication(option =>
{
//认证middleware配置
option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
//Token颁发机构
ValidIssuer = JwtConfig.Issuer,
//颁发给谁
ValidAudience = JwtConfig.Audience,
//这里的key要进行加密
IssuerSigningKey = JwtConfig.SymmetricSecurityKey,
//是否验证Token有效期,使用当前时间与Token的Claims中的NotBefore和Expires对比
ValidateLifetime = true,
ValidateIssuer = true,
ValidateAudience = true,
ValidateIssuerSigningKey = true,
RequireExpirationTime = true,
ClockSkew = TimeSpan.FromSeconds(30) // 缓冲时间,默认5分钟(因为服务器与客户端的时间可能不同步,所以需要设置一个缓冲时间,最终总过期时间=过期时间+缓冲时间)
};
});
}
/// <summary>
/// 返回
/// </summary>
/// <returns></returns>
public string GetJwtToken()
{
var claims = new List<Claim>();
var jwtSecurityToken = new JwtSecurityToken(
JwtConfig.Issuer, // 发布者
JwtConfig.Audience, // 接受者
claims,
JwtConfig.NotBefore, // 生效时间
JwtConfig.Expiration, // 过期时间
JwtConfig.SigningCredentials // 加密后的密钥
);
var token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
token = "Bearer " + token; // 为了更简单的使用 Token,直接把返回的 Token 自带[Bearer ]这个前缀
return token;
}
/// <summary>
/// 返回
/// </summary>
/// <returns></returns>
public string GetJwtToken(List<Claim> claims)
{
var jwtSecurityToken = new JwtSecurityToken(
JwtConfig.Issuer, // 发布者
JwtConfig.Audience, // 接受者
claims,
JwtConfig.NotBefore, // 生效时间
JwtConfig.Expiration, // 过期时间
JwtConfig.SigningCredentials // 加密后的密钥
);
var token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
token = "Bearer " + token; // 为了更简单的使用 Token,直接把返回的 Token 自带[Bearer ]这个前缀
return token;
}
/// <summary>
/// UserModel类Token
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public string GetJwtToken(TUser user)
{
var claims = new List<Claim>() {
new Claim("UserId",user.id.ToString()),
new Claim("UserCode",user.code),
new Claim("UserName",user.name),
new Claim("UserRoleId",user.role_id.ToString()),
};
return GetJwtToken(claims);
}
/// <summary>
/// 获取 Jwt 的信息
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public IEnumerable<Claim> Decode(HttpRequest request)
{
var authorization = request.Headers["Authorization"].ToString();
// 因为我们的Jwt是自带【Bearer 】这个请求头的,所以去掉前面的头
var auth = authorization.Split(" ")[1];
var handler = new JwtSecurityTokenHandler();
//反解密,获取其中的Claims
var payload = handler.ReadJwtToken(auth).Payload;
var claims = payload.Claims;
return claims;
}
/// <summary>
/// 解析得到 User
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public TUser DecodeToUser(HttpRequest request)
{
var claims = Decode(request);
var user = new TUser()
{
id = new Guid(claims.Where(t => t.Type == "UserId").First().Value),
code = claims.Where(t => t.Type == "UserCode").First().Value,
name = claims.Where(t => t.Type == "UserName").First().Value,
role_id = new Guid(claims.Where(t => t.Type == "UserRoleId").First().Value)
};
return user;
}
}
在 ServiceExtensions.cs 类中增加 Jwt 的服务。
/// <summary>
/// Jwt
/// </summary>
/// <param name="service"></param>
/// <param name="config"></param>
public static JwtHelper AddJwt(this IServiceCollection service, IConfiguration config)
{
// 获取 Config
var jwtConfig = new JwtConfig();
config.Bind("JwtConfig", jwtConfig);
var jwtHelper = new JwtHelper()
{
JwtConfig = jwtConfig
};
service.AddSingleton<JwtHelper>(jwtHelper);
return jwtHelper;
}
在 Program.cs 中增加 Jwt 的引用。
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers().AddNewtonsoftJson(options =>
{
options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
});
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddSwaggerExt();
builder.Services.ConfigureCors();
builder.Services.ConfigureMysqlContext(builder.Configuration);
builder.Services.ConfigureRepositoryWrapper();
builder.Services.AddAutoMapper(Assembly.GetExecutingAssembly());
JwtHelper jwtHelper = builder.Services.AddJwt(builder.Configuration);
jwtHelper.AddJwtService(builder.Services);
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseCors("CorsPolicy");
app.UseAuthentication(); // 要在授权之前认证,这个和 [Authorize] 特性有关
app.UseAuthorization();
app.MapControllers();
app.Run();
}
}
在 TUserController 控制器上增加 [Authorize] 特性,该特性指定了控制器中的所有接口,都有 Jwt 认证。
若某个接口需要放开 Jwt 认证,可在接口上使用 [AllowAnonymous] 特性进行标记。
[Authorize] 特性也可单独使用在接口上,只对某个接口指定需要 Jwt 认证。
private JwtHelper jwtHelper;
// 构造函数注入
public TUserController(IRepositoryWrapper repositoryWrapper, ILogger<TUserController> logger,IMapper mapper, JwtHelper jwtHelper)
{
_repositoryWrapper = repositoryWrapper;
_logger = logger;
_mapper = mapper;
this.jwtHelper = jwtHelper;
}
定义用户登录方法
/// <summary>
/// 用户登录
/// </summary>
/// <returns></returns>
[AllowAnonymous]
[HttpGet("login")]
public async Task<IActionResult> Login([FromQuery] TUserParameter userParameter)
{
try
{
var user = await _repositoryWrapper.UserRepository.GetByCode(userParameter.code);
if (user == null) return NotFound("用户不存在");
if(userParameter.code.Equals(user.code) && MD5Encrypt.Encrypt(userParameter.pwd).Equals(user.pwd))
{
var token = jwtHelper.GetJwtToken(new TUser()
{
id = user.id,
code = user.code,
name = user.name,
role_id = user.role_id
});
return Ok(token);
}
else
{
return Unauthorized("用户名或密码错误");
}
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
return StatusCode(500, ex.Message);
}
}
启动 webapi 项目,调用登录方法获取 Token 。
以上步骤成功获取 Token,然后在 TUserController 控制器中增加查询所有用户的方法,同时解析传入的 Token 。
/// <summary>
/// 查询所有用户
/// </summary>
/// <returns></returns>
[HttpGet("getAll")]
public async Task<IActionResult> GetAll()
{
try
{
var operateUser = jwtHelper.DecodeToUser(this.Request);
var users = await _repositoryWrapper.UserRepository.GetAll();
var result = _mapper.Map<IEnumerable<TUserDto>>(users);
return Ok(result);
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
return StatusCode(500,ex.Message);
}
}
运行程序,先调用登录方法获取 Token 。
将 Token 添加到 Swagger 里面。
调用 “查询所有用户” 的方法,成功获取到 Token 中传递的信息。