using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using Dpz.Core.Public.ViewModel.Request;
using Dpz.Core.Public.ViewModel.Response;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
namespace Dpz.Core.Service.RepositoryServiceImpl;
public class TokenAuthenticationService(
IAccountService accountService,
IOptions<TokenManagement> tokenManagement,
IAccountTokenService accountTokenService,
IAppOptionService optionService,
ILogger<TokenAuthenticationService> logger,
IConfiguration configuration
) : IAuthenticateService
{
private readonly TokenManagement _tokenManagement = tokenManagement.Value;
/// <summary>
/// 验证
/// </summary>
/// <returns></returns>
public async Task<AuthenticatedResponse?> AuthenticatedAsync(AuthenticateRequest request)
{
var userInfo = await accountService.LoginAsync(request.Account, request.Md5Password);
if (userInfo == null)
return null;
if (!userInfo.Avatar.StartsWith("http", StringComparison.CurrentCultureIgnoreCase))
{
userInfo.Avatar = configuration["WebHost"] + userInfo.Avatar;
}
var accessToken = GenerateAccessToken(userInfo);
var accountTokenRequest = new AccountTokenRequest
{
Account = request.Account,
ExpiresTime = DateTime.Now.AddDays(_tokenManagement.RefreshExpiration),
Platform = request.Platform,
IpAddress = request.IpAddress,
UserAgent = request.UserAgent
};
var refreshToken = await accountTokenService.GenerateRefreshTokenAsync(accountTokenRequest);
return new AuthenticatedResponse(
accessToken.expires,
accessToken.token,
refreshToken,
userInfo
);
}
/// <summary>
/// 生成访问令牌
/// </summary>
/// <param name="userInfo"></param>
/// <returns></returns>
private (DateTime expires, string token) GenerateAccessToken(VmUserInfo userInfo)
{
var claims = userInfo
.GetType()
.GetProperties()
.Select(x => new Claim(x.Name, x.GetValue(userInfo)?.ToString() ?? ""))
.ToList();
claims.Add(new Claim(ClaimTypes.Name, userInfo.Id));
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_tokenManagement.Secret));
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var expires = DateTime.Now.AddMinutes(_tokenManagement.AccessExpiration);
var jwtToken = new JwtSecurityToken(
issuer: _tokenManagement.Issuer,
audience: _tokenManagement.Audience,
claims: claims,
expires: expires,
signingCredentials: credentials
);
var token = new JwtSecurityTokenHandler().WriteToken(jwtToken);
return (expires, token);
}
/// <summary>
/// 刷新token
/// </summary>
/// <returns></returns>
public async Task<AuthenticatedResponse?> RefreshTokenAsync(RefreshTokenRequest request)
{
var tokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = _tokenManagement.Issuer,
ValidAudience = _tokenManagement.Audience,
ValidateIssuer = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(_tokenManagement.Secret)
),
// 忽略token过期时间,验证token
ValidateLifetime = false
};
var principal = new JwtSecurityTokenHandler().ValidateToken(
request.AccessToken,
tokenValidationParameters,
out var securityToken
);
if (
principal.Identity?.IsAuthenticated != true
|| securityToken is not JwtSecurityToken jwtSecurityToken
|| !jwtSecurityToken.Header.Alg.Equals(
SecurityAlgorithms.HmacSha256,
StringComparison.InvariantCultureIgnoreCase
)
)
{
logger.LogError("refresh token fail,stack trace:{StackTrace}", Environment.StackTrace);
return null;
}
var account = principal.Identity?.Name ?? "";
if (
await accountTokenService.ValidateRefreshTokenAsync(
account,
request.RefreshToken,
request.Platform
)
)
{
var userInfo = await accountService.GetOneUserAsync(account);
if (!userInfo.Avatar.StartsWith("http", StringComparison.CurrentCultureIgnoreCase))
{
userInfo.Avatar = configuration["WebHost"] + userInfo.Avatar;
}
var accountTokenRequest = new AccountTokenRequest
{
Account = userInfo.Id,
ExpiresTime = DateTime.Now.AddDays(_tokenManagement.RefreshExpiration),
Platform = request.Platform,
IpAddress = request.IpAddress,
UserAgent = request.UserAgent
};
var newAccessToken = GenerateAccessToken(userInfo);
var newRefreshToken = await accountTokenService.GenerateRefreshTokenAsync(
accountTokenRequest
);
return new AuthenticatedResponse(
newAccessToken.expires,
newAccessToken.token,
newRefreshToken,
userInfo
);
}
return null;
}
/// <summary>
/// 登入
/// </summary>
/// <returns></returns>
public async Task<string?> SignInAsync(string account, string password)
{
//VmUserInfo user, VmSaltOption salt
var userInfo = await accountService.LoginAsync(account, (account + password).GenerateMd5());
if (userInfo == null)
return null;
var salt = await optionService.GetSaltAsync(account);
var claims = userInfo
.GetType()
.GetProperties()
.Select(x => new Claim(x.Name, x.GetValue(userInfo)?.ToString() ?? ""))
.ToList();
claims.Add(new Claim(ClaimTypes.Name, userInfo.Id));
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(salt.Salt));
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var expires = salt.Expire;
var jwtToken = new JwtSecurityToken(
issuer: "api-server",
audience: "api-client",
claims: claims,
expires: expires,
signingCredentials: credentials
);
var token = new JwtSecurityTokenHandler().WriteToken(jwtToken);
return token;
}
}