using Entities.Configuration;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using Entities.DTO;
namespace TechHelper.Services.Beta
{
///
/// 用户认证服务,实现 JWT 令牌生成、刷新令牌生成、过期令牌解析等功能。
///
public class AuthenticationService : IAuthenticationService
{
private readonly JwtConfiguration _jwtSettings;
private readonly JwtSecurityTokenHandler _jwtHandler;
private readonly UserManager _userManager;
private readonly IClassService _classService;
///
/// 构造函数,注入 JWT 配置、用户管理器和班级服务。
///
public AuthenticationService(IOptions jwtSettings, UserManager userManager, IClassService classService)
{
_jwtSettings = jwtSettings.Value;
_jwtHandler = new JwtSecurityTokenHandler();
_userManager = userManager;
_classService = classService;
}
///
/// 生成指定用户的 JWT 访问令牌。
///
/// 用户实体
/// JWT 字符串
public async Task GetToken(Entities.Contracts.User user)
{
var signingCredentials = GetSigningCredentials();
var claims = await GetClaims(user);
var tokenOptions = GenerateTokenOptions(signingCredentials, claims);
return _jwtHandler.WriteToken(tokenOptions);
}
///
/// 获取 JWT 签名凭证。
///
/// 签名凭证
private SigningCredentials GetSigningCredentials()
{
var key = Encoding.UTF8.GetBytes(_jwtSettings.SecurityKey);
var secret = new SymmetricSecurityKey(key);
return new SigningCredentials(secret, SecurityAlgorithms.HmacSha256);
}
///
/// 获取用户的声明信息,包括角色和班级信息。
///
/// 用户实体
/// 声明集合
private async Task> GetClaims(Entities.Contracts.User user)
{
var claims = new List
{
new Claim(ClaimTypes.Name, user.Email)
};
// 添加用户角色声明
var roles = await _userManager.GetRolesAsync(user);
foreach (var role in roles)
{
claims.Add(new Claim(ClaimTypes.Role, role));
}
#region ClassInfo
// 添加用户班级信息声明
var classInfo = await _classService.GetUserInjoinedClasses(user.Id);
if (classInfo.Status)
{
var classs = classInfo.Result as UserClassDetailInfoDto;
if (classs == null) return claims;
foreach (var c in classs.UserClassInfos)
{
claims.Add(new Claim("Grade", c.Grade.ToString()));
claims.Add(new Claim("Class", c.Class.ToString()));
}
}
#endregion
return claims;
}
///
/// 生成 JWT 令牌对象。
///
/// 签名凭证
/// 声明集合
/// JWT 令牌对象
private JwtSecurityToken GenerateTokenOptions(SigningCredentials signingCredentials, IEnumerable claims)
{
var tokenOptions = new JwtSecurityToken(
issuer: _jwtSettings.ValidIssuer,
audience: _jwtSettings.ValidAudience,
claims: claims,
expires: DateTime.UtcNow.AddMinutes(Convert.ToDouble(
_jwtSettings.ExpiryInMinutes)),
signingCredentials: signingCredentials);
return tokenOptions;
}
///
/// 生成安全的刷新令牌(Base64 字符串)。
///
/// 刷新令牌
public string GenerateRefreshToken()
{
var randomNumber = new byte[32];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(randomNumber);
return Convert.ToBase64String(randomNumber);
}
}
///
/// 从过期的 JWT 令牌中获取声明主体(不验证过期时间)。
///
/// 过期的 JWT 令牌
/// 声明主体
/// 令牌无效时抛出
public ClaimsPrincipal GetPrincipalFromExpiredToken(string token)
{
var tokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = true,
ValidateIssuer = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(_jwtSettings.SecurityKey)),
ValidateLifetime = false, // 不验证过期时间
ValidIssuer = _jwtSettings.ValidIssuer,
ValidAudience = _jwtSettings.ValidAudience,
};
var tokenHandler = new JwtSecurityTokenHandler();
SecurityToken securityToken;
var principal = tokenHandler.ValidateToken(token,
tokenValidationParameters, out securityToken);
var jwtSecurityToken = securityToken as JwtSecurityToken;
if (jwtSecurityToken == null ||
!jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256,
StringComparison.InvariantCultureIgnoreCase))
{
throw new SecurityTokenException("Invalid token");
}
return principal;
}
}
}