using Microsoft.IdentityModel.Tokens; using QYZH.InteractiveMagazine.Models.Settings; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; namespace QYZH.InteractiveMagazine.Infrastructure.Auth; /// /// JWT工具类 /// public static class JwtHelper { public const string AdminTokenKeyPrefix = "InteractiveMagazine:AdminAuth:Token"; public const string WeChatTokenKeyPrefix = "InteractiveMagazine:WeChatAuth:Token"; /// /// 生成JWT令牌(管理端 / 单用户场景) /// /// 用户ID /// 用户名 /// JWT配置 /// JWT令牌字符串 public static string GenerateToken(long userId, string userName, JwtSettings settings) { var claims = new[] { new Claim(JwtRegisteredClaimNames.Sub, userId.ToString()), new Claim(JwtRegisteredClaimNames.Name, userName), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new Claim(ClaimTypes.NameIdentifier, userId.ToString()), new Claim(ClaimTypes.Name, userName) }; return BuildToken(claims, settings); } /// /// 生成JWT令牌(小程序场景,同时携带 WxUserId 和 UserId) /// /// 微信用户ID(WxUser.Id) /// 当前激活用户ID(Users.Id,无用户时为 0) /// 用户名 /// JWT配置 /// JWT令牌字符串 public static string GenerateToken(long wxUserId, long userId, string userName, JwtSettings settings) { var claims = new[] { new Claim(JwtRegisteredClaimNames.Sub, userId.ToString()), new Claim(JwtRegisteredClaimNames.Name, userName), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new Claim(ClaimTypes.NameIdentifier, userId.ToString()), new Claim(ClaimTypes.Name, userName), new Claim(WxUserIdClaimType, wxUserId.ToString()) }; return BuildToken(claims, settings); } /// /// 自定义 Claim 类型:WxUserId /// public const string WxUserIdClaimType = "WxUserId"; public static string BuildAdminTokenKey(string userId) { return $"{AdminTokenKeyPrefix}:{userId}"; } public static string BuildWeChatTokenKey(string wxUserId, string userId) { return $"{WeChatTokenKeyPrefix}:{wxUserId}:{userId}"; } public static string BuildWeChatTokenKey(long wxUserId, long userId) { return BuildWeChatTokenKey(wxUserId.ToString(), userId.ToString()); } private static string BuildToken(Claim[] claims, JwtSettings settings) { var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(settings.SecretKey!)); var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken( issuer: settings.Issuer, audience: settings.Audience, claims: claims, expires: DateTime.Now.AddDays(settings.JwtTokenExpiryDays), signingCredentials: credentials ); return new JwtSecurityTokenHandler().WriteToken(token); } /// /// 获取Token过期时间 /// /// JWT令牌 /// 过期时间 public static DateTime? GetTokenExpiry(string token) { var tokenHandler = new JwtSecurityTokenHandler(); if (tokenHandler.ReadToken(token) is JwtSecurityToken jwtToken) { return jwtToken.ValidTo; } return null; } /// /// 从Token中获取用户ID /// /// JWT令牌 /// 用户ID public static long? GetUserIdFromToken(string token) { var tokenHandler = new JwtSecurityTokenHandler(); if (tokenHandler.ReadToken(token) is JwtSecurityToken jwtToken) { var userIdClaim = GetClaim(jwtToken, ClaimTypes.NameIdentifier, JwtRegisteredClaimNames.NameId, JwtRegisteredClaimNames.Sub); if (long.TryParse(userIdClaim?.Value, out long userId)) { return userId; } } return null; } /// /// 从Token中获取微信用户ID(WxUserId) /// /// JWT令牌 /// WxUserId public static long? GetWxUserIdFromToken(string token) { var tokenHandler = new JwtSecurityTokenHandler(); if (tokenHandler.ReadToken(token) is JwtSecurityToken jwtToken) { var wxUserIdClaim = jwtToken.Claims.FirstOrDefault(c => c.Type == WxUserIdClaimType); if (long.TryParse(wxUserIdClaim?.Value, out long wxUserId)) { return wxUserId; } } return null; } /// /// 从Token中获取用户名 /// /// JWT令牌 /// 用户名 public static string GetUserNameFromToken(string token) { var tokenHandler = new JwtSecurityTokenHandler(); if (tokenHandler.ReadToken(token) is JwtSecurityToken jwtToken) { return GetClaim(jwtToken, ClaimTypes.Name, JwtRegisteredClaimNames.UniqueName, JwtRegisteredClaimNames.Name)?.Value ?? string.Empty; } return string.Empty; } private static Claim? GetClaim(JwtSecurityToken jwtToken, params string[] claimTypes) { return jwtToken.Claims.FirstOrDefault(c => claimTypes.Contains(c.Type)); } }