✨
This commit is contained in:
174
StopShopping.Services/Implementions/AccessTokenService.cs
Normal file
174
StopShopping.Services/Implementions/AccessTokenService.cs
Normal file
@@ -0,0 +1,174 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user