103 lines
3.4 KiB
C#
103 lines
3.4 KiB
C#
using System.IdentityModel.Tokens.Jwt;
|
||
using System.Security.Claims;
|
||
using System.Security.Cryptography;
|
||
using System.Text;
|
||
using F9WebAiTest.Domain.Entities.SYS.RBAC;
|
||
using Microsoft.Extensions.Configuration;
|
||
using Microsoft.IdentityModel.Tokens;
|
||
|
||
namespace F9WebAiTest.Application.Services.Auth;
|
||
|
||
public class JwtService : IJwtService
|
||
{
|
||
private readonly IConfiguration _config;
|
||
|
||
public JwtService(IConfiguration config)
|
||
{
|
||
_config = config;
|
||
}
|
||
|
||
// 生成 access token(短期,如 2 小时)
|
||
public string GenerateAccessToken(User user, IEnumerable<string> roles)
|
||
{
|
||
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
|
||
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
|
||
|
||
var claims = new List<Claim>
|
||
{
|
||
new("id", user.Id.ToString()),
|
||
new(ClaimTypes.Name, user.LoginName),
|
||
new("realName", user.UserName)
|
||
};
|
||
foreach (var r in roles)
|
||
{
|
||
claims.Add(new Claim(ClaimTypes.Role, r));
|
||
}
|
||
|
||
var expireMinutes = int.Parse(_config["Jwt:AccessTokenExpireMinutes"] ?? "120");
|
||
var token = new JwtSecurityToken(
|
||
issuer: _config["Jwt:Issuer"],
|
||
audience: _config["Jwt:Audience"],
|
||
claims: claims,
|
||
expires: DateTime.UtcNow.AddMinutes(expireMinutes),
|
||
signingCredentials: creds
|
||
);
|
||
|
||
return new JwtSecurityTokenHandler().WriteToken(token);
|
||
}
|
||
|
||
// 生成 refresh token(随机字符串,长期,如 7 天)
|
||
public string GenerateRefreshToken()
|
||
{
|
||
var randomNumber = new byte[64];
|
||
using var rng = RandomNumberGenerator.Create();
|
||
rng.GetBytes(randomNumber);
|
||
return Convert.ToBase64String(randomNumber);
|
||
}
|
||
|
||
public (long id, string username) ValidateAccessToken(string token)
|
||
{
|
||
var handler = new JwtSecurityTokenHandler();
|
||
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
|
||
var validationParams = new TokenValidationParameters
|
||
{
|
||
ValidateIssuerSigningKey = true,
|
||
IssuerSigningKey = key,
|
||
ValidateIssuer = true,
|
||
ValidIssuer = _config["Jwt:Issuer"],
|
||
ValidateAudience = true,
|
||
ValidAudience = _config["Jwt:Audience"],
|
||
ValidateLifetime = true,
|
||
ClockSkew = TimeSpan.Zero
|
||
};
|
||
var principal = handler.ValidateToken(token, validationParams, out _);
|
||
var id = long.Parse(principal.FindFirst("id")!.Value);
|
||
var username = principal.Identity!.Name;
|
||
return (id, username);
|
||
}
|
||
|
||
// 校验 refresh token 的签名(refresh token 也是 JWT,但只含 id claim)
|
||
public long? ValidateRefreshToken(string token)
|
||
{
|
||
try
|
||
{
|
||
var handler = new JwtSecurityTokenHandler();
|
||
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
|
||
var validationParams = new TokenValidationParameters
|
||
{
|
||
ValidateIssuerSigningKey = true,
|
||
IssuerSigningKey = key,
|
||
ValidateIssuer = false,
|
||
ValidateAudience = false,
|
||
ValidateLifetime = false
|
||
};
|
||
var principal = handler.ValidateToken(token, validationParams, out _);
|
||
return long.Parse(principal.FindFirst("id")!.Value);
|
||
}
|
||
catch
|
||
{
|
||
return null;
|
||
}
|
||
}
|
||
}
|