using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Security.Claims; using System.Text; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using WalkingTec.Mvvm.Core; using WalkingTec.Mvvm.Core.Auth; using WalkingTec.Mvvm.Core.Extensions; namespace WalkingTec.Mvvm.Mvc.Auth { public class TokenService : ITokenService { private readonly ILogger _logger; private readonly JwtOption _jwtOptions; private const Token _emptyToken = null; private readonly Configs _configs; private readonly IDataContext _dc; public IDataContext DC => _dc; public TokenService( ILogger logger, IOptionsMonitor configs ) { _configs = configs.CurrentValue; _jwtOptions = _configs.JwtOptions; _logger = logger; _dc = CreateDC(); } public async Task IssueTokenAsync(LoginUserInfo loginUserInfo) { if (loginUserInfo == null) throw new ArgumentNullException(nameof(loginUserInfo)); var signinCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtOptions.SecurityKey)), SecurityAlgorithms.HmacSha256); var cls = new List() { new Claim(AuthConstants.JwtClaimTypes.Subject, loginUserInfo.ITCode.ToString()) }; if (string.IsNullOrEmpty(loginUserInfo.TenantCode) == false) { cls.Add(new Claim(AuthConstants.JwtClaimTypes.TenantCode, loginUserInfo.TenantCode.ToString())); } var tokeOptions = new JwtSecurityToken( issuer: _jwtOptions.Issuer, audience: _jwtOptions.Audience, claims: cls, expires: DateTime.Now.AddSeconds(_jwtOptions.Expires), signingCredentials: signinCredentials ); var refreshToken = new PersistedGrant() { UserCode = loginUserInfo.ITCode + "$`$" + loginUserInfo.TenantCode, Type = "refresh_token", CreationTime = DateTime.Now, RefreshToken = Guid.NewGuid().ToString("N"), Expiration = DateTime.Now.AddSeconds(_jwtOptions.RefreshTokenExpires) }; _dc.AddEntity(refreshToken); await _dc.SaveChangesAsync(); var token = new JwtSecurityTokenHandler().WriteToken(tokeOptions); return await Task.FromResult(new Token() { AccessToken = token, ExpiresIn = _jwtOptions.Expires, TokenType = AuthConstants.JwtTokenType, RefreshToken = refreshToken.RefreshToken }); } private IDataContext CreateDC() { string cs = "tokendefault"; if (_configs.Connections.Any(x => x.Key.ToLower() == cs) == false) { cs = "default"; } return _configs.Connections.Where(x => x.Key.ToLower() == cs).FirstOrDefault().CreateDC(); } /// /// refresh token /// /// refreshToken /// public async Task RefreshTokenAsync(string refreshToken) { // 获取 RefreshToken PersistedGrant persistedGrant = await _dc.Set().Where(x => x.RefreshToken == refreshToken).SingleOrDefaultAsync(); if (persistedGrant != null) { // 校验 regresh token 有效期 if (persistedGrant.Expiration < DateTime.Now) throw new Exception("refresh token 已过期"); // 删除 refresh token _dc.DeleteEntity(persistedGrant); await _dc.SaveChangesAsync(); var pair = persistedGrant.UserCode?.Split("$`$"); //生成并返回登录用户信息 var loginUserInfo = new LoginUserInfo() { ITCode = pair[0], TenantCode = pair[1], }; // 颁发 token return await IssueTokenAsync(loginUserInfo); } else { return null; } } /// /// clear expired refresh tokens /// /// public async Task ClearExpiredRefreshTokenAsync() { var dataTime = DateTime.Now; var mapping = _dc.GetTableName(); var sql = $"DELETE FROM {mapping} WHERE Expiration<=@dataTime"; _dc.RunSQL(sql, new { dataTime = dataTime }); await Task.CompletedTask; } } }