这里写自定义目录标题
在VS2022环境下 做JWT验证,的记录。
一、建项目
提示:删掉自带的demo
二、JWT验权相关
2.1、nuGet 包
加载:Microsoft.AspNetCore.Authentication.JwtBearer
2.2、JWT设置
改动:appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Jwt": {
"SecretKey": "#&**……&%*&d@DC%TGN&UJ(OL)P:123u^Bdob@OJ&KF2RcAB%ybsoy&2#%^&*(*()))_+.-.apikey",
"Issuer": "MyApi", //颁发者
"Audience": "WebAppAudience", //接收者
"ExpireSeconds": 7200 //过期时间
}
}
改动:Program.cs
#region JWT服务
// 注册JWT服务
builder.Services.AddSingleton(new JwtHelper(builder.Configuration));
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true, //是否验证Issuer
ValidIssuer = builder.Configuration["Jwt:Issuer"], //发行人Issuer
ValidateAudience = false, //是否验证Audience
ValidateIssuerSigningKey = true, //是否验证SecurityKey
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:SecretKey"] ?? "")), //SecurityKey
ValidateLifetime = true, //是否验证失效时间
ClockSkew = TimeSpan.FromSeconds(30), //过期时间容错值,解决服务器端时间不同步问题(秒)
RequireExpirationTime = true,
};
}
);
#endregion
2.3、Common/JwtHelper.cs (JWT逻辑类)
添加:Common/JwtHelper.cs
using Microsoft.IdentityModel.Tokens;
using System;
using System.Diagnostics;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
namespace WebApi.Common
{
/// <summary>
/// 授权JWT类
/// </summary>
public class JwtHelper
{
private readonly IConfiguration _configuration;
/// <summary>
/// Token配置
/// </summary>
/// <param name="configuration"></param>
public JwtHelper(IConfiguration configuration)
{
_configuration = configuration;
}
/// <summary>
/// 创建Token 这里面可以保存自己想要的信息
/// </summary>
/// <param name="username"></param>
/// <param name="mobile"></param>
/// <returns></returns>
public string CreateToken(string username, string mobile)
{
try
{
// 1. 定义需要使用到的Claims
var claims = new[]
{
new Claim("username", username),
new Claim("mobile", mobile),
/* 可以保存自己想要信息,传参进来即可
new Claim("sex", "sex"),
new Claim("limit", "limit"),
new Claim("head_url", "xxxxx")
*/
};
// 2. 从 appsettings.json 中读取SecretKey
var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:SecretKey"]));
// 3. 选择加密算法
var algorithm = SecurityAlgorithms.HmacSha256;
// 4. 生成Credentials
var signingCredentials = new SigningCredentials(secretKey, algorithm);
int ExpireSeconds = int.Parse(_configuration["Jwt:ExpireSeconds"]);//生效时间
// 5. 根据以上,生成token
var jwtSecurityToken = new JwtSecurityToken(
_configuration["Jwt:Issuer"], //颁发者
_configuration["Jwt:ExpireSeconds"], //生效时间
claims, //票据,
DateTime.Now, //当前时间
DateTime.Now.AddSeconds(ExpireSeconds), //过期时间
signingCredentials //Credentials
);
// 6. 将token变为string
var token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
return token;
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// 解密jwt
/// </summary>
/// <param name="jwt"></param>
/// <returns></returns>
public string JwtDecrypt(string jwt)
{
StringBuilder sb = new StringBuilder();
try
{
JwtSecurityTokenHandler tokenHandler = new();
TokenValidationParameters valParam = new();
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:SecretKey"]));
valParam.IssuerSigningKey = securityKey;
valParam.ValidateIssuer = false;
valParam.ValidateAudience = false;
//解密
ClaimsPrincipal claimsPrincipal = tokenHandler.ValidateToken(jwt,
valParam, out SecurityToken secToken);
foreach (var claim in claimsPrincipal.Claims)
{
sb.Append($"{claim.Type}={claim.Value};");
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
return sb.ToString();
}
}
}
2.4、实体类Model/UserInfo.cs
改动:Model/UserInfo.cs
using System.ComponentModel.DataAnnotations;
namespace WebApi.Models
{
public class UserInfo
{
/// <summary>
/// 其中 [Required] 表示非空判断
/// </summary>
[Required]
public string ? UserName { get; set; }
[Required]
public string ? Password { get; set; }
[Required]
public string ? PhoneNumber { get; set; }
}
}
2.5、控制器Controllers/UserInfoController.cs
添加:Controllers/UserInfoController.cs
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using WebApi.Common;
using WebApi.Models;
namespace WebApi.Controllers
{
[Route("[controller]/[action]")]
[ApiController]
public class UserInfoController : ControllerBase
{
private readonly JwtHelper _jwt;
/// <summary>
/// 初始化
/// </summary>
/// <param name="jwtHelper"></param>
/// <param name="redis"></param>
public UserInfoController(JwtHelper jwtHelper)
{
_jwt = jwtHelper;
}
/// <summary>
/// 获取Token
/// </summary>
/// <returns></returns>
[HttpPost]
public IActionResult GetToken(UserInfo user)
{
//参数验证等等....
if (string.IsNullOrEmpty(user.UserName))
{
return Ok("参数异常!");
}
//获取Token
var token = _jwt.CreateToken(user.UserName, user.PhoneNumber ?? "");
//解密后的Token
return Ok(token);
}
/// <summary>
/// 获取自己的详细信息,其中 [Authorize] 就表示要带Token才行
/// </summary>
/// <returns></returns>
[HttpPost]
[Authorize]
public IActionResult GetSelfInfo()
{
//解析token
string ? token = this.Request.Headers["Authorization"];
string _token = token.Replace("Bearer ", "");
string PWToken = _jwt.JwtDecrypt(_token);
//解析出用户名,redis里获取用户信息,并返回
return Ok("授权通过了!");
}
}
}
2.6、运行 F5
三、Redis使用
3.1、nuGet 支持包,安装小皮(phpstudy)
3.2、Redis引入设置
修改:appsettings.json
"Redis": {
"Default": {
"Connection": "127.0.0.1:6379,password=d0d493hGlqePd2Tn", //redis连接地址,端口号,密码
"InstanceName": "local", //实例名
"DefaultDB": "8" //Db8数据库
}
}
修改:Program.cs
#region 配置Redis
// 配置Redis 注入类似于配置数据库连接字符串
var section = builder.Configuration.GetSection("Redis:Default");
// 连接字符串
string? _connectionString = section.GetSection("Connection").Value;
// 实例名称
string? _instanceName = section.GetSection("InstanceName").Value;
// 默认数据库
int _defaultDB = int.Parse(section.GetSection("DefaultDB").Value ?? "0");
builder.Services.AddSingleton(new RedisHelper(_connectionString ?? "", _instanceName ?? "", _defaultDB));
#endregion
添加:Common/RedisHelper.cs
using Microsoft.AspNetCore.Hosting.Server;
using StackExchange.Redis;
using System.Collections.Concurrent;
namespace WebApi.Common
{
/// <summary>
/// redis类
/// </summary>
public class RedisHelper : IDisposable
{
//连接字符串
private string _connectionString;
//实例名称
private string _instanceName;
//默认数据库
private int _defaultDB;
private ConcurrentDictionary<string, ConnectionMultiplexer> _connections;
/// <summary>
/// 初始化
/// </summary>
/// <param name="connectionString"></param>
/// <param name="instanceName"></param>
/// <param name="defaultDB"></param>
public RedisHelper(string connectionString, string instanceName, int defaultDB = 0)
{
_connectionString = connectionString;
_instanceName = instanceName;
_defaultDB = defaultDB;
_connections = new ConcurrentDictionary<string, ConnectionMultiplexer>();
}
/// <summary>
/// 获取ConnectionMultiplexer
/// </summary>
/// <returns></returns>
private ConnectionMultiplexer GetConnect()
{
return _connections.GetOrAdd(_instanceName, p => ConnectionMultiplexer.Connect(_connectionString));
}
/// <summary>
/// 获取redis数据库
/// </summary>
/// <returns></returns>
public IDatabase GetDatabase()
{
return GetConnect().GetDatabase(_defaultDB);
}
/// <summary>
///
/// </summary>
/// <param name="configName"></param>
/// <param name="endPointsIndex"></param>
/// <returns></returns>
public StackExchange.Redis.IServer GetServer(string? configName = null, int endPointsIndex = 0)
{
var confOption = ConfigurationOptions.Parse(_connectionString);
return GetConnect().GetServer(confOption.EndPoints[endPointsIndex]);
}
/// <summary>
///
/// </summary>
/// <param name="configName"></param>
/// <returns></returns>
public ISubscriber GetSubscriber(string? configName = null)
{
return GetConnect().GetSubscriber();
}
/// <summary>
///
/// </summary>
public void Dispose()
{
if (_connections != null && _connections.Count > 0)
{
foreach (var item in _connections.Values)
{
item.Close();
}
}
}
}
}
3.3、Redis应用
Controllers/UserInfoControl.cs
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using WebApi.Common;
using WebApi.Models;
namespace WebApi.Controllers
{
[Route("[controller]/[action]")]
[ApiController]
public class UserInfoController : ControllerBase
{
private readonly JwtHelper _jwt;
private readonly RedisHelper _redis;
/// <summary>
/// 初始化
/// </summary>
/// <param name="jwtHelper"></param>
/// <param name="redis"></param>
public UserInfoController(JwtHelper jwtHelper, RedisHelper redis)
{
_jwt = jwtHelper;
_redis = redis;
}
/// <summary>
/// 获取Token
/// </summary>
/// <returns></returns>
[HttpPost]
public IActionResult GetToken(UserInfo user)
{
//参数验证等等....
if (string.IsNullOrEmpty(user.UserName))
{
return Ok("参数异常!");
}
#region Redis缓存验证
bool ishave = _redis.GetDatabase().KeyExists(user.UserName);//查存
if (ishave)//验证
{
string? Password = _redis.GetDatabase().StringGet(user.UserName);// 从Redis里面取数据
if (user.Password != Password) {
return Ok("密码错误!");
}
}
else//查询后写入
{
_redis.GetDatabase().StringSet(user.UserName, user.Password, TimeSpan.FromSeconds(7200));// 往Redis里面存入数据
}
#endregion Redis缓存验证 end
//获取Token
var token = _jwt.CreateToken(user.UserName, user.PhoneNumber ?? "");
//解密后的Token
return Ok(token);
}
/// <summary>
/// 获取自己的详细信息,其中 [Authorize] 就表示要带Token才行
/// </summary>
/// <returns></returns>
[HttpPost]
[Authorize]
public IActionResult GetSelfInfo()
{
//解析token
string ? token = this.Request.Headers["Authorization"];
string _token = token.Replace("Bearer ", "");
string PWToken = _jwt.JwtDecrypt(_token);
//解析出用户名,redis里获取用户信息,并返回
return Ok("授权通过了!");
}
}
}
Redis缓存了用户信息,不需要重复访问数据库
四、Swagger添加JWT验证
4.1、Swagger 设置 JWT验证
Program.cs
# region swagger里添加JWT授权
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Web API", Version = "v1" });
//开启注释
var xmlFile = $"{Assembly.GetEntryAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath, true);
// 配置 JWT Bearer 授权
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT Authorization header using the Bearer scheme",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.Http,
Scheme = "bearer"
});
var securityScheme = new OpenApiSecurityScheme
{
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" }
};
var securityRequirement = new OpenApiSecurityRequirement { { securityScheme, new string[] { } } };
c.AddSecurityRequirement(securityRequirement);
});
#endregion
4.2、异常:注释未开处理
提示:右键项目 -> 属性
4.3、测试
4.31、贴入生成的token
4.32、测试成功
五、总结
[Authorize] 封装了验证逻辑,没必要深究
数据接口验权,无外乎就是登录的时候生成 Token , 客户端用 Cookie 或 localStorage 缓存
使用时写入headers , 如:
axios.get(url, {
headers: {
'Authorization': 'Bearer ' + token
}
})
或
//index.js
Vue.http.interceptors.push((request, next) => {
//请求发送前的处理逻辑
request.headers.set('Authorization', 'Bearer ' + token)
next((response) => {
//请求发送后的处理逻辑
//根据请求的状态,response参数返回给successCallback或errorCallback
return response
})
})
参考:
C# ASP.NET Core Web API 身份授权(JWT)验证(一)
Swagger添加JWT验证(ASP.NET)
项目代码
Demo下载:https://download.csdn.net/download/yqsy123123/89571832