2015年8月27日 星期四

Token Authentication with ASP.NET OWIN Identity (2) – Customize the user’s information

 ASP.NET   IDENTITY   Token based authentication

背景

本篇重點在於延伸並客製OWIN IdentityCode First資料庫,以符合企業的內部需求。


相關文章


目標

OWIN Identity的資料庫中,主要是以 AspNetUsers 這個表格儲存使用者的資訊,
我們目標是在 AspNetUsers 加上新的Foreign Key Column,參照到其他Table
以下是結果:



環境

l   Visual Studio 2015
l   WEB API 2.2
l   Entity Framework 6
l   套件版本:
Microsoft.Owin.Host.SystemWeb 3.0.1
Microsoft ASP.NET Identity Owin 2.2.1
Microsoft ASP.NET Identity EntityFramework 2.2.1
Microsoft ASP.NET Web API 2.2 OWIN 5.2.3
Microsoft.Owin.Security.OAuth 3.0.1


實作


Code First Database Model

首先當然是建立Code FirstModel~~

l   AspNetDepartment

/// <summary>
/// 部門()
/// </summary>
public class AspNetDepartment
{
        /// Department Id
        [Key]
        [Required]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public Int32 AspNetDepartmentId { get; set; }

        /// Department Name
        [Required]
        [Column(TypeName = "NVARCHAR")]
        [MaxLength(100, ErrorMessage = "The legth of department's name must be 1~100")]
        public String Name { get; set; }

        /// AspNetDepartment 1..* EnhancedIdentityUser
        public virtual ICollection<EnhancedIdentityUser> EnhancedIdentityUsers { get; set; }
}


l   AspNetPositionLevel

/// <summary>
/// 職位
/// </summary>
public class AspNetPositionLevel
{
        /// Position Level Id
        [Key]
        [Required]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public Int32 AspNetPositionLevelId { get; set; }

        /// Position Level Name
        [Required]
        [Column(TypeName = "NVARCHAR")]
        [MaxLength(100)]
        public String Name { get; set; }

        /// AspNetPositionLevel 1..* EnhancedIdentityUsers
        public virtual ICollection<EnhancedIdentityUser> EnhancedIdentityUsers { get; set; }
}


l   EnhancedIdentityUser

這是擴展IdentityUser的新Model,注意一定要加上constructor並呼叫基礎類別的建構。

/// IdentityUser的擴充
public class EnhancedIdentityUser : IdentityUser
{
       
/// Must inherit the base constructor
        public EnhancedIdentityUser():base()
        {
        }

        [Column(Order = 100)]
        public Int32 AspNetDepartmentId { get; set; }

        [Column(Order = 101)]
        public Int32 AspNetPositionLevelId { get; set; }

        //Foreign Key to AspNetDepartment
        [ForeignKey("AspNetDepartmentId")]
        public virtual AspNetDepartment AspNetDepartment { get; set; }

        //Foreign Key to AspNetPositionLevel
        [ForeignKey("AspNetPositionLevelId")]
        public virtual AspNetPositionLevel AspNetPositionLevel { get; set; }
}


更新DbContext

更新DbContext是比較麻煩的地方,通常如果在Run-time DB建不起來有99.999… % 是錯在這邊。
需要特別注意的地方:
l   在覆寫(Override) OnModelCreating 這個方法時,必須呼叫基礎類別的OnModelCreating
l   須建立上面資料表的一對多關聯。

完整 DbContext (這邊命名為AuthContext)

public class AuthContext : IdentityDbContext<EnhancedIdentityUser>
{
        public AuthContext() : base("AuthContext")
        {
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            try
            {
                //MUST call the OnModelCreating method from base
                base.OnModelCreating(modelBuilder);

                #region Create relationship
                //設定 PositionLevel 一對多 User
                modelBuilder.Entity<AspNetPositionLevel>().HasMany(m => m.EnhancedIdentityUsers).WithRequired(m => m.AspNetPositionLevel);

                //設定 Department 一對多 User
                modelBuilder.Entity<AspNetDepartment>().HasMany(m => m.EnhancedIdentityUsers).WithRequired(m => m.AspNetDepartment);
                #endregion

                #region Create Index
                //…
                #endregion

            }
            catch (Exception ex) { }
        }


        //宣告資料模型: AspNetDepartments (AspNetDepartment類別組成)
        public DbSet<AspNetDepartment> AspNetDepartments { get; set; }

        //宣告資料模型: AspNetPositionLevels (AspNetPositionLevel類別組成)
        public DbSet<AspNetPositionLevel> AspNetPositionLevels { get; set; }
}



DB initialize (非必要)

可直接用Migration的方式更新現有資料庫!

或建立DB initialize的類別,並在Application Start時叫用。

public class AuthContextInitializer : System.Data.Entity.CreateDatabaseIfNotExists<AuthContext>
{
        protected override void Seed(AuthContext context)
        {
            try
            {
                context.AspNetDepartments.Add(new AspNetDepartment() { Name="應用一課" });
                context.AspNetDepartments.Add(new AspNetDepartment() { Name = "應用二課" });
                context.AspNetDepartments.Add(new AspNetDepartment() { Name = "應用三課" });

                context.AspNetPositionLevels.Add(new AspNetPositionLevel() { Name = "助理工程師" });
                context.AspNetPositionLevels.Add(new AspNetPositionLevel() { Name = "工程師" });
                context.AspNetPositionLevels.Add(new AspNetPositionLevel() { Name = "資深工程師" });
                context.AspNetPositionLevels.Add(new AspNetPositionLevel() { Name = "主任工程師" });

                context.SaveChanges();
            }
            catch (Exception ex) { }
        }

    }



Global.asax
protected void Application_Start()
{
    // …
    //註冊資料庫Initial
    Database.SetInitializer(new AuthContextInitializer());
}



更新 DTO : UserModel

還記得Identity WebApi DTO物件: UserModel嗎?
別忘記加上新欄位囉!

public class UserModel
{
        [Required]
        [Display(Name = "User Name")]
        public String UserName { get; set; }

        [Required]
        [StringLength(100, MinimumLength = 6, ErrorMessage = "The {0}'s length must be between {2}~{1} characters long")]
        [DataType(DataType.Password)]
        [Display(Name = "Password")]
        public String Password { get; set; }


        [DataType(DataType.Password)]
        [Display(Name = "Confirm password")]
        [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
        public String ConfirmPassword { get; set; }

        [Required]
        [Display(Name = "Department Id")]
        public Int32 DepartmentId { get; set; }

        [Required]
        [Display(Name = "Position Level Id")]
        public Int32 PositionLevelId { get; set; }
}


更新 Authentication Repository

別忘記以新的Model 更新CRUDRepository!
IdentityUser 全部取代為 EnhancedIdentityUser!

另外,也記得在 RegisterUser這個方法 (亦即建立使用者時)
從前端回拋的資料指定回DB Model



測試


更新後的資料庫:


1.  Register a user





2.  Unregister


3.  Get the token

 

4.  Authorization








Reference






沒有留言:

張貼留言