/// <summary> /// RefreshAccessTokenAsync /// </summary> /// <param name="context"></param> /// <param name="lastUser"></param> /// <returns></returns> /// <exception cref="IdentityException"></exception> /// <exception cref="DatabaseException"></exception> /// <exception cref="CacheException"></exception> public async Task <UserAccessResult> RefreshAccessTokenAsync(RefreshContext context, string lastUser) { ThrowIf.NotValid(context, nameof(context)); //解决并发涌入 using IDistributedLock distributedLock = await _lockManager.NoWaitLockAsync( nameof (RefreshAccessTokenAsync) + context.DeviceId, _options.RefreshIntervalTimeSpan, notUnlockWhenDispose : true).ConfigureAwait(false); if (!distributedLock.IsAcquired) { throw Exceptions.AuthorizationTooFrequent(context: context); } //AccessToken, Claims 验证 ClaimsPrincipal?claimsPrincipal = null; try { claimsPrincipal = JwtHelper.ValidateTokenWithoutLifeCheck(context.AccessToken, _options.OpenIdConnectConfiguration.Issuer, _issuerSigningKeys, _decryptionSecurityKey); } catch (Exception ex) { throw Exceptions.AuthorizationInvalideAccessToken(context: context, innerException: ex); } //TODO: 这里缺DeviceId验证. 放在了StartupUtil.cs中 if (claimsPrincipal == null) { //TODO: Black concern SigninToken by RefreshToken throw Exceptions.AuthorizationInvalideAccessToken(context: context); } if (claimsPrincipal.GetDeviceId() != context.DeviceId) { throw Exceptions.AuthorizationInvalideDeviceId(context: context); } long userId = claimsPrincipal.GetUserId().GetValueOrDefault(); if (userId <= 0) { throw Exceptions.AuthorizationInvalideUserId(context: context); } //SignInToken 验证 User? user; SignInToken? signInToken = null; TransactionContext transactionContext = await _transaction.BeginTransactionAsync <SignInToken>().ConfigureAwait(false); try { signInToken = await _signInTokenRepo.GetByConditionAsync( claimsPrincipal.GetSignInTokenId().GetValueOrDefault(), context.RefreshToken, context.DeviceId, userId, transactionContext).ConfigureAwait(false); if (signInToken == null || signInToken.Blacked) { throw Exceptions.AuthorizationNoTokenInStore(cause: "Refresh token error. signInToken not saved in db. "); } //验证SignInToken过期问题 if (signInToken.ExpireAt < TimeUtil.UtcNow) { throw Exceptions.AuthorizationRefreshTokenExpired(); } // User 信息变动验证 user = await _userRepo.GetByIdAsync(userId, transactionContext).ConfigureAwait(false); if (user == null || user.SecurityStamp != claimsPrincipal.GetUserSecurityStamp()) { throw Exceptions.AuthorizationUserSecurityStampChanged(cause: "Refresh token error. User SecurityStamp Changed."); } // 更新SignInToken signInToken.RefreshCount++; await _signInTokenRepo.UpdateAsync(signInToken, lastUser, transactionContext).ConfigureAwait(false); // 发布新的AccessToken string accessToken = await ConstructJwtAsync(user, signInToken, claimsPrincipal.GetAudience(), transactionContext).ConfigureAwait(false); await _transaction.CommitAsync(transactionContext).ConfigureAwait(false); return(new UserAccessResult(accessToken, context.RefreshToken, user)); } catch { await _transaction.RollbackAsync(transactionContext).ConfigureAwait(false); if (signInToken != null) { await _signInTokenRepo.DeleteAsync(signInToken, lastUser, null).ConfigureAwait(false); } throw; } }
/// <summary> /// CosumeTaskProcedure /// </summary> private async Task CosumeAsync(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { try { long now = TimeUtil.UtcNowUnixTimeSeconds; //1, Get Entity IDatabase database = await RedisInstanceManager.GetDatabaseAsync(_instanceSetting, _logger).ConfigureAwait(false); RedisValue redisValue = await database.ListRightPopLeftPushAsync(RedisEventBusEngine.QueueName(_eventType), RedisEventBusEngine.HistoryQueueName(_eventType)).ConfigureAwait(false); if (redisValue.IsNullOrEmpty) { _logger.LogTrace("ConsumeTask Sleep, {InstanceName}, {eventType}", _instanceSetting.InstanceName, _eventType); await Task.Delay(_cONSUME_INTERVAL_SECONDS * 1000, cancellationToken).ConfigureAwait(false); continue; } EventMessageEntity?entity = SerializeUtil.FromJson <EventMessageEntity>(redisValue); if (entity == null) { _logger.LogCritical("有空EventMessageEntity, {eventType}, {value}", _eventType, redisValue); continue; } using IDistributedLock distributedLock = await _lockManager.NoWaitLockAsync( "eBusC_" + entity.Guid, TimeSpan.FromSeconds(_options.EventBusConsumerAckTimeoutSeconds), false, cancellationToken).ConfigureAwait(false); if (!distributedLock.IsAcquired) { //竟然有人在检查entity.Guid,好了,这下肯定有人在处理了,任务结束。哪怕那个人没处理成功,也没事,等着history吧。 continue; } //2, 过期检查 if (now - entity.Timestamp > _eventBusEventMessageExpiredSeconds) { _logger.LogCritical("有EventMessage过期,{eventType}, {entity}", _eventType, SerializeUtil.ToJson(entity)); await distributedLock.DisposeAsync().ConfigureAwait(false); continue; } //3, 防重检查 string AcksSetName = RedisEventBusEngine.AcksSetName(_eventType); bool?isExist = await IsAcksExistedAsync(database, AcksSetName, entity.Guid).ConfigureAwait(false); if (isExist == null || isExist.Value) { _logger.LogInformation("有EventMessage重复,{eventType}, {entity}", _eventType, SerializeUtil.ToJson(entity)); await distributedLock.DisposeAsync().ConfigureAwait(false); continue; } //4, Handle Entity try { await _eventHandler.HandleAsync(entity.JsonData, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { _logger.LogCritical(ex, "处理消息出错, {_eventType}, {entity}", _eventType, SerializeUtil.ToJson(entity)); } //5, Acks await AddAcksAsync(now, database, AcksSetName, entity.Guid, entity.Timestamp).ConfigureAwait(false); _logger.LogTrace("Consume Task For {eventType} Stopped.", _eventType); } catch (RedisConnectionException ex) { _logger.LogError(ex, "Consume 中出现Redis链接问题. {eventType}", _eventType); await Task.Delay(5000, cancellationToken).ConfigureAwait(false); } catch (RedisTimeoutException ex) { _logger.LogError(ex, "Consume 中出现Redis超时问题. {eventType}", _eventType); } catch (Exception ex) { _logger.LogCritical(ex, "Consume 出现未知问题. {eventType}", _eventType); } } }
//TODO: 做好详细的历史纪录,各个阶段都要打log。一有风吹草动,就立马删除SignInToken /// <returns>新的AccessToken</returns> public async Task <string> RefreshAccessTokenAsync(RefreshContext context, string lastUser) where User : User, new() where TUserClaim : UserClaim, new() where TRole : Role, new() where TRoleOfUser : RoleOfUser, new() { ThrowIf.NotValid(context); //解决并发涌入 using IDistributedLock distributedLock = await _lockManager.NoWaitLockAsync( nameof (RefreshAccessTokenAsync) + context.DeviceId, _options.RefreshIntervalTimeSpan).ConfigureAwait(false); if (!distributedLock.IsAcquired) { throw new AuthorizationException(ErrorCode.AuthorizationTooFrequent, $"Context:{SerializeUtil.ToJson(context)}"); } //AccessToken, Claims 验证 ClaimsPrincipal?claimsPrincipal = null; try { claimsPrincipal = ValidateTokenWithoutLifeCheck(context); } catch (Exception ex) { throw new AuthorizationException(ErrorCode.AuthorizationInvalideAccessToken, $"Context: {SerializeUtil.ToJson(context)}", ex); } //TODO: 这里缺DeviceId验证. 放在了StartupUtil.cs中 if (claimsPrincipal == null) { //TODO: Black concern SigninToken by RefreshToken throw new AuthorizationException(ErrorCode.AuthorizationInvalideAccessToken, $"Context: {SerializeUtil.ToJson(context)}"); } if (claimsPrincipal.GetDeviceId() != context.DeviceId) { throw new AuthorizationException(ErrorCode.AuthorizationInvalideDeviceId, $"Context: {SerializeUtil.ToJson(context)}"); } string?userGuid = claimsPrincipal.GetUserGuid(); if (string.IsNullOrEmpty(userGuid)) { throw new AuthorizationException(ErrorCode.AuthorizationInvalideUserGuid, $"Context: {SerializeUtil.ToJson(context)}"); } //SignInToken 验证 User? user; SignInToken?signInToken; //TransactionContext transactionContext = await _transaction.BeginTransactionAsync<SignInToken>().ConfigureAwait(false); try { signInToken = await _signInTokenBiz.GetAsync( claimsPrincipal.GetSignInTokenGuid(), context.RefreshToken, context.DeviceId, userGuid, ).ConfigureAwait(false); if (signInToken == null || signInToken.Blacked) { //await _database.RollbackAsync(transactionContext).ConfigureAwait(false); throw new AuthorizationException(ErrorCode.AuthorizationNoTokenInStore, $"Refresh token error. signInToken not saved in db. "); } //验证SignInToken过期问题 if (signInToken.ExpireAt < DateTimeOffset.UtcNow) { //await _transaction.RollbackAsync(transactionContext).ConfigureAwait(false); await BlackSignInTokenAsync(signInToken, lastUser).ConfigureAwait(false); throw new AuthorizationException(ErrorCode.AuthorizationRefreshTokenExpired, $"Refresh Token Expired."); } // User 信息变动验证 user = await _identityService.GetUserByUserGuidAsync(userGuid).ConfigureAwait(false); if (user == null || user.SecurityStamp != claimsPrincipal.GetUserSecurityStamp()) { //await _transaction.RollbackAsync(transactionContext).ConfigureAwait(false); await BlackSignInTokenAsync(signInToken, lastUser).ConfigureAwait(false); throw new AuthorizationException(ErrorCode.AuthorizationUserSecurityStampChanged, $"Refresh token error. User SecurityStamp Changed."); } // 更新SignInToken signInToken.RefreshCount++; await _signInTokenBiz.UpdateAsync(signInToken, lastUser, transactionContext).ConfigureAwait(false); await _transaction.CommitAsync(transactionContext).ConfigureAwait(false); } catch { await _transaction.RollbackAsync(transactionContext).ConfigureAwait(false); throw; } // 发布新的AccessToken return(await _jwtBuilder.BuildJwtAsync(user, signInToken, claimsPrincipal.GetAudience()).ConfigureAwait(false)); }