• .net core2.x


    一个系统组织架构是不可少的,所以 认证(authentication) 和 授权(authorization)是必不可少的,但是 微软很人性化的为我们提供了Identity这个东西,嗯,是好东西,但是总是稍微有那么一点不足,就是命名,以及部分字段可能用不到。没关系,我们可以自定义。

      1.第三方或官方提供的便捷方式?

        1.1.默认的IdentityUser,IdentityRole :

        

         生成方式没有什么好说的,创建项目时,修改下身份验证方式即可见到,如上面的表结构。这里涉及到 Claims identityClaims,如果还不知道是什么 看这里  应该是最简的总结了吧。这里很明显的,默认生成的表名称前缀是AspNet打头,,,根本不需要是不是。而且需要使用到 IdentityDbContext这个对象进行 迁移,那么如果我们的项目中已经自定义或者已有一个上下文对象,这两个是不是有些冲突?该用哪个呢?这里的 IdentityDbContext定义如下(Microsoft.AspNetCore.Identity.EntityFrameworkCore下定义的):

        

        1.2.ids4(identityServer4)生成的表如下:

        

        不得不说ids4很方便,但是有点多有点乱,不是我们想要的,所以怎么办呢?

         

       2.简要分析。

        按照我们以前用FX的时候的一贯逻辑,知道identity使用SignInManager,但是SignInManager使用到一个 UserManager对象,而这个UserManager使用到了 UserStore这个东西,好,这样的话我们就打开core.Indetity的源码再看看访问数据那里是如何定义的(因为我们一开始就说了,要自定义或者使用现有的模型)。

        

        很明显,思路也很明确,UserStore的实现,还是继承自 Identity.EntityFramework下的一个UserStore,继续跟进: 

       

        到这里就很直观了,Framework下面的这个UserStore实现了一系列的接口,在构造函数中注入了默认的上下问对象DbContext,呵呵,找就找到了根源了,那么我们就重新实现这个UserStore就可以了,将其中的数据上下文DbContext换成我们自己的就好了,我这么说没意见吧?

      

      3.重定义UserStore

        定义前我们要注意下,2中,泛型涉及到的 参数类型,我们都需要定义,可以直接使用core Identity中定义的,改下名称即可,我们自定义的UserStore名称我依旧定义成UserStore.cs:  (有点长。。。) 

    public class UserStore : IQueryableUserStore<User>,
              IUserLoginStore<User>,
              IUserClaimStore<User>,
              IUserPasswordStore<User>,
              IUserSecurityStampStore<User>,
              IUserEmailStore<User>,
              IUserLockoutStore<User>,
              IUserPhoneNumberStore<User>,
              IUserTwoFactorStore<User>,
              IUserAuthenticationTokenStore<User>,
              IUserAuthenticatorKeyStore<User>,
              IUserTwoFactorRecoveryCodeStore<User>,
              IUserRoleStore<User> /*<User, Guid, UserClain, UserLogin, UserToken, Role, Guid, UserRole>*/
        {
    
            #region ctor
            public UserStore(IUnitOfWork unitOfWork)
            {
                _unitOfWork = unitOfWork;
                _userRepository = _unitOfWork.Repository<User, Guid>();
                _roleRepository = _unitOfWork.Repository<Role, Guid>();
                _userRoleRepository = _unitOfWork.Repository<UserRole, Guid>();
                _userLoginRepository = _unitOfWork.Repository<UserLogin, Guid>();
                _userClaimRepository = _unitOfWork.Repository<UserClaim, Guid>();
                _userTokenRepository = _unitOfWork.Repository<UserToken, Guid>();
            }
            #endregion
    
            #region  fields
            private bool _disposed;
            private readonly IUnitOfWork _unitOfWork;
            private readonly IRepository<User, Guid> _userRepository;
            private readonly IRepository<Role, Guid> _roleRepository;
            private readonly IRepository<UserRole, Guid> _userRoleRepository;
            private readonly IRepository<UserLogin, Guid> _userLoginRepository;
            private readonly IRepository<UserClaim, Guid> _userClaimRepository;
            private readonly IRepository<UserToken, Guid> _userTokenRepository;
            #endregion
    
            #region Implementation of IQueryableUserStore<User>
    
            /// <summary>
            /// Returns an <see cref="T:System.Linq.IQueryable`1" /> collection of users.
            /// </summary>
            /// <value>An <see cref="T:System.Linq.IQueryable`1" /> collection of users.</value>
            public IQueryable<User> Users => _userRepository.Query(x => x.ID != null);
    
            #endregion
    
            #region Implementation of IDisposable
    
            /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
            public void Dispose()
            {
                _disposed = true;
            }
    
            #endregion
    
            #region Implementation of IUserStore<User>
    
            /// <summary>
            /// Gets the user identifier for the specified <paramref name="user" />.
            /// </summary>
            /// <param name="user">The user whose identifier should be retrieved.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing the identifier for the specified <paramref name="user" />.</returns>
            public Task<string> GetUserIdAsync(User user, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                return Task.FromResult(ConvertIdToString(user.ID));
            }
    
            /// <summary>
            /// Gets the user name for the specified <paramref name="user" />.
            /// </summary>
            /// <param name="user">The user whose name should be retrieved.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing the name for the specified <paramref name="user" />.</returns>
            public Task<string> GetUserNameAsync(User user, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                return Task.FromResult(user.UserName);
            }
    
            /// <summary>
            /// Sets the given <paramref name="userName" /> for the specified <paramref name="user" />.
            /// </summary>
            /// <param name="user">The user whose name should be set.</param>
            /// <param name="userName">The user name to set.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
            public Task SetUserNameAsync(User user, string userName, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                user.UserName = userName;
                return Task.CompletedTask;
            }
    
            /// <summary>
            /// Gets the normalized user name for the specified <paramref name="user" />.
            /// </summary>
            /// <param name="user">The user whose normalized name should be retrieved.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing the normalized user name for the specified <paramref name="user" />.</returns>
            public Task<string> GetNormalizedUserNameAsync(User user, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                return Task.FromResult(user.UserName);
            }
    
            /// <summary>
            /// Sets the given normalized name for the specified <paramref name="user" />.
            /// </summary>
            /// <param name="user">The user whose name should be set.</param>
            /// <param name="normalizedName">The normalized name to set.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
            public Task SetNormalizedUserNameAsync(User user, string normalizedName, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                user.UserName = normalizedName;
                return Task.CompletedTask;
            }
    
            /// <summary>
            /// Creates the specified <paramref name="user" /> in the user store.
            /// </summary>
            /// <param name="user">The user to create.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing the <see cref="T:Microsoft.AspNetCore.Identity.IdentityResult" /> of the creation operation.</returns>
            public async Task<IdentityResult> CreateAsync(User user, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                await _userRepository.InsertAsync(user);
    
                //系统的第一个用户,自动成为超级管理员
                int count = _userRepository.Query(x => x.ID != null).Count();
                if (count == 1)
                {
                    Role adminRole = _roleRepository.Query().FirstOrDefault();
                    if (adminRole != null)
                    {
                        UserRole userRole = new UserRole() { UserId = user.ID, RoleId = adminRole.ID };
                        await _userRoleRepository.InsertAsync(userRole);
    
                        user.IsSystem = true;
                        _userRepository.Update(user);
                    }
                }
    
                //默认角色
                Role defaultRole = _roleRepository.Query().FirstOrDefault(m => m.IsDefault);
                if (defaultRole != null)
                {
                    UserRole userRole = new UserRole() { UserId = user.ID, RoleId = defaultRole.ID };
                    await _userRoleRepository.InsertAsync(userRole);
                }
                await _unitOfWork.CommitAsync();
                return IdentityResult.Success;
            }
    
            /// <summary>
            /// Updates the specified <paramref name="user" /> in the user store.
            /// </summary>
            /// <param name="user">The user to update.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing the <see cref="T:Microsoft.AspNetCore.Identity.IdentityResult" /> of the update operation.</returns>
            public async Task<IdentityResult> UpdateAsync(User user, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                if (user.Email.IsMissing())
                {
                    user.EmailConfirmed = false;
                }
                if (user.PhoneNumber.IsMissing())
                {
                    user.PhoneNumberConfirmed = false;
                }
    
                _userRepository.Update(user);
                await _unitOfWork.CommitAsync();
                return IdentityResult.Success;
            }
    
            /// <summary>
            /// Deletes the specified <paramref name="user" /> from the user store.
            /// </summary>
            /// <param name="user">The user to delete.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing the <see cref="T:Microsoft.AspNetCore.Identity.IdentityResult" /> of the update operation.</returns>
            public async Task<IdentityResult> DeleteAsync(User user, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                if (user.IsSystem)
                {
                    return new IdentityResult().Failed($"用户“{user.UserName}”是系统用户,不能删除");
                }
                _userRepository.Remove(user);
                await _unitOfWork.CommitAsync();
                return IdentityResult.Success;
            }
    
            /// <summary>
            /// Finds and returns a user, if any, who has the specified <paramref name="userId" />.
            /// </summary>
            /// <param name="userId">The user ID to search for.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>
            /// The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing the user matching the specified <paramref name="userId" /> if it exists.
            /// </returns>
            public Task<User> FindByIdAsync(string userId, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
    
                Guid id = ConvertIdFromString(userId);
                return Task.FromResult(_userRepository.GetByKey(id));
            }
    
            /// <summary>
            /// Finds and returns a user, if any, who has the specified normalized user name.
            /// </summary>
            /// <param name="normalizedUserName">The normalized user name to search for.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>
            /// The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing the user matching the specified <paramref name="normalizedUserName" /> if it exists.
            /// </returns>
            public Task<User> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
    
                return Task.FromResult(_userRepository.Query().FirstOrDefault(m => m.UserName == normalizedUserName));
            }
    
            #endregion
    
            #region Implementation of IUserLoginStore<User>
    
            /// <summary>
            /// Adds an external <see cref="T:Microsoft.AspNetCore.Identity.UserLoginInfo" /> to the specified <paramref name="user" />.
            /// </summary>
            /// <param name="user">The user to add the login to.</param>
            /// <param name="login">The external <see cref="T:Microsoft.AspNetCore.Identity.UserLoginInfo" /> to add to the specified <paramref name="user" />.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
            public async Task AddLoginAsync(User user, UserLoginInfo login, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
                Check.NotNull(login, nameof(login));
    
                UserLogin userLogin = new UserLogin()
                {
                    UserId = user.ID,
                    LoginProvider = login.LoginProvider,
                    ProviderKey = login.ProviderKey,
                    ProviderDisplayName = login.ProviderDisplayName
                };
                await _userLoginRepository.InsertAsync(userLogin);
                await _unitOfWork.CommitAsync();
            }
    
            /// <summary>
            /// Attempts to remove the provided login information from the specified <paramref name="user" />.
            /// and returns a flag indicating whether the removal succeed or not.
            /// </summary>
            /// <param name="user">The user to remove the login information from.</param>
            /// <param name="loginProvider">The login provide whose information should be removed.</param>
            /// <param name="providerKey">The key given by the external login provider for the specified user.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
            public async Task RemoveLoginAsync(User user, string loginProvider, string providerKey, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
                Check.NotNullOrEmpty(loginProvider, nameof(loginProvider));
                Check.NotNullOrEmpty(providerKey, nameof(providerKey));
    
                _userLoginRepository.Remove(m => m.UserId.Equals(user.ID) && m.LoginProvider == loginProvider && m.ProviderKey == providerKey);
                await _unitOfWork.CommitAsync();
            }
    
            /// <summary>
            /// Retrieves the associated logins for the specified <param ref="user" />.
            /// </summary>
            /// <param name="user">The user whose associated logins to retrieve.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>
            /// The <see cref="T:System.Threading.Tasks.Task" /> for the asynchronous operation, containing a list of <see cref="T:Microsoft.AspNetCore.Identity.UserLoginInfo" /> for the specified <paramref name="user" />, if any.
            /// </returns>
            public Task<IList<UserLoginInfo>> GetLoginsAsync(User user, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                IList<UserLoginInfo> loginInfos = _userLoginRepository.Query(m => m.UserId.Equals(user.ID)).Select(m =>
                    new UserLoginInfo(m.LoginProvider, m.ProviderKey, m.ProviderDisplayName)).ToList();
                return Task.FromResult(loginInfos);
            }
    
            /// <summary>
            /// Retrieves the user associated with the specified login provider and login provider key.
            /// </summary>
            /// <param name="loginProvider">The login provider who provided the <paramref name="providerKey" />.</param>
            /// <param name="providerKey">The key provided by the <paramref name="loginProvider" /> to identify a user.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>
            /// The <see cref="T:System.Threading.Tasks.Task" /> for the asynchronous operation, containing the user, if any which matched the specified login provider and key.
            /// </returns>
            public Task<User> FindByLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNullOrEmpty(loginProvider, nameof(loginProvider));
                Check.NotNullOrEmpty(providerKey, nameof(providerKey));
    
                Guid userId = _userLoginRepository.Query(m => m.LoginProvider == loginProvider && m.ProviderKey == providerKey)
                    .Select(m => m.UserId).FirstOrDefault();
                if (Equals(userId, default(Guid)))
                {
                    return Task.FromResult(default(User));
                }
                User user = _userRepository.GetByKey(userId);
                return Task.FromResult(user);
            }
    
            #endregion
    
            #region Implementation of IUserClaimStore<User>
    
            /// <summary>
            /// Gets a list of <see cref="T:System.Security.Claims.Claim" />s to be belonging to the specified <paramref name="user" /> as an asynchronous operation.
            /// </summary>
            /// <param name="user">The role whose claims to retrieve.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>
            /// A <see cref="T:System.Threading.Tasks.Task`1" /> that represents the result of the asynchronous query, a list of <see cref="T:System.Security.Claims.Claim" />s.
            /// </returns>
            public Task<IList<Claim>> GetClaimsAsync(User user, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                IList<Claim> claims = _userClaimRepository.Query(m => m.UserId.Equals(user.ID))
                    .Select(m => new Claim(m.ClaimType, m.ClaimValue)).ToList();
                return Task.FromResult(claims);
            }
    
            /// <summary>Add claims to a user as an asynchronous operation.</summary>
            /// <param name="user">The user to add the claim to.</param>
            /// <param name="claims">The collection of <see cref="T:System.Security.Claims.Claim" />s to add.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The task object representing the asynchronous operation.</returns>
            public async Task AddClaimsAsync(User user, IEnumerable<Claim> claims, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                UserClaim[] userClaims = claims.Select(m => new UserClaim() { UserId = user.ID, ClaimType = m.Type, ClaimValue = m.Value }).ToArray();
                await _userClaimRepository.InsertAsync(userClaims);
                await _unitOfWork.CommitAsync();
            }
    
            /// <summary>
            /// Replaces the given <paramref name="claim" /> on the specified <paramref name="user" /> with the <paramref name="newClaim" />
            /// </summary>
            /// <param name="user">The user to replace the claim on.</param>
            /// <param name="claim">The claim to replace.</param>
            /// <param name="newClaim">The new claim to replace the existing <paramref name="claim" /> with.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The task object representing the asynchronous operation.</returns>
            public Task ReplaceClaimAsync(User user, Claim claim, Claim newClaim, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                List<UserClaim> userClaims = _userClaimRepository.Query(m => m.UserId.Equals(user.ID) && m.ClaimType == claim.Type && m.ClaimValue == claim.Value).ToList();
                foreach (UserClaim userClaim in userClaims)
                {
                    userClaim.ClaimType = newClaim.Type;
                    userClaim.ClaimValue = newClaim.Value;
                }
                return Task.CompletedTask;
            }
    
            /// <summary>
            /// Removes the specified <paramref name="claims" /> from the given <paramref name="user" />.
            /// </summary>
            /// <param name="user">The user to remove the specified <paramref name="claims" /> from.</param>
            /// <param name="claims">A collection of <see cref="T:System.Security.Claims.Claim" />s to remove.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The task object representing the asynchronous operation.</returns>
            public async Task RemoveClaimsAsync(User user, IEnumerable<Claim> claims, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                _userClaimRepository.Remove(m =>
                    m.UserId.Equals(user.ID) && claims.Any(n => n.Type == m.ClaimType && n.Value == m.ClaimValue));
                await _unitOfWork.CommitAsync();
            }
    
            /// <summary>
            /// Returns a list of users who contain the specified <see cref="T:System.Security.Claims.Claim" />.
            /// </summary>
            /// <param name="claim">The claim to look for.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>
            /// A <see cref="T:System.Threading.Tasks.Task`1" /> that represents the result of the asynchronous query, a list of <typeparamref name="User" /> who
            /// contain the specified claim.
            /// </returns>
            public Task<IList<User>> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(claim, nameof(claim));
    
                Guid[] userIds = _userClaimRepository.Query(m => m.ClaimType == claim.Type && m.ClaimValue == claim.Value)
                    .Select(m => m.UserId).ToArray();
                IList<User> users = _userRepository.Query(m => userIds.Contains(m.ID)).ToList();
                return Task.FromResult(users);
            }
    
            #endregion
    
            #region Implementation of IUserPasswordStore<User>
    
            /// <summary>
            /// Sets the password hash for the specified <paramref name="user" />.
            /// </summary>
            /// <param name="user">The user whose password hash to set.</param>
            /// <param name="passwordHash">The password hash to set.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
            public Task SetPasswordHashAsync(User user, string passwordHash, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                user.PasswordHash = passwordHash;
                return Task.CompletedTask;
            }
    
            /// <summary>
            /// Gets the password hash for the specified <paramref name="user" />.
            /// </summary>
            /// <param name="user">The user whose password hash to retrieve.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, returning the password hash for the specified <paramref name="user" />.</returns>
            public Task<string> GetPasswordHashAsync(User user, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                return Task.FromResult(user.PasswordHash);
            }
    
            /// <summary>
            /// Gets a flag indicating whether the specified <paramref name="user" /> has a password.
            /// </summary>
            /// <param name="user">The user to return a flag for, indicating whether they have a password or not.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>
            /// The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, returning true if the specified <paramref name="user" /> has a password
            /// otherwise false.
            /// </returns>
            public Task<bool> HasPasswordAsync(User user, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                return Task.FromResult(!string.IsNullOrEmpty(user.PasswordHash));
            }
    
            #endregion
    
            #region Implementation of IUserSecurityStampStore<User>
    
            /// <summary>
            /// Sets the provided security <paramref name="stamp" /> for the specified <paramref name="user" />.
            /// </summary>
            /// <param name="user">The user whose security stamp should be set.</param>
            /// <param name="stamp">The security stamp to set.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
            public Task SetSecurityStampAsync(User user, string stamp, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                user.SecurityStamp = stamp;
    
                //移除用户在线缓存
                //OnlineUserCacheRemoveEventData eventData = new OnlineUserCacheRemoveEventData() { UserNames = new[] { user.UserName } };
                //_eventBus.Publish(eventData);
    
                return Task.CompletedTask;
            }
    
            /// <summary>
            /// Get the security stamp for the specified <paramref name="user" />.
            /// </summary>
            /// <param name="user">The user whose security stamp should be set.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing the security stamp for the specified <paramref name="user" />.</returns>
            public Task<string> GetSecurityStampAsync(User user, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                return Task.FromResult(user.SecurityStamp);
            }
    
            #endregion
    
            #region Implementation of IUserEmailStore<User>
    
            /// <summary>
            /// Sets the <paramref name="email" /> address for a <paramref name="user" />.
            /// </summary>
            /// <param name="user">The user whose email should be set.</param>
            /// <param name="email">The email to set.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The task object representing the asynchronous operation.</returns>
            public Task SetEmailAsync(User user, string email, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
                Check.NotNull(email, nameof(email));
    
                user.Email = email;
                return Task.CompletedTask;
            }
    
            /// <summary>
            /// Gets the email address for the specified <paramref name="user" />.
            /// </summary>
            /// <param name="user">The user whose email should be returned.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The task object containing the results of the asynchronous operation, the email address for the specified <paramref name="user" />.</returns>
            public Task<string> GetEmailAsync(User user, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                return Task.FromResult(user.Email);
            }
    
            /// <summary>
            /// Gets a flag indicating whether the email address for the specified <paramref name="user" /> has been verified, true if the email address is verified otherwise
            /// false.
            /// </summary>
            /// <param name="user">The user whose email confirmation status should be returned.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>
            /// The task object containing the results of the asynchronous operation, a flag indicating whether the email address for the specified <paramref name="user" />
            /// has been confirmed or not.
            /// </returns>
            public Task<bool> GetEmailConfirmedAsync(User user, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                return Task.FromResult(user.EmailConfirmed);
            }
    
            /// <summary>
            /// Sets the flag indicating whether the specified <paramref name="user" />'s email address has been confirmed or not.
            /// </summary>
            /// <param name="user">The user whose email confirmation status should be set.</param>
            /// <param name="confirmed">A flag indicating if the email address has been confirmed, true if the address is confirmed otherwise false.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The task object representing the asynchronous operation.</returns>
            public Task SetEmailConfirmedAsync(User user, bool confirmed, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                user.EmailConfirmed = true;
                return Task.CompletedTask;
            }
    
            /// <summary>
            /// Gets the user, if any, associated with the specified, normalized email address.
            /// </summary>
            /// <param name="normalizedEmail">The normalized email address to return the user for.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>
            /// The task object containing the results of the asynchronous lookup operation, the user if any associated with the specified normalized email address.
            /// </returns>
            public Task<User> FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
    
                User user = _userRepository.Query().FirstOrDefault(m => m.Email == normalizedEmail);
                return Task.FromResult(user);
            }
    
            /// <summary>
            /// Returns the normalized email for the specified <paramref name="user" />.
            /// </summary>
            /// <param name="user">The user whose email address to retrieve.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>
            /// The task object containing the results of the asynchronous lookup operation, the normalized email address if any associated with the specified user.
            /// </returns>
            public Task<string> GetNormalizedEmailAsync(User user, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                return Task.FromResult(user.UserName);
            }
    
            /// <summary>
            /// Sets the normalized email for the specified <paramref name="user" />.
            /// </summary>
            /// <param name="user">The user whose email address to set.</param>
            /// <param name="normalizedEmail">The normalized email to set for the specified <paramref name="user" />.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The task object representing the asynchronous operation.</returns>
            public Task SetNormalizedEmailAsync(User user, string normalizedEmail, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                user.Email = normalizedEmail;
                return Task.CompletedTask;
            }
    
            #endregion
    
            #region Implementation of IUserLockoutStore<User>
    
            /// <summary>
            /// Gets the last <see cref="T:System.DateTimeOffset" /> a user's last lockout expired, if any.
            /// Any time in the past should be indicates a user is not locked out.
            /// </summary>
            /// <param name="user">The user whose lockout date should be retrieved.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>
            /// A <see cref="T:System.Threading.Tasks.Task`1" /> that represents the result of the asynchronous query, a <see cref="T:System.DateTimeOffset" /> containing the last time
            /// a user's lockout expired, if any.
            /// </returns>
            public Task<DateTimeOffset?> GetLockoutEndDateAsync(User user, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                return Task.FromResult(user.LockoutEnd);
            }
    
            /// <summary>
            /// Locks out a user until the specified end date has passed. Setting a end date in the past immediately unlocks a user.
            /// </summary>
            /// <param name="user">The user whose lockout date should be set.</param>
            /// <param name="lockoutEnd">The <see cref="T:System.DateTimeOffset" /> after which the <paramref name="user" />'s lockout should end.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
            public Task SetLockoutEndDateAsync(User user, DateTimeOffset? lockoutEnd, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                user.LockoutEnd = lockoutEnd;
                return Task.CompletedTask;
            }
    
            /// <summary>
            /// Records that a failed access has occurred, incrementing the failed access count.
            /// </summary>
            /// <param name="user">The user whose cancellation count should be incremented.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing the incremented failed access count.</returns>
            public Task<int> IncrementAccessFailedCountAsync(User user, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                user.AccessFailedCount++;
                return Task.FromResult(user.AccessFailedCount);
            }
    
            /// <summary>Resets a user's failed access count.</summary>
            /// <param name="user">The user whose failed access count should be reset.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
            /// <remarks>This is typically called after the account is successfully accessed.</remarks>
            public Task ResetAccessFailedCountAsync(User user, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                user.AccessFailedCount = 0;
                return Task.CompletedTask;
            }
    
            /// <summary>
            /// Retrieves the current failed access count for the specified <paramref name="user" />.
            /// </summary>
            /// <param name="user">The user whose failed access count should be retrieved.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing the failed access count.</returns>
            public Task<int> GetAccessFailedCountAsync(User user, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                return Task.FromResult(user.AccessFailedCount);
            }
    
            /// <summary>
            /// Retrieves a flag indicating whether user lockout can enabled for the specified user.
            /// </summary>
            /// <param name="user">The user whose ability to be locked out should be returned.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>
            /// The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, true if a user can be locked out, otherwise false.
            /// </returns>
            public Task<bool> GetLockoutEnabledAsync(User user, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
                //TODO
                return Task.FromResult(true);
            }
    
            /// <summary>
            /// Set the flag indicating if the specified <paramref name="user" /> can be locked out.
            /// </summary>
            /// <param name="user">The user whose ability to be locked out should be set.</param>
            /// <param name="enabled">A flag indicating if lock out can be enabled for the specified <paramref name="user" />.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
            public Task SetLockoutEnabledAsync(User user, bool enabled, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                //user.LockoutEnabled = enabled;
                return Task.CompletedTask;
            }
    
            #endregion
    
            #region Implementation of IUserPhoneNumberStore<User>
    
            /// <summary>
            /// Sets the telephone number for the specified <paramref name="user" />.
            /// </summary>
            /// <param name="user">The user whose telephone number should be set.</param>
            /// <param name="phoneNumber">The telephone number to set.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
            public Task SetPhoneNumberAsync(User user, string phoneNumber, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                user.PhoneNumber = phoneNumber;
                return Task.CompletedTask;
            }
    
            /// <summary>
            /// Gets the telephone number, if any, for the specified <paramref name="user" />.
            /// </summary>
            /// <param name="user">The user whose telephone number should be retrieved.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing the user's telephone number, if any.</returns>
            public Task<string> GetPhoneNumberAsync(User user, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                return Task.FromResult(user.PhoneNumber);
            }
    
            /// <summary>
            /// Gets a flag indicating whether the specified <paramref name="user" />'s telephone number has been confirmed.
            /// </summary>
            /// <param name="user">The user to return a flag for, indicating whether their telephone number is confirmed.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>
            /// The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, returning true if the specified <paramref name="user" /> has a confirmed
            /// telephone number otherwise false.
            /// </returns>
            public Task<bool> GetPhoneNumberConfirmedAsync(User user, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                return Task.FromResult(user.PhoneNumberConfirmed);
            }
    
            /// <summary>
            /// Sets a flag indicating if the specified <paramref name="user" />'s phone number has been confirmed.
            /// </summary>
            /// <param name="user">The user whose telephone number confirmation status should be set.</param>
            /// <param name="confirmed">A flag indicating whether the user's telephone number has been confirmed.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
            public Task SetPhoneNumberConfirmedAsync(User user, bool confirmed, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                user.PhoneNumberConfirmed = confirmed;
                return Task.CompletedTask;
            }
    
            #endregion
    
            #region Implementation of IUserTwoFactorStore<User>
    
            /// <summary>
            /// Sets a flag indicating whether the specified <paramref name="user" /> has two factor authentication enabled or not,
            /// as an asynchronous operation.
            /// </summary>
            /// <param name="user">The user whose two factor authentication enabled status should be set.</param>
            /// <param name="enabled">A flag indicating whether the specified <paramref name="user" /> has two factor authentication enabled.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
            public Task SetTwoFactorEnabledAsync(User user, bool enabled, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                //user.TwoFactorEnabled = enabled;
                return Task.CompletedTask;
            }
    
            /// <summary>
            /// Returns a flag indicating whether the specified <paramref name="user" /> has two factor authentication enabled or not,
            /// as an asynchronous operation.
            /// </summary>
            /// <param name="user">The user whose two factor authentication enabled status should be set.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>
            /// The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing a flag indicating whether the specified
            /// <paramref name="user" /> has two factor authentication enabled or not.
            /// </returns>
            public Task<bool> GetTwoFactorEnabledAsync(User user, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                return Task.FromResult(true/*user.TwoFactorEnabled*/);
            }
    
            #endregion
    
            #region Implementation of IUserAuthenticationTokenStore<User>
    
            /// <summary>Sets the token value for a particular user.</summary>
            /// <param name="user">The user.</param>
            /// <param name="loginProvider">The authentication provider for the token.</param>
            /// <param name="name">The name of the token.</param>
            /// <param name="value">The value of the token.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
            public async Task SetTokenAsync(User user, string loginProvider, string name, string value, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                UserToken token = _userTokenRepository.Query()
                    .FirstOrDefault(m => m.ID.Equals(user.ID) && m.LoginProvider == loginProvider && m.Name == name);
                if (token == null)
                {
                    token = new UserToken() { UserId = user.ID, LoginProvider = loginProvider, Name = name, Value = value };
                    await _userTokenRepository.InsertAsync(token);
                }
                else
                {
                    token.Value = value;
                    _userTokenRepository.Update(token);
                    await _unitOfWork.CommitAsync();
                }
            }
    
            /// <summary>Deletes a token for a user.</summary>
            /// <param name="user">The user.</param>
            /// <param name="loginProvider">The authentication provider for the token.</param>
            /// <param name="name">The name of the token.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
            public async Task RemoveTokenAsync(User user, string loginProvider, string name, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                _userTokenRepository.Remove(m => m.UserId.Equals(user.ID) && m.LoginProvider == loginProvider && m.Name == name);
                await _unitOfWork.CommitAsync();
            }
    
            /// <summary>Returns the token value.</summary>
            /// <param name="user">The user.</param>
            /// <param name="loginProvider">The authentication provider for the token.</param>
            /// <param name="name">The name of the token.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
            public Task<string> GetTokenAsync(User user, string loginProvider, string name, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                string value = _userTokenRepository.Query(m => m.UserId.Equals(user.ID) && m.LoginProvider == loginProvider && m.Name == name)
                    .Select(m => m.Value).FirstOrDefault();
                return Task.FromResult(value);
            }
    
            #endregion
    
            #region Implementation of IUserAuthenticatorKeyStore<User>
    
            private const string InternalLoginProvider = "[AspNetUserStore]";
            private const string AuthenticatorKeyTokenName = "AuthenticatorKey";
            private const string RecoveryCodeTokenName = "RecoveryCodes";
    
            /// <summary>
            /// Sets the authenticator key for the specified <paramref name="user" />.
            /// </summary>
            /// <param name="user">The user whose authenticator key should be set.</param>
            /// <param name="key">The authenticator key to set.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
            public Task SetAuthenticatorKeyAsync(User user, string key, CancellationToken cancellationToken)
            {
                return SetTokenAsync(user, InternalLoginProvider, AuthenticatorKeyTokenName, key, cancellationToken);
            }
    
            /// <summary>
            /// Get the authenticator key for the specified <paramref name="user" />.
            /// </summary>
            /// <param name="user">The user whose security stamp should be set.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing the security stamp for the specified <paramref name="user" />.</returns>
            public Task<string> GetAuthenticatorKeyAsync(User user, CancellationToken cancellationToken)
            {
                return GetTokenAsync(user, InternalLoginProvider, AuthenticatorKeyTokenName, cancellationToken);
            }
    
            #endregion
    
            #region Implementation of IUserTwoFactorRecoveryCodeStore<User>
    
            /// <summary>
            /// Updates the recovery codes for the user while invalidating any previous recovery codes.
            /// </summary>
            /// <param name="user">The user to store new recovery codes for.</param>
            /// <param name="recoveryCodes">The new recovery codes for the user.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The new recovery codes for the user.</returns>
            public Task ReplaceCodesAsync(User user, IEnumerable<string> recoveryCodes, CancellationToken cancellationToken)
            {
                string mergedCodes = string.Join(";", recoveryCodes);
                return SetTokenAsync(user, InternalLoginProvider, RecoveryCodeTokenName, mergedCodes, cancellationToken);
            }
    
            /// <summary>
            /// Returns whether a recovery code is valid for a user. Note: recovery codes are only valid
            /// once, and will be invalid after use.
            /// </summary>
            /// <param name="user">The user who owns the recovery code.</param>
            /// <param name="code">The recovery code to use.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>True if the recovery code was found for the user.</returns>
            public async Task<bool> RedeemCodeAsync(User user, string code, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
                Check.NotNullOrEmpty(code, nameof(code));
    
                string mergedCodes = await GetTokenAsync(user, InternalLoginProvider, RecoveryCodeTokenName, cancellationToken) ?? String.Empty;
                string[] splitCodes = mergedCodes.Split(';');
                if (splitCodes.Contains(code))
                {
                    List<string> updatedCodes = new List<string>(splitCodes.Where(s => s != code));
                    await ReplaceCodesAsync(user, updatedCodes, cancellationToken);
                    return true;
                }
                return false;
            }
    
            /// <summary>
            /// Returns how many recovery code are still valid for a user.
            /// </summary>
            /// <param name="user">The user who owns the recovery code.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The number of valid recovery codes for the user..</returns>
            public async Task<int> CountCodesAsync(User user, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                string mergedCodes = await GetTokenAsync(user, InternalLoginProvider, RecoveryCodeTokenName, cancellationToken);
                if (mergedCodes.Length > 0)
                {
                    return mergedCodes.Split(';').Length;
                }
                return 0;
            }
    
            #endregion
    
            #region Implementation of IUserRoleStore<User>
    
            /// <summary>
            /// Add a the specified <paramref name="user" /> to the named role.
            /// </summary>
            /// <param name="user">The user to add to the named role.</param>
            /// <param name="normalizedRoleName">The name of the role to add the user to.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
            public async Task AddToRoleAsync(User user, string normalizedRoleName, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
                Check.NotNullOrEmpty(normalizedRoleName, nameof(normalizedRoleName));
    
                Guid roleId = _roleRepository.Query(m => m.Name == normalizedRoleName).Select(m => m.ID).FirstOrDefault();
                if (Equals(roleId, default(Guid)))
                {
                    throw new InvalidOperationException($"名称为“{normalizedRoleName}”的角色信息不存在");
                }
                UserRole userRole = new UserRole() { RoleId = roleId, UserId = user.ID };
                await _userRoleRepository.InsertAsync(userRole);
                await _unitOfWork.CommitAsync();
            }
    
            /// <summary>
            /// Add a the specified <paramref name="user" /> from the named role.
            /// </summary>
            /// <param name="user">The user to remove the named role from.</param>
            /// <param name="normalizedRoleName">The name of the role to remove.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
            public async Task RemoveFromRoleAsync(User user, string normalizedRoleName, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
                Check.NotNullOrEmpty(normalizedRoleName, nameof(normalizedRoleName));
    
                Role role = _roleRepository.Query().FirstOrDefault(m => m.Name == normalizedRoleName);
                if (role == null)
                {
                    throw new InvalidOperationException($"名称为“{normalizedRoleName}”的角色信息不存在");
                }
                if (user.IsSystem)
                {
                    throw new InvalidOperationException($"系统用户“{user.UserName}”的系统角色“{role.Name}”不能移除");
                }
                _userRoleRepository.Remove(m => m.UserId.Equals(user.ID) && m.RoleId.Equals(role.ID));
                await _unitOfWork.CommitAsync();
            }
    
            /// <summary>
            /// Gets a list of role names the specified <paramref name="user" /> belongs to.
            /// </summary>
            /// <param name="user">The user whose role names to retrieve.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing a list of role names.</returns>
            public Task<IList<string>> GetRolesAsync(User user, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                IList<string> list = new List<string>();
                List<Guid> roleIds = _userRoleRepository.Query(m => m.UserId.Equals(user.ID)).Select(m => m.RoleId).ToList();
                if (roleIds.Count == 0)
                {
                    return Task.FromResult(list);
                }
                list = _roleRepository.Query(m => roleIds.Contains(m.ID)).Select(m => m.Name).ToList();
                return Task.FromResult(list);
            }
    
            /// <summary>
            /// Returns a flag indicating whether the specified <paramref name="user" /> is a member of the give named role.
            /// </summary>
            /// <param name="user">The user whose role membership should be checked.</param>
            /// <param name="roleName">The name of the role to be checked.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>
            /// The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing a flag indicating whether the specified <paramref name="user" /> is
            /// a member of the named role.
            /// </returns>
            public Task<bool> IsInRoleAsync(User user, string roleName, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(user, nameof(user));
    
                Guid roleId = _roleRepository.Query(m => m.Name == roleName).Select(m => m.ID).FirstOrDefault();
                if (Equals(roleId, default(Guid)))
                {
                    throw new InvalidOperationException($"名称为“{roleName}”的角色信息不存在");
                }
                bool exist = _userRoleRepository.Query(m => m.UserId.Equals(user.ID) && m.RoleId.Equals(roleId)).Any();
                return Task.FromResult(exist);
            }
    
            /// <summary>
            /// Returns a list of Users who are members of the named role.
            /// </summary>
            /// <param name="roleName">The name of the role whose membership should be returned.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>
            /// The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing a list of users who are in the named role.
            /// </returns>
            public Task<IList<User>> GetUsersInRoleAsync(string roleName, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNullOrEmpty(roleName, nameof(roleName));
    
                Guid roleId = _roleRepository.Query(m => m.Name == roleName).Select(m => m.ID).FirstOrDefault();
                if (Equals(roleId, default(Guid)))
                {
                    throw new InvalidOperationException($"名称为“{roleName}”的角色信息不存在");
                }
                List<Guid> userIds = _userRoleRepository.Query(m => m.RoleId.Equals(roleId)).Select(m => m.UserId).ToList();
                IList<User> users = _userRepository.Query(m => userIds.Contains(m.ID)).ToList();
                return Task.FromResult(users);
            }
    
            #endregion
    
            #region Other
    
            /// <summary>
            /// Converts the provided <paramref name="id"/> to a strongly typed key object.
            /// </summary>
            /// <param name="id">The id to convert.</param>
            /// <returns>An instance of <typeparamref name="Guid"/> representing the provided <paramref name="id"/>.</returns>
            public virtual Guid ConvertIdFromString(string id)
            {
                if (id == null)
                {
                    return default(Guid);
                }
                return (Guid)TypeDescriptor.GetConverter(typeof(Guid)).ConvertFromInvariantString(id);
            }
    
            /// <summary>
            /// Converts the provided <paramref name="id"/> to its string representation.
            /// </summary>
            /// <param name="id">The id to convert.</param>
            /// <returns>An <see cref="string"/> representation of the provided <paramref name="id"/>.</returns>
            public virtual string ConvertIdToString(Guid id)
            {
                if (id.Equals(default(Guid)))
                {
                    return null;
                }
                return id.ToString();
            }
    
            /// <summary>
            /// 如果已释放,则抛出异常
            /// </summary>
            protected void ThrowIfDisposed()
            {
                if (_disposed)
                {
                    throw new ObjectDisposedException(GetType().Name);
                }
            }
    
            #endregion
        }
    View Code

        这里我是通过uow获取的仓储对象,如果 看不明白,看这里 关于UOW模式 ,然后重定义其中的 增删删改查操作,,嗯,是的就是这么简单的。

        同样的 RoleStore我们也是需要自定义的,所以也看下一下源码的实现:

        

        看到了吧,这里就实现了两个接口,所以比UserStore实现要简单很多了,自定义实现如下(其中涉及的Role,RoleClaim依旧需要自定义,建议赋值 core identity中的):

    public class RoleStore
              : IQueryableRoleStore<Role>,
                IRoleClaimStore<Role>
        {
    
            private bool _disposed;
    
            #region ctor
            public RoleStore(IUnitOfWork unitOfWork)
            {
                _unitOfWork = unitOfWork;
                _roleRepository = _unitOfWork.Repository<Role, Guid>();
                _roleClaimRepository = _unitOfWork.Repository<RoleClaim, Guid>();
    
            }
            #endregion
    
            #region fields
            private readonly IUnitOfWork _unitOfWork;
            private readonly IRepository<Role, Guid> _roleRepository;
            private readonly IRepository<RoleClaim, Guid> _roleClaimRepository;
            #endregion
    
            #region Implementation of IDisposable
    
            /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
            public void Dispose()
            {
                _disposed = true;
            }
    
            #endregion
    
            #region Implementation of IQueryableRoleStore<Role>
    
            /// <summary>
            /// Returns an <see cref="T:System.Linq.IQueryable`1" /> collection of roles.
            /// </summary>
            /// <value>An <see cref="T:System.Linq.IQueryable`1" /> collection of roles.</value>
            public IQueryable<Role> Roles => _roleRepository.Query();
    
            #endregion
    
            #region Implementation of IRoleStore<Role>
    
            /// <summary>
            /// Creates a new role in a store as an asynchronous operation.
            /// </summary>
            /// <param name="role">The role to create in the store.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>A <see cref="T:System.Threading.Tasks.Task`1" /> that represents the <see cref="T:Microsoft.AspNetCore.Identity.IdentityResult" /> of the asynchronous query.</returns>
            public async Task<IdentityResult> CreateAsync(Role role, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(role, nameof(role));
    
                if (role.IsDefault)
                {
                    string defaultRole = _roleRepository.Query(m => m.IsDefault).Select(m => m.Name).FirstOrDefault();
                    if (defaultRole != null)
                    {
                        return new IdentityResult().Failed($"系统中已存在默认角色“{defaultRole}”,不能重复添加");
                    }
                }
                await _roleRepository.InsertAsync(role);
                await _unitOfWork.CommitAsync();
                return IdentityResult.Success;
            }
    
            /// <summary>
            /// Updates a role in a store as an asynchronous operation.
            /// </summary>
            /// <param name="role">The role to update in the store.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>A <see cref="T:System.Threading.Tasks.Task`1" /> that represents the <see cref="T:Microsoft.AspNetCore.Identity.IdentityResult" /> of the asynchronous query.</returns>
            public async Task<IdentityResult> UpdateAsync(Role role, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(role, nameof(role));
    
                if (role.IsDefault)
                {
                    var defaultRole = _roleRepository.Query(m => m.IsDefault).Select(m => new { m.ID, m.Name }).FirstOrDefault();
                    if (defaultRole != null && !defaultRole.ID.Equals(role.ID))
                    {
                        return new IdentityResult().Failed($"系统中已存在默认角色“{defaultRole.Name}”,不能重复添加");
                    }
                }
                _roleRepository.Update(role);
                await _unitOfWork.CommitAsync();
                return IdentityResult.Success;
            }
    
            /// <summary>
            /// Deletes a role from the store as an asynchronous operation.
            /// </summary>
            /// <param name="role">The role to delete from the store.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>A <see cref="T:System.Threading.Tasks.Task`1" /> that represents the <see cref="T:Microsoft.AspNetCore.Identity.IdentityResult" /> of the asynchronous query.</returns>
            public async Task<IdentityResult> DeleteAsync(Role role, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(role, nameof(role));
    
                _roleRepository.Remove(role);
                await _unitOfWork.CommitAsync();
                return IdentityResult.Success;
            }
    
            /// <summary>
            /// Gets the ID for a role from the store as an asynchronous operation.
            /// </summary>
            /// <param name="role">The role whose ID should be returned.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>A <see cref="T:System.Threading.Tasks.Task`1" /> that contains the ID of the role.</returns>
            public Task<string> GetRoleIdAsync(Role role, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(role, nameof(role));
    
                return Task.FromResult(ConvertIdToString(role.ID));
            }
    
            /// <summary>
            /// Gets the name of a role from the store as an asynchronous operation.
            /// </summary>
            /// <param name="role">The role whose name should be returned.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>A <see cref="T:System.Threading.Tasks.Task`1" /> that contains the name of the role.</returns>
            public Task<string> GetRoleNameAsync(Role role, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(role, nameof(role));
    
                return Task.FromResult(role.Name);
            }
    
            /// <summary>
            /// Sets the name of a role in the store as an asynchronous operation.
            /// </summary>
            /// <param name="role">The role whose name should be set.</param>
            /// <param name="roleName">The name of the role.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
            public Task SetRoleNameAsync(Role role, string roleName, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(role, nameof(role));
    
                role.Name = roleName;
                return Task.CompletedTask;
            }
    
            /// <summary>
            /// Get a role's normalized name as an asynchronous operation.
            /// </summary>
            /// <param name="role">The role whose normalized name should be retrieved.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>A <see cref="T:System.Threading.Tasks.Task`1" /> that contains the name of the role.</returns>
            public Task<string> GetNormalizedRoleNameAsync(Role role, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(role, nameof(role));
    
                return Task.FromResult(role.Name);
            }
    
            /// <summary>
            /// Set a role's normalized name as an asynchronous operation.
            /// </summary>
            /// <param name="role">The role whose normalized name should be set.</param>
            /// <param name="normalizedName">The normalized name to set</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
            public Task SetNormalizedRoleNameAsync(Role role, string normalizedName, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(role, nameof(role));
    
                role.Name = normalizedName;
                return Task.CompletedTask;
            }
    
            /// <summary>
            /// Finds the role who has the specified ID as an asynchronous operation.
            /// </summary>
            /// <param name="roleId">The role ID to look for.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>A <see cref="T:System.Threading.Tasks.Task`1" /> that result of the look up.</returns>
            public Task<Role> FindByIdAsync(string roleId, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
    
                Guid id = ConvertIdFromString(roleId);
                return Task.FromResult(_roleRepository.Query().FirstOrDefault(m => m.ID.Equals(id)));
            }
    
            /// <summary>
            /// Finds the role who has the specified normalized name as an asynchronous operation.
            /// </summary>
            /// <param name="normalizedRoleName">The normalized role name to look for.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>A <see cref="T:System.Threading.Tasks.Task`1" /> that result of the look up.</returns>
            public Task<Role> FindByNameAsync(string normalizedRoleName, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                return Task.FromResult(_roleRepository.Query().FirstOrDefault(m => m.Name == normalizedRoleName));
            }
    
            #endregion
    
            #region Implementation of IRoleClaimStore<Role>
    
            /// <summary>
            ///  Gets a list of <see cref="T:System.Security.Claims.Claim" />s to be belonging to the specified <paramref name="role" /> as an asynchronous operation.
            /// </summary>
            /// <param name="role">The role whose claims to retrieve.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>
            /// A <see cref="T:System.Threading.Tasks.Task`1" /> that represents the result of the asynchronous query, a list of <see cref="T:System.Security.Claims.Claim" />s.
            /// </returns>
            public Task<IList<Claim>> GetClaimsAsync(Role role, CancellationToken cancellationToken = new CancellationToken())
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(role, nameof(role));
    
                IList<Claim> list = _roleClaimRepository.Query(m => m.RoleId.Equals(role.ID)).Select(n => new Claim(n.ClaimType, n.ClaimValue)).ToList();
                return Task.FromResult(list);
            }
    
            /// <summary>
            /// Add a new claim to a role as an asynchronous operation.
            /// </summary>
            /// <param name="role">The role to add a claim to.</param>
            /// <param name="claim">The <see cref="T:System.Security.Claims.Claim" /> to add.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The task object representing the asynchronous operation.</returns>
            public async Task AddClaimAsync(Role role, Claim claim, CancellationToken cancellationToken = new CancellationToken())
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(role, nameof(role));
                Check.NotNull(claim, nameof(claim));
    
                RoleClaim roleClaim = new RoleClaim() { RoleId = role.ID, ClaimType = claim.Type, ClaimValue = claim.Value };
                await _roleClaimRepository.InsertAsync(roleClaim);
                await _unitOfWork.CommitAsync();
            }
    
            /// <summary>
            /// Remove a claim from a role as an asynchronous operation.
            /// </summary>
            /// <param name="role">The role to remove the claim from.</param>
            /// <param name="claim">The <see cref="T:System.Security.Claims.Claim" /> to remove.</param>
            /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The task object representing the asynchronous operation.</returns>
            public async Task RemoveClaimAsync(Role role, Claim claim, CancellationToken cancellationToken = new CancellationToken())
            {
                cancellationToken.ThrowIfCancellationRequested();
                ThrowIfDisposed();
                Check.NotNull(role, nameof(role));
                Check.NotNull(claim, nameof(claim));
    
                _roleClaimRepository.Remove(m => m.RoleId.Equals(role.ID) && m.ClaimValue == claim.Type && m.ClaimValue == claim.Value);
                await _unitOfWork.CommitAsync();
            }
    
            #endregion
    
            /// <summary>
            /// Converts the provided <paramref name="id"/> to a strongly typed key object.
            /// </summary>
            /// <param name="id">The id to convert.</param>
            /// <returns>An instance of <typeparamref name="TRoleKey"/> representing the provided <paramref name="id"/>.</returns>
            public virtual Guid ConvertIdFromString(string id)
            {
                if (id == null)
                {
                    return default(Guid);
                }
                return (Guid)TypeDescriptor.GetConverter(typeof(Guid)).ConvertFromInvariantString(id);
            }
    
            /// <summary>
            /// Converts the provided <paramref name="id"/> to its string representation.
            /// </summary>
            /// <param name="id">The id to convert.</param>
            /// <returns>An <see cref="string"/> representation of the provided <paramref name="id"/>.</returns>
            public virtual string ConvertIdToString(Guid id)
            {
                if (id.Equals(default(Guid)))
                {
                    return null;
                }
                return id.ToString();
            }
    
            /// <summary>
            /// 如果已释放,则抛出异常
            /// </summary>
            protected void ThrowIfDisposed()
            {
                if (_disposed)
                {
                    throw new ObjectDisposedException(GetType().Name);
                }
            }
        }
    View Code

        这样我们就完成了 50% 的工作了,那么重点来了,怎么用起来?

    services.AddScoped<IUserStore<User>, UserStore>();
    services.AddScoped<IRoleStore<Role>, RoleStore>();
    //services.AddScoped<SignInManager<User>>();
    //services.AddScoped<UserManager<User>>();
    //services.AddScoped<RoleManager<Role>>();

    如此注入到 IOC 容器就好啦,嗯,我本很开心的觉得这样就完事了,但是错误不断,也就是上面红色(注释掉)的三行,解决一个有提示另一个没有注入而无法正常使用,其实,少了一步骤,你可能会说是 Configures中 app.UseAuthentication() 是吧,其实不是的,呵呵:

    configureSercvices中继续加入

    IdentityBuilder builder = services.AddIdentity<User, Role>(

    (options) =>
    {
         options.SignIn.RequireConfirmedEmail = true;
      options.Password.RequireNonAlphanumeric = false;
      options.Password.RequireUppercase = false;
      options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(20);
    }

    );                       
    builder.AddDefaultTokenProviders(); 

    //关于 IdentityBuilder ,这里的两个类型User,Role,分别是我们自定义的 User实体和Role实体,此时我们再 app.UseAuthentication()  就可以了。

     

    此时我们只需要在项目中如此注入即可:

     

          public IdentityService(
                IUnitOfWork unitOfWork,
                SignInManager<User> signInManager,
                UserManager<User> userManager,
                ILogger<IdentityService> logger)
            {
                _unitOfWork = unitOfWork;
                _signInManager = signInManager;
                _userManager = userManager;
                _logger = logger;
            }
    
            #region fields
            private readonly IUnitOfWork _unitOfWork;
            private readonly SignInManager<User> _signInManager;
            private readonly UserManager<User> _userManager;
            private readonly ILogger<IdentityService> _logger;
    View Code

     

     

    这里的泛型参数 分别是我们自定义的 User和Role的模型。

    这时候我们使用已有的上下文进行迁移,将得到如下结果:

    看我们自定义的结果:

        看我们的API的请求操作:

        (注册 并启用邮箱验证):

        

        这里使用自定义的个注册API模拟注册一个用户,然后模拟登录:

        

        

        嗯,结果很理想,需要验证。

        其他,可自定扩展。

    下班,,,,

        

        

     

     

      

        

        

  • 相关阅读:
    前端脚手架的那些事儿
    CSS重置默认样式reset.css代码模板
    Web 3.0 前瞻:基于区块链的下一代浏览器
    关键词定位是网站推广的基础
    6年架构师针对web前端小白,作出的职业规划建议
    和程序员约会的优点和缺点
    如何在软件开发中避免出现漏洞
    Linux下修改时区
    前端需要掌握的Nginx知识
    Nginx入门指南
  • 原文地址:https://www.cnblogs.com/Tmc-Blog/p/9958493.html
Copyright © 2020-2023  润新知