Example #1
0
        /// <summary>
        /// 反应Version变化
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="items"></param>
        /// <param name="lastUser"></param>
        /// <returns></returns>
        /// <exception cref="KVStoreException"></exception>
        public async Task UpdateAsync <T>(IEnumerable <T> items, string lastUser) where T : KVStoreEntity, new()
        {
            if (!items.Any())
            {
                return;
            }

            ThrowIf.NotValid(items, nameof(items));

            KVStoreEntityDef entityDef = EntityDefFactory.GetDef <T>();

            try
            {
                IEnumerable <int> originalVersions = items.Select(t => t.Version).ToArray();

                foreach (var t in items)
                {
                    t.LastUser = lastUser;
                    t.LastTime = TimeUtil.UtcNow;
                }

                await _engine.EntityUpdateAsync(
                    entityDef.KVStoreName,
                    entityDef.EntityType.FullName !,
                    items.Select(t => GetEntityKey(t, entityDef)),
                    items.Select(t => SerializeUtil.ToJson(t)),
                    originalVersions).ConfigureAwait(false);

                //反应Version变化
                foreach (var t in items)
                {
                    t.Version++;
                }
            }

            catch (Exception ex) when(!(ex is KVStoreException))
            {
                throw Exceptions.Unkown(entityDef.EntityType.FullName, entityDef.KVStoreName, items, ex);
            }
        }
        /// <summary>
        /// AddOrUpdateByIdAsync
        /// </summary>
        /// <param name="database"></param>
        /// <param name="item"></param>
        /// <param name="transContext"></param>
        /// <returns></returns>
        /// <exception cref="DatabaseException"></exception>
        public static async Task AddOrUpdateByIdAsync <T>(this IDatabase database, T item, TransactionContext?transContext = null) where T : DatabaseEntity, new()
        {
            ThrowIf.NotValid(item, nameof(item));

            EntityDef entityDef = EntityDefFactory.GetDef <T>() !;

            if (!entityDef.DatabaseWriteable)
            {
                throw Exceptions.NotWriteable(entityDef.EntityFullName, entityDef.DatabaseName);
            }

            try
            {
                item.LastTime = TimeUtil.UtcNow;

                if (item.Version < 0)
                {
                    item.Version = 0;
                }

                var command = DbCommandBuilder.CreateAddOrUpdateCommand(database.EngineType, entityDef, item);

                using var reader = await database.DatabaseEngine.ExecuteCommandReaderAsync(transContext?.Transaction, entityDef.DatabaseName !, command, true).ConfigureAwait(false);

                IList <T> entities = reader.ToEntities <T>(database.EngineType, entityDef);

                T newItem = entities[0];

                item.CreateTime = newItem.CreateTime;
                item.Version    = newItem.Version;
                item.LastUser   = newItem.LastUser;
            }
            catch (Exception ex) when(!(ex is DatabaseException))
            {
                throw Exceptions.UnKown(entityDef.EntityFullName, SerializeUtil.ToJson(item), ex);
            }
        }
        /// <summary>
        /// SignInAsync
        /// </summary>
        /// <param name="context"></param>
        /// <param name="lastUser"></param>
        /// <returns></returns>
        /// <exception cref="IdentityException"></exception>
        /// <exception cref="DatabaseException"></exception>
        /// <exception cref="KVStoreException"></exception>
        /// <exception cref="CacheException"></exception>
        public async Task <UserAccessResult> SignInAsync(SignInContext context, string lastUser)
        {
            ThrowIf.NotValid(context, nameof(context));

            switch (context.SignInType)
            {
            case SignInType.ByMobileAndPassword:
                ThrowIf.NullOrEmpty(context.Mobile, "SignInContext.Mobile");
                ThrowIf.NullOrEmpty(context.Password, "SignInContext.Password");
                break;

            case SignInType.BySms:
                ThrowIf.NullOrEmpty(context.Mobile, "SignInContext.Mobile");
                break;

            case SignInType.ByLoginNameAndPassword:
                ThrowIf.NullOrEmpty(context.LoginName, "SignInContext.LoginName");
                ThrowIf.NullOrEmpty(context.Password, "SignInContext.Password");
                break;

            default:
                break;
            }

            TransactionContext transactionContext = await _transaction.BeginTransactionAsync <SignInToken>().ConfigureAwait(false);

            try
            {
                //查询用户
                User?user = context.SignInType switch
                {
                    SignInType.ByLoginNameAndPassword => await _userRepo.GetByLoginNameAsync(context.LoginName !, transactionContext).ConfigureAwait(false),
                    SignInType.BySms => await _userRepo.GetByMobileAsync(context.Mobile !, transactionContext).ConfigureAwait(false),
                    SignInType.ByMobileAndPassword => await _userRepo.GetByMobileAsync(context.Mobile !, transactionContext).ConfigureAwait(false),
                    _ => null
                };

                //不存在,则新建用户

                if (user == null && context.SignInType == SignInType.BySms)
                {
                    user = await _identityService.CreateUserAsync(context.Mobile !, null, context.LoginName, context.Password, true, false, lastUser, transactionContext).ConfigureAwait(false);
                }

                if (user == null)
                {
                    throw Exceptions.AuthorizationNotFound(signInContext: context);
                }

                UserLoginControl userLoginControl = await GetOrCreateUserLoginControlAsync(lastUser, user.Id).ConfigureAwait(false);

                //密码检查
                if (context.SignInType == SignInType.ByMobileAndPassword || context.SignInType == SignInType.ByLoginNameAndPassword)
                {
                    if (!PassowrdCheck(user, context.Password !))
                    {
                        await OnSignInFailedAsync(userLoginControl, lastUser).ConfigureAwait(false);

                        throw Exceptions.AuthorizationPasswordWrong(signInContext: context);
                    }
                }

                //其他检查
                await PreSignInCheckAsync(user, userLoginControl, lastUser).ConfigureAwait(false);

                //注销其他客户端
                await DeleteSignInTokensAsync(user.Id, context.DeviceInfos.Idiom, context.LogOffType, context.DeviceInfos.Name, transactionContext).ConfigureAwait(false);

                //创建Token

                SignInToken signInToken = new SignInToken
                                          (
                    userId: user.Id,
                    refreshToken: SecurityUtil.CreateUniqueToken(),
                    expireAt: TimeUtil.UtcNow + (context.RememberMe ? _options.SignInOptions.RefreshTokenLongExpireTimeSpan : _options.SignInOptions.RefreshTokenShortExpireTimeSpan),
                    deviceId: context.DeviceId,
                    deviceVersion: context.DeviceVersion,
                    deviceIp: context.DeviceIp,

                    deviceName: context.DeviceInfos.Name,
                    deviceModel: context.DeviceInfos.Model,
                    deviceOSVersion: context.DeviceInfos.OSVersion,
                    devicePlatform: context.DeviceInfos.Platform,
                    deviceIdiom: context.DeviceInfos.Idiom,
                    deviceType: context.DeviceInfos.Type
                                          );

                await _signInTokenRepo.AddAsync(signInToken, lastUser, transactionContext).ConfigureAwait(false);

                //构造 Jwt
                string jwt = await ConstructJwtAsync(user, signInToken, context.SignToWhere, transactionContext).ConfigureAwait(false);

                UserAccessResult result = new UserAccessResult
                                          (
                    accessToken: jwt,
                    refreshToken: signInToken.RefreshToken,
                    currentUser: user
                                          );

                await _transaction.CommitAsync(transactionContext).ConfigureAwait(false);

                return(result);
            }
            catch
            {
                await _transaction.RollbackAsync(transactionContext).ConfigureAwait(false);

                throw;
            }
        }
        /// <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;
            }
        }
Example #5
0
        //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));
        }
Example #6
0
        public async Task <SignInResult> SignInAsync(SignInContext context, string lastUser)
        {
            ThrowIf.NotValid(context);

            switch (context.SignInType)
            {
            case SignInType.ByMobileAndPassword:
                ThrowIf.NullOrEmpty(context.Mobile, "SignInContext.Mobile");
                ThrowIf.NullOrEmpty(context.Password, "SignInContext.Password");
                break;

            case SignInType.BySms:
                ThrowIf.NullOrEmpty(context.Mobile, "SignInContext.Mobile");
                break;

            case SignInType.ByLoginNameAndPassword:
                ThrowIf.NullOrEmpty(context.LoginName, "SignInContext.LoginName");
                ThrowIf.NullOrEmpty(context.Password, "SignInContext.Password");
                break;

            default:
                break;
            }

            TransactionContext transactionContext = await _transaction.BeginTransactionAsync <SignInToken>().ConfigureAwait(false);

            try
            {
                //查询用户
                User?user = context.SignInType switch
                {
                    SignInType.ByLoginNameAndPassword => await _identityService.GetUserByLoginNameAsync(context.LoginName !).ConfigureAwait(false),
                    SignInType.BySms => await _identityService.GetUserByMobileAsync(context.Mobile !).ConfigureAwait(false),
                    SignInType.ByMobileAndPassword => await _identityService.GetUserByMobileAsync(context.Mobile !).ConfigureAwait(false),
                    _ => null
                };

                //不存在,则新建用户
                bool newUserCreated = false;

                if (user == null && context.SignInType == SignInType.BySms)
                {
                    user = await _identityService.CreateUserAsync(
                        mobile : context.Mobile !,
                        email : null,
                        loginName : context.LoginName,
                        password : context.Password,
                        mobileConfirmed : true,
                        emailConfirmed : false,
                        lastUser : lastUser).ConfigureAwait(false);

                    newUserCreated = true;
                }

                if (user == null)
                {
                    throw new AuthorizationException(ErrorCode.AuthorizationNotFound, $"SignInContext:{SerializeUtil.ToJson(context)}");
                }

                //密码检查
                if (context.SignInType == SignInType.ByMobileAndPassword || context.SignInType == SignInType.ByLoginNameAndPassword)
                {
                    if (!PassowrdCheck(user, context.Password !))
                    {
                        await OnPasswordCheckFailedAsync(user, lastUser).ConfigureAwait(false);

                        throw new AuthorizationException(ErrorCode.AuthorizationPasswordWrong, $"SignInContext:{SerializeUtil.ToJson(context)}");
                    }
                }

                //其他检查
                await PreSignInCheckAsync(user, lastUser).ConfigureAwait(false);

                //注销其他客户端
                await _signInTokenBiz.DeleteByLogOffTypeAsync(user.Guid, context.DeviceInfos.Idiom, context.LogOffType, context.DeviceInfos.Name, transactionContext).ConfigureAwait(false);

                //创建Token
                SignInToken userToken = await _signInTokenBiz.CreateAsync(
                    user.Guid,
                    context.DeviceId,
                    context.DeviceInfos,
                    context.DeviceVersion,
                    //context.DeviceAddress,
                    context.DeviceIp,
                    context.RememberMe?_options.SignInOptions.RefreshTokenLongExpireTimeSpan : _options.SignInOptions.RefreshTokenShortExpireTimeSpan,
                    lastUser,
                    transactionContext).ConfigureAwait(false);

                //构造 Jwt



                IEnumerable <Claim> claims = GetClaims(user, transactionContext);

                SignInResult result = new SignInResult
                                      (
                    accessToken: await JwtHelper.BuildJwt(user, userToken, context.SignToWhere).ConfigureAwait(false),
                    refreshToken: userToken.RefreshToken,
                    newUserCreated: newUserCreated,
                    currentUser: user
                                      );

                await _transaction.CommitAsync(transactionContext).ConfigureAwait(false);

                return(result);
            }
            catch
            {
                await _transaction.RollbackAsync(transactionContext).ConfigureAwait(false);

                throw;
            }
        }
        /// <summary>
        /// SignInAsync
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        /// <exception cref="HB.Framework.Common.ValidateErrorException"></exception>
        /// <exception cref="HB.Component.Authorization.AuthorizationException"></exception>
        /// <exception cref="DatabaseException"></exception>
        public async Task <SignInResult> SignInAsync <TUser, TUserClaim, TRole, TRoleOfUser>(SignInContext context)
            where TUser : IdenityUser, new()
            where TUserClaim : IdentityUserClaim, new()
            where TRole : IdentityRole, new()
            where TRoleOfUser : IdentityRoleOfUser, new()
        {
            ThrowIf.NotValid(context);

            switch (context.SignInType)
            {
            case SignInType.ByMobileAndPassword:
                ThrowIf.NullOrEmpty(context.Mobile, "SignInContext.Mobile");
                ThrowIf.NullOrEmpty(context.Password, "SignInContext.Password");
                break;

            case SignInType.BySms:
                ThrowIf.NullOrEmpty(context.Mobile, "SignInContext.Mobile");
                break;

            case SignInType.ByLoginNameAndPassword:
                ThrowIf.NullOrEmpty(context.LoginName, "SignInContext.LoginName");
                ThrowIf.NullOrEmpty(context.Password, "SignInContext.Password");
                break;

            default:
                break;
            }

            TransactionContext transactionContext = await _database.BeginTransactionAsync <SignInToken>(IsolationLevel.ReadCommitted).ConfigureAwait(false);

            try
            {
                //查询用户
                TUser?user = context.SignInType switch
                {
                    SignInType.ByLoginNameAndPassword => await _identityService.GetUserByLoginNameAsync <TUser>(context.LoginName !).ConfigureAwait(false),
                    SignInType.BySms => await _identityService.GetUserByMobileAsync <TUser>(context.Mobile !).ConfigureAwait(false),
                    SignInType.ByMobileAndPassword => await _identityService.GetUserByMobileAsync <TUser>(context.Mobile !).ConfigureAwait(false),
                    _ => null
                };

                //不存在,则新建用户
                bool newUserCreated = false;

                if (user == null && context.SignInType == SignInType.BySms)
                {
                    user = await _identityService.CreateUserByMobileAsync <TUser>(context.Mobile !, context.LoginName, context.Password, true).ConfigureAwait(false);

                    newUserCreated = true;
                }

                if (user == null)
                {
                    throw new AuthorizationException(ErrorCode.AuthorizationNotFound, $"SignInContext:{SerializeUtil.ToJson(context)}");
                }

                //密码检查
                if (context.SignInType == SignInType.ByMobileAndPassword || context.SignInType == SignInType.ByLoginNameAndPassword)
                {
                    if (!PassowrdCheck(user, context.Password !))
                    {
                        await OnPasswordCheckFailedAsync(user).ConfigureAwait(false);

                        throw new AuthorizationException(ErrorCode.AuthorizationPasswordWrong, $"SignInContext:{SerializeUtil.ToJson(context)}");
                    }
                }

                //其他检查
                await PreSignInCheckAsync(user).ConfigureAwait(false);

                //注销其他客户端
                DeviceType clientType = DeviceTypeChecker.Check(context.DeviceType);

                if (clientType != DeviceType.Web && _signInOptions.AllowOnlyOneAppClient)
                {
                    await _signInTokenBiz.DeleteAppClientTokenByUserGuidAsync(user.Guid, transactionContext).ConfigureAwait(false);
                }

                //创建Token
                SignInToken userToken = await _signInTokenBiz.CreateAsync(
                    user.Guid,
                    context.DeviceId,
                    clientType.ToString(),
                    context.DeviceVersion,
                    //context.DeviceAddress,
                    context.DeviceIp,
                    context.RememberMe?_signInOptions.RefreshTokenLongExpireTimeSpan : _signInOptions.RefreshTokenShortExpireTimeSpan,
                    transactionContext).ConfigureAwait(false);

                await _database.CommitAsync(transactionContext).ConfigureAwait(false);

                //构造 Jwt
                SignInResult result = new SignInResult
                                      (
                    accessToken: await _jwtBuilder.BuildJwtAsync <TUserClaim, TRole, TRoleOfUser>(user, userToken, context.SignToWhere).ConfigureAwait(false),
                    refreshToken: userToken.RefreshToken,
                    newUserCreated: newUserCreated,
                    currentUser: user
                                      );

                return(result);
            }
            catch
            {
                await _database.RollbackAsync(transactionContext).ConfigureAwait(false);

                throw;
            }
        }