Files
OpenAuth.Net/OpenAuth.App/SSO/JwtTokenHelper.cs
2026-07-06 21:14:28 +08:00

145 lines
5.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.IdentityModel.Tokens;
namespace OpenAuth.App.SSO
{
/// <summary>
/// JWT Token辅助类用于本地认证模式下的Token生成和验证
/// </summary>
public static class JwtTokenHelper
{
/// <summary>
/// 生成JWT Token
/// </summary>
/// <param name="account">用户账号</param>
/// <param name="name">用户名称</param>
/// <param name="appKey">应用标识</param>
/// <param name="sessionId">会话ID用作缓存Key</param>
/// <param name="secret">JWT签名密钥</param>
/// <param name="expireDays">过期天数</param>
/// <returns>JWT Token字符串</returns>
public static string GenerateToken(string account, string name, string appKey, string sessionId, string secret, int expireDays = 10)
{
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(EnsureKeyLength(secret)));
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, account),
new Claim(JwtRegisteredClaimNames.Jti, sessionId),
new Claim("name", name ?? string.Empty),
new Claim("app_key", appKey ?? string.Empty),
};
var token = new JwtSecurityToken(
issuer: "OpenAuth",
audience: "OpenAuth",
claims: claims,
expires: DateTime.UtcNow.AddDays(expireDays),
signingCredentials: credentials
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
/// <summary>
/// 验证JWT Token并返回ClaimsPrincipal
/// </summary>
/// <param name="token">JWT Token字符串</param>
/// <param name="secret">JWT签名密钥</param>
/// <returns>验证成功返回ClaimsPrincipal失败返回null</returns>
public static ClaimsPrincipal ValidateToken(string token, string secret)
{
if (string.IsNullOrEmpty(token))
return null;
var tokenHandler = new JwtSecurityTokenHandler();
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(EnsureKeyLength(secret)));
try
{
var validationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = key,
ValidateIssuer = true,
ValidIssuer = "OpenAuth",
ValidateAudience = true,
ValidAudience = "OpenAuth",
ValidateLifetime = true,
ClockSkew = TimeSpan.FromMinutes(5)
};
var principal = tokenHandler.ValidateToken(token, validationParameters, out _);
return principal;
}
catch
{
return null;
}
}
/// <summary>
/// 从JWT Token中提取会话IDjti
/// </summary>
public static string GetSessionId(string token)
{
var principal = ReadTokenWithoutValidation(token);
return principal?.FindFirst(JwtRegisteredClaimNames.Jti)?.Value;
}
/// <summary>
/// 从JWT Token中提取用户账号sub
/// </summary>
public static string GetAccount(string token)
{
var principal = ReadTokenWithoutValidation(token);
return principal?.FindFirst(JwtRegisteredClaimNames.Sub)?.Value
?? principal?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
}
/// <summary>
/// 不验证签名仅读取Token中的Claims用于快速提取信息
/// </summary>
private static ClaimsPrincipal ReadTokenWithoutValidation(string token)
{
if (string.IsNullOrEmpty(token))
return null;
try
{
var tokenHandler = new JwtSecurityTokenHandler();
if (!tokenHandler.CanReadToken(token))
return null;
var jwtToken = tokenHandler.ReadJwtToken(token);
var identity = new ClaimsIdentity(jwtToken.Claims);
return new ClaimsPrincipal(identity);
}
catch
{
return null;
}
}
/// <summary>
/// 确保密钥长度至少为32字节256位不足时进行填充
/// </summary>
private static string EnsureKeyLength(string secret)
{
if (string.IsNullOrEmpty(secret))
secret = "openauth_default_jwt_secret_key_2024";
// HMAC-SHA256要求密钥至少128位(16字节)建议256位(32字节)
while (secret.Length < 32)
{
secret += secret;
}
return secret.Substring(0, Math.Max(32, secret.Length));
}
}
}