【ASP.NET Core 认证】自定义Jwt token验证(ISecurityTokenValidator)
Core为我们提供了自定义token验证的接口,当我们需要使用自己的方式验证token时可以使用。
比如Jwt只能数据签名,不能加密。当需要校验加密的jwt token。在登录时将jwt token加密后传给客户端,客户端回传token。这时需要我们自定义token校验。
自定义token校验,实现ISecurityTokenValidator接口
/// <summary>
/// 自定义token校验
/// </summary>
public class MyTokenValidator : ISecurityTokenValidator
{
public int MaximumTokenSizeInBytes { get; set; }
public bool CanReadToken(string securityToken)
{
return true;
}
public bool CanValidateToken
{
get
{
return true;
}
}
//验证token
public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken)
{
string jwtToken = AESCryptoHelper.Decrypt(securityToken);
ClaimsPrincipal principal = new JwtSecurityTokenHandler().ValidateToken(jwtToken, validationParameters, out validatedToken);
return principal;
}
}
配置文件中添加配置:
"JwtTokenOptions": {
"Issuer": "FAN.Issuer",
"ValidateIssuer": true,
"Audience": "FAN.Audience",
"ValidateAudience": true,
"RawSigningKey": "11111111-1111-1111-1111-111111111111",/*签名秘钥*/
"ValidateIssuerSigningKey": true,
"ValidateLifetime": false,
"RequireExpirationTime": false,
"JwtExpiresInMinutes": 6000,
"ValidateIntervaltime": true,
"IntervalExpiresInMinutes": 3000
}
Startup.cs添加如下内容:
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<TokenBuilder>();
services.Configure<JwtTokenOptions>(Configuration.GetSection("JwtTokenOptions"));
JwtTokenOptions tokenOptions = Configuration.GetSection("JwtTokenOptions").Get<JwtTokenOptions>();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
//开启Bearer服务认证
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.SaveToken = true;
options.TokenValidationParameters = tokenOptions.ToTokenValidationParams();
options.SecurityTokenValidators.Clear();
options.SecurityTokenValidators.Add(new MyTokenValidator());
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseAuthentication();
}
User:用户信息
/// <summary>
/// 登录用户信息
/// </summary>
public class User
{
public int UserID { get; set; }
public string Email { get; set; }
public string Name { get; set; }
public string Role { get; set; }
public User(int userID, string name, string email, string role)
{
this.UserID = userID;
this.Name = name;
this.Email = email;
this.Role = role;
}
}
TokenBuilder:用于创建jwt token
/// <summary>
/// token创建
/// </summary>
public class TokenBuilder
{
private JwtTokenOptions _tokenOptions = null;
public TokenBuilder(IOptions<JwtTokenOptions> tokenOptions)
{
this._tokenOptions = tokenOptions.Value;
}
/// <summary>
/// 创建加密JwtToken
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public string CreateJwtToken(User user)
{
var claimList = this.CreateClaimList(user);
JwtSecurityToken jwtSecurityToken = new JwtSecurityToken(
issuer: this._tokenOptions.Issuer
, audience: this._tokenOptions.Audience
, claims: claimList
//, notBefore: utcNow
, expires: DateTime.Now.AddDays(3)
, signingCredentials: new SigningCredentials(this._tokenOptions.SigningKey, SecurityAlgorithms.HmacSha256)
);
string jwtToken = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
//加密jwtToken
jwtToken = AESCryptoHelper.Encrypt(jwtToken);
return jwtToken;
}
/// <summary>
/// 创建包含用户信息的CalimList
/// </summary>
/// <param name="authUser"></param>
/// <returns></returns>
private List<Claim> CreateClaimList(User authUser)
{
//身份单元项项集合
List<Claim> claimList = new List<Claim>()
{
new Claim(type: ClaimTypes.Email, value: authUser.Email), //身份单元项
new Claim(type: ClaimTypes.Name, value: authUser.Name),
new Claim(type: ClaimTypes.NameIdentifier, value: authUser.UserID.ToString()),
new Claim(type: ClaimTypes.Role, value: authUser.Role ?? string.Empty)
};
return claimList;
}
}
AESCryptoHelper:使用AES对jwttoken的加密解密
/// <summary>
/// AES加密解密
/// </summary>
public class AESCryptoHelper
{
/// <summary>
/// AES加密解密Key,Key必须十六位
/// </summary>
private static readonly string AESKey = "1111111111111111";
/// <summary>
/// AES 加密
/// </summary>
/// <param name="plainText"></param>
/// <returns></returns>
public static string Encrypt(string plainText)
{
return Encrypt(plainText, AESKey);
}
/// <summary>
/// AES 加密
/// </summary>
/// <param name="plainText"></param>
/// <param name="key">密码必须是16位,否则会报错哈</param>
/// <returns></returns>
public static string Encrypt(string plainText, string key)
{
string result = null;
if (string.IsNullOrEmpty(plainText))
{
return result;
}
byte[] plainTextArray = Encoding.UTF8.GetBytes(plainText);
using (MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider())
{
using (RijndaelManaged rijndaelManaged = new RijndaelManaged
{
Key = provider.ComputeHash(Encoding.UTF8.GetBytes(key)),
//Key = Encoding.UTF8.GetBytes(key),
Mode = CipherMode.ECB,
Padding = PaddingMode.PKCS7
})
{
using (ICryptoTransform cryptoTransform = rijndaelManaged.CreateEncryptor())
{
byte[] resultArray = cryptoTransform.TransformFinalBlock(plainTextArray, 0, plainTextArray.Length);
result = Convert.ToBase64String(resultArray, 0, resultArray.Length);
Array.Clear(resultArray, 0, resultArray.Length);
resultArray = null;
}
}
}
Array.Clear(plainTextArray, 0, plainTextArray.Length);
plainTextArray = null;
return result;
}
/// <summary>
/// AES 解密
/// </summary>
/// <param name="encryptText"></param>
/// <returns></returns>
public static string Decrypt(string encryptText)
{
return Decrypt(encryptText, AESKey);
}
/// <summary>
/// AES 解密
/// </summary>
/// <param name="encryptText"></param>
/// <param name="key">密码必须是16位,否则会报错哈</param>
/// <returns></returns>
public static string Decrypt(string encryptText, string key)
{
string result = null;
if (string.IsNullOrEmpty(encryptText))
{
return result;
}
byte[] encryptTextArray = Convert.FromBase64String(encryptText);
using (MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider())
{
using (RijndaelManaged rijndaelManaged = new RijndaelManaged
{
Key = provider.ComputeHash(Encoding.UTF8.GetBytes(key)),
//Key = Encoding.UTF8.GetBytes(key),
Mode = CipherMode.ECB,
Padding = PaddingMode.PKCS7
})
{
using (ICryptoTransform cryptoTransform = rijndaelManaged.CreateDecryptor())
{
byte[] resultArray = cryptoTransform.TransformFinalBlock(encryptTextArray, 0, encryptTextArray.Length);
result = Encoding.UTF8.GetString(resultArray);
Array.Clear(resultArray, 0, resultArray.Length);
resultArray = null;
}
}
}
Array.Clear(encryptTextArray, 0, encryptTextArray.Length);
encryptTextArray = null;
return result;
}
}
控制器:
[Route("api/[controller]/[action]")]
[ApiController]
public class ValuesController : ControllerBase
{
private TokenBuilder _tokenBuilder = null;
public ValuesController(TokenBuilder tokenBuilder)
{
this._tokenBuilder = tokenBuilder;
}
/// <summary>
/// 登录,生成加密token
/// </summary>
/// <returns></returns>
[HttpGet]
public ActionResult<string> Login()
{
//省略登录逻辑。。。。
var user = new User(1, "fan", "410577910@qq.com", "admin");
string signToken = this._tokenBuilder.CreateJwtToken(user);
return signToken;
}
/// <summary>
/// 创建订单,需要校验token
/// </summary>
/// <returns></returns>
[Authorize]
[HttpPost]
public ActionResult<string> CreateOrder()
{
if (base.User.Identity.IsAuthenticated)
{
string userName = base.User.Identity.Name;
}
//省略创建订单逻辑。。。
return "生成订单成功";
}
}
测试
登录:(获取token)
http://localhost:5000/api/values/CreateOrder
创建订单:(带上token,才能授权通过)
http://localhost:5000/api/values/CreateOrder POST
Headers添加:
Key: Authorization
Value: Bearer 2FrDOaIT8r9n7axl4ARhe2dp7S7bWS5+eCcErbkeIIzVbutYr+LmMI1HBEWCLwgwS80adJlwDGW9iNtL2Kp4y+twxHmEpPJfhmg8X1uzgYReh6Nd7XcJmQh9zXvma9gfySU8DcEOijD79wellmRqPNh8ZYX7C0DqKx55L8DVgkJ4emOHIyni/V7qs3xqgU6RstCCxOAI4tHS7v67jmtQJks2x88iBntAJWsPwr7jRChvdWXHpsMeOzyZDjEwiT/XZIFHwB6wyUOfGqqkpdj3s36XwaKEvEBJzwSL2LCUEpMJUr7SQ0r4ZJNnPEBsFoiA/s35nbhgt9OU5w4Z+9MUJ2hBW/eTRlhwtcUR48nitE+PyJS9Ipan+wngvKkkX64BU0l4aLQ9b+REmjf9NZ1RD1UGCL2bmP5XDeoHtYM8uEGYCXu4vvZ1C16oy7AJv/PEEUtL1WNEwJ0Pf9A3DV0o0UxR1q1T5x5nFHftmUu/wrgL2R6GJkUKvWOFWJgAILRk1jHd6HueuqTdTZWdPIDmUH6XwaKEvEBJzwSL2LCUEpMATTE2c3Sw+06USRWWJ3+pIYWS7pM632cOahyPj5iyAihGleXc3bFgjTH/9zlUnJAz3FTrFdRLjj4Vy/UTQDwUKLZEcPWXcYFTPtfuMuR1rwrzIXJOa8ywA/qdbn0XTHtCbTPTHADBaqPEzK8NxMQdCrtgfuCjJ4kPrmVxOaf784RiHgDmcvJ+2Xtazf09hYo+gKtSy8xLTktDZHxfniAtfZP8QU18rpGNsOcKl/E7rA==