175 lines
5.7 KiB
C#
175 lines
5.7 KiB
C#
using System.Security.Claims;
|
|
using System.Text;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Caching.Distributed;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Options;
|
|
using Microsoft.IdentityModel.JsonWebTokens;
|
|
using Microsoft.IdentityModel.Tokens;
|
|
using StopShopping.EF;
|
|
using StopShopping.EF.Models;
|
|
using StopShopping.Services.Models;
|
|
using StopShopping.Services.Models.Resp;
|
|
|
|
namespace StopShopping.Services.Implementions;
|
|
|
|
public class AccessTokenService : IAccessTokenService
|
|
{
|
|
public AccessTokenService(IOptions<JwtOptions> jwtOptions,
|
|
IDistributedCache cache,
|
|
StopShoppingContext dbContext,
|
|
ILogger<AccessTokenService> logger,
|
|
IClaimsService claimsService)
|
|
{
|
|
_jwtOptions = jwtOptions.Value;
|
|
_cache = cache;
|
|
_dbContext = dbContext;
|
|
_logger = logger;
|
|
_claimsService = claimsService;
|
|
}
|
|
|
|
private readonly JwtOptions _jwtOptions;
|
|
private readonly IDistributedCache _cache;
|
|
private readonly StopShoppingContext _dbContext;
|
|
private readonly ILogger<AccessTokenService> _logger;
|
|
private readonly IClaimsService _claimsService;
|
|
|
|
public AccessToken GenerateAccessToken(ClaimsIdentity claims)
|
|
{
|
|
var tokenDescriptor = new SecurityTokenDescriptor
|
|
{
|
|
Audience = _jwtOptions.ValidAudience,
|
|
Issuer = _jwtOptions.ValidIssuer,
|
|
Subject = claims,
|
|
IssuedAt = DateTime.UtcNow,
|
|
NotBefore = DateTime.UtcNow,
|
|
Expires = DateTime.UtcNow.AddSeconds(_jwtOptions.AccessTokenExpiresIn),
|
|
SigningCredentials = new SigningCredentials(
|
|
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtOptions.SigningKey!)),
|
|
SecurityAlgorithms.HmacSha256
|
|
)
|
|
};
|
|
|
|
var token = new JsonWebTokenHandler()
|
|
.CreateToken(tokenDescriptor);
|
|
|
|
return new AccessToken
|
|
{
|
|
Token = token,
|
|
ExpiresIn = _jwtOptions.AccessTokenExpiresIn
|
|
};
|
|
}
|
|
|
|
public async Task<bool> AddAccessTokenBlacklistAsync(string accessToken)
|
|
{
|
|
JsonWebTokenHandler jwtHandler = new JsonWebTokenHandler();
|
|
var jwtToken = jwtHandler.ReadJsonWebToken(accessToken);
|
|
var expClaim = jwtToken.Claims.FirstOrDefault(c => c.Type == JwtRegisteredClaimNames.Exp);
|
|
if (null == expClaim)
|
|
{
|
|
_logger.LogError("access_token:{Token}中无法找到exp", accessToken);
|
|
return false;
|
|
}
|
|
|
|
var expTime = DateTimeOffset.FromUnixTimeSeconds(long.Parse(expClaim.Value)).UtcDateTime;
|
|
if (DateTime.UtcNow > expTime)
|
|
{
|
|
_logger.LogError("access_token:{Token}已过期", accessToken);
|
|
return false;
|
|
}
|
|
|
|
await _cache.SetStringAsync(Consts.CacheKeys.AccessTokenBlacklist(accessToken),
|
|
"quited user",
|
|
new DistributedCacheEntryOptions
|
|
{
|
|
AbsoluteExpiration = expTime
|
|
});
|
|
|
|
return true;
|
|
}
|
|
|
|
public async Task<bool> IsAccessTokenBlacklistAsync(string accessToken)
|
|
{
|
|
var blacklist = await _cache.GetStringAsync(Consts.CacheKeys.AccessTokenBlacklist(accessToken));
|
|
|
|
return !string.IsNullOrWhiteSpace(blacklist);
|
|
}
|
|
|
|
public async Task<AccessToken> SetRefreshTokenAsync(int userId, SystemRoles systemRole)
|
|
{
|
|
var refreshToken = Guid.NewGuid().ToString("N");
|
|
var now = DateTime.Now;
|
|
char role = (char)systemRole;
|
|
|
|
// var qry = await _dbContext.RefreshTokens
|
|
// .Where(r => r.UserId == userId && r.SystemRole == role)
|
|
// .ExecuteDeleteAsync();
|
|
|
|
var model = new RefreshToken
|
|
{
|
|
CreateTime = now,
|
|
ExpiresAt = now.AddSeconds(_jwtOptions.RefreshTokenExpiresIn),
|
|
SystemRole = role,
|
|
Token = refreshToken,
|
|
UserId = userId
|
|
};
|
|
await _dbContext.RefreshTokens.AddAsync(model);
|
|
await _dbContext.SaveChangesAsync();
|
|
|
|
return new AccessToken
|
|
{
|
|
Token = refreshToken,
|
|
ExpiresIn = _jwtOptions.RefreshTokenExpiresIn
|
|
};
|
|
}
|
|
|
|
public async Task RevokeRefreshTokenAsync(string refreshToken)
|
|
{
|
|
await _dbContext.RefreshTokens
|
|
.Where(r => r.Token == refreshToken)
|
|
.ExecuteDeleteAsync();
|
|
}
|
|
|
|
public async Task<AccessToken?> GenerateAccessTokenAsync(string refreshToken)
|
|
{
|
|
var rt = await _dbContext.RefreshTokens
|
|
.AsNoTracking()
|
|
.Where(r => r.Token == refreshToken && r.ExpiresAt > DateTime.Now)
|
|
.FirstOrDefaultAsync();
|
|
if (null == rt)
|
|
return null;
|
|
|
|
ClaimsIdentity? claimsIdentity = null;
|
|
|
|
switch (rt.SystemRole)
|
|
{
|
|
case (char)SystemRoles.Admin:
|
|
{
|
|
var admin = await _dbContext.Administrators
|
|
.AsNoTracking()
|
|
.Where(a => a.Id == rt.UserId)
|
|
.FirstOrDefaultAsync();
|
|
if (null != admin)
|
|
claimsIdentity = _claimsService.BuildAdminIdentity(admin);
|
|
}
|
|
break;
|
|
case (char)SystemRoles.User:
|
|
{
|
|
var user = await _dbContext.Users
|
|
.AsNoTracking()
|
|
.Where(u => u.Id == rt.UserId)
|
|
.FirstOrDefaultAsync();
|
|
if (null != user)
|
|
claimsIdentity = _claimsService.BuildIdentity(user);
|
|
}
|
|
break;
|
|
default: break;
|
|
}
|
|
|
|
if (null == claimsIdentity)
|
|
return null;
|
|
|
|
return GenerateAccessToken(claimsIdentity);
|
|
}
|
|
}
|