Microsoft.AspNet.Identity 自定義使用現有的表—登錄實現


Microsoft.AspNet.Identity是微軟新引入的一種membership框架,也是微軟Owin標准的一個實現。Microsoft.AspNet.Identity.EntityFramework則是Microsoft.AspNet.Identity的數據提供實現。但是在使用此框架的時候存在一些問題,如果是全新的項目還可以使用它默認提供的表名,字段名等。但是如果是在一些老的數據庫上應用這個框架就比較麻煩了。所以我們實現一個自己的Microsoft.AspNet.Identity.EntityFramework

首先我們只說登錄,登錄的入口代碼是

var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);

對應Owin框架中的代碼為

public virtual async Task<SignInStatus> PasswordSignInAsync(string userName, string password, bool isPersistent, bool shouldLockout)
{
    SignInStatus result;
    if (this.UserManager == null)
    {
        result = SignInStatus.Failure;
    }
    else
    {
        TUser tUser = await this.UserManager.FindByNameAsync(userName).WithCurrentCulture<TUser>();
        if (tUser == null)
        {
            result = SignInStatus.Failure;
        }
        else if (await this.UserManager.IsLockedOutAsync(tUser.Id).WithCurrentCulture<bool>())
        {
            result = SignInStatus.LockedOut;
        }
        else if (await this.UserManager.CheckPasswordAsync(tUser, password).WithCurrentCulture<bool>())
        {
            await this.UserManager.ResetAccessFailedCountAsync(tUser.Id).WithCurrentCulture<IdentityResult>();
            result = await this.SignInOrTwoFactor(tUser, isPersistent).WithCurrentCulture<SignInStatus>();
        }
        else
        {
            if (shouldLockout)
            {
                await this.UserManager.AccessFailedAsync(tUser.Id).WithCurrentCulture<IdentityResult>();
                if (await this.UserManager.IsLockedOutAsync(tUser.Id).WithCurrentCulture<bool>())
                {
                    result = SignInStatus.LockedOut;
                    return result;
                }
            }
            result = SignInStatus.Failure;
        }
    }
    return result;
}

由此代碼可大概知曉登錄的流程是,當然還有登錄失敗的流程就先不實現了。需要實現也非常簡單,根據Owin的源代碼實現對應的接口即可.

1.FindByNameAsync 先根據登錄名找到user對象,使用UserManager中的UserStroe所實現IUserStore的接口方法

2.IsLockedOutAsync 檢查登錄是否鎖定,使用UserManager中的UserStroe所實現的IUserLockoutStore接口方法

3.CheckPasswordAsync 檢查密碼,使用UserManager中的UserStroe所實現的IUserPasswordStore接口方法

4.ResetAccessFailedCountAsync 登錄成功,重置登錄失敗計數,使用UserManager中的UserStroe所實現的IUserLockoutStore接口方法

5.SignInOrTwoFactor 雙重身份驗證,使用UserManager中的UserStroe所實現的IUserTwoFactorStore接口方法

 

SignInManager是入口,需要用到UserManager,UserManager需要用到關鍵的UserStore,具體的框架的介紹可以參考園子里其他的文章,都講的很好,並且很好的講明了為何需要這么設計。

實現

已有資源,假如我們已經有了數據庫,有了user表,有了id字段guid類型,有了loginid代表登錄的用戶名,也就是源代碼中的username

第一步 先實現我們自己的SignInManager,繼承自Microsoft.AspNet.Identity.Owin.SignInManager<TUser, TKey>

public class WXSignInManager : SignInManager<WXUser, Guid>
    {
        public WXSignInManager(UserManager<WXUser, Guid> userManager, IAuthenticationManager authenticationManager) : base(userManager, authenticationManager)
        {
        }
        public static WXSignInManager Create(IdentityFactoryOptions<WXSignInManager> options, IOwinContext context)
        {
            return new WXSignInManager(context.GetUserManager<WXUserManager>(), context.Authentication);
        }

我們的SignInManager代碼中有一行context.GetUserManager<WXUserManager>(),所以繼續實現我們的UserManager。

第二步 實現我們的自己的UserManager,繼承自Microsoft.AspNet.Identity.UserManager<TUser, TKey>

public class WXUserManager : UserManager<WXUser, Guid>
    {
        public WXUserManager(IUserStore<WXUser, Guid> store) : base(store)
        {
        }

        public static WXUserManager Create(IdentityFactoryOptions<WXUserManager> options, IOwinContext context)
        {
            return new WXUserManager(new WXUserStore(context.Get<WXDBContexnt>()))
            {
                PasswordHasher = new MyPasswordHasher()
            };
        }
    }

由之前Owin源代碼可以知道重點代碼都在UserStore中,接下來

第三步,實現我們自己的UserStore,分別實現接口

Microsoft.AspNet.Identity.IUserStore<TUser, in TKey>,//數據庫訪問相關接口

Microsoft.AspNet.Identity.IUserLockoutStore<TUser, in TKey>,//用戶鎖定,登錄失敗計數相關接口

Microsoft.AspNet.Identity.IUserPasswordStore<TUser, in TKey>,//用戶密碼相關接口

Microsoft.AspNet.Identity,IUserTwoFactorStore<TUser, in TKey>//雙重身份驗證相關接口

public class WXUserStore : IUserStore<WXUser, Guid>, IUserLockoutStore<WXUser, Guid>, IUserPasswordStore<WXUser, Guid>, IUserTwoFactorStore<WXUser, Guid>
        {
            public WXUserStore(WXDBContexnt dbContext)
            {
                this.dbContext = dbContext;
            }
            WXDBContexnt dbContext;

            public async Task<WXUser> FindByIdAsync(Guid userId)
            {
                var user = await dbContext.WXUser.FindAsync(userId);
                return user;
            }

            public async Task<WXUser> FindByNameAsync(string userName)
            {
                return dbContext.WXUser.Where(p => p.LoginId == userName).FirstOrDefaultAsync();
            }
            public Task ResetAccessFailedCountAsync(WXUser user)
            {
                return Task.FromResult(false);
            }
            public Task<bool> GetLockoutEnabledAsync(WXUser user)
            {
                return Task.FromResult(false);
            }
            public Task<string> GetPasswordHashAsync(WXUser user)
            {
                return Task.FromResult(user.LoginPWD);
            }
            public Task<bool> GetTwoFactorEnabledAsync(WXUser user)
            {
                return Task.FromResult(false);
            }
        }

這里僅僅是完成一個超級簡單的登錄功能,所以無關的實現都刪除了,需要注意的是p => p.LoginId == userName,原有數據庫中登錄名的字段是loginId。接口的意思可以查看文檔即可,相信從方法的名字就能猜到具體的意思,人家設計的接口就是好<!_!>。

我這里使用的是EF作為數據提供源,當然你也可以使用自己的,只需要替換FindByIdAsync,FindByNameAsync方法中對應的實現,哪怕是在這些方面里面使用ado.net直接查詢數據都是完全沒有問題的。wxuser我繼承了系統已經存在的user對象,然后強類型實現了IUser接口,因為我原系統對象已存在了username屬性。而此處的wxuser.username屬性是作為用戶登錄的賬號意思存在的。所以我強類型實現。

public class WXUser : 系統已存在的user entity對象, IUser<Guid>
    {
        Guid IUser<Guid>.Id
        {
            get
            {
                return this.Id;
            }
        }

        string IUser<Guid>.UserName
        {
            get
            {
                return this.LoginId;
            }

            set
            {
                this.LoginId = value;
            }
        }
    }

public class WXDBContexnt : DbContext
    {
        public WXDBContexnt()
        {

        }
        public static WXDBContexnt Create() { return new WXDBContexnt(); }
        public DbSet<WXUser> WXUser { get; set; }
    }

大致代碼就是如此了,當然我們自己實現的UserStore對象還有很多方法沒有實現,but我只是需要一個登錄不是么,可以慢慢改造的嘛<!_!>

寫到最后想到通過重寫的方式估計也能實現,這是新建項目生成的默認代碼,為什么不可以增加[Table("Users")],[Column("LoginId")],override達到效果呢。

[Table("Users")]
    public class ApplicationUser : IdentityUser
    {
        public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
        {
            // 請注意,authenticationType 必須與 CookieAuthenticationOptions.AuthenticationType 中定義的相應項匹配
            var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
            // 在此處添加自定義用戶聲明
            return userIdentity;
        }
        [Column("LoginId")]
        public override string UserName
        {
            get
            {
                return base.UserName;
            }

            set
            {
                base.UserName = value;
            }
        }
    }

PS:看過源代碼了,標記方式針對表名不行,源代碼中寫死了table name。字段名可以通過標記方式重命名 --by 2016-10-24


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM