Identity application of ASP. Net core (Part 2)

Time:2021-7-29

1、 Foreword

stayPart IIt briefly introduces the identity of ASP. Net core, a framework responsible for user identity authentication. When we select this framework as needed to manage and store user account data in our application, it will be added to our own project. At this time, we will use the built-in data model by default, but considering the requirements, we can update the data model in a customized way to meet our requirements.

2、 First acquaintance

stayPart IWe have completed the data migration and the construction of the projectApplicationDbContextIs responsible forIdentityIdentify related users, inherited fromIdentityDbContextContext. Of course, we found that user data can be customized and configured according to the extensibility of this context.

For example, customize the extended user data class to inherit fromIdentityUserClass, change user data model properties, change primary key, change table name, column name, etc. to meet our business requirements.

3、 Practice

Follow the previous articleWebIdentityDemoV3.1Project, add custom user data to identity dB. The user data class of custom extension should inherit identityuser class, and the file name is areas / identity / data / {project name} user.cs.

3.1 table description

This is the user data we want to prepare for customization. This example is directly inherited fromIdentity of ASP. Net coreYes.

Just from the database table name, we know the meaning, that is, user role management.

Data Description:

_EFMigrationsHistoryIs the migration history table of EF.

AspNetUserClaimsAspNetRoleClaimsIt is a declaration table of users and roles in which claim plays a very important role. Even roles have been converted into claims. You can understand what I said earlierAuthentication authorizationpattern.

AspNetUsersAspNetRolesandAspNetUserRolesStore user and role information.

AspNetUserTokensFor external authenticationTokenStorage.

AspNetUserLoginsKeep the login information of third parties such as Google, Facebook, twitter and QQ.

3.2 user defined model

Context is used to configure the model in two ways:

  • Provide entity and key types for generic type parameters.
  • rewriteOnModelCreatingTo modify the mappings of these types.

When rewritingOnModelCreatingbase.OnModelCreatingYou should first call and then call the rewrite configuration. EF core usually has the last wins policy for configuration. For example, ifToTableIf you call a method of an entity type with one table name and then call the method again with another table name, the table name in the second call is used.

Custom data

Here, the user class is taken as an example:

3.3.1 user defined user class

definitionApplicationUserClass inherits fromIdentityUserUser data class, custom class naming convention {application} user.

public class ApplicationUser:IdentityUser
    { 
        /// 
        ///User number
        /// 
        public string UserNo { get; set; }
        /// 
        ///Real name
        /// 
        public string UserTrueName { get; set; }

    }

3.3.2 modify service configuration

The original file in startupConfigureServicesIn service configurationIdentityUserChange toApplicationUser

services.AddDefaultIdentity(options => options.SignIn.RequireConfirmedAccount = true)
                .AddEntityFrameworkStores();

Change to:

services.AddDefaultIdentity(options => options.SignIn.RequireConfirmedAccount = true)
                .AddEntityFrameworkStores();

3.3.3 modify context

Will the originalApplicationDbContextContext inherited fromIdentityDbContext, change toIdentityDbContext

customary

public class ApplicationDbContext : IdentityDbContext
{
    public ApplicationDbContext(DbContextOptions options)
    : base(options)
    {
    }
}

Change to:

takeApplicationUserType is used as a generic parameter for the context

public class ApplicationDbContext : IdentityDbContext
{
    public ApplicationDbContext(DbContextOptions options)
    : base(options)
    {
    }
}

3.3.4 data migration

#1. If there is a database, delete the database first
#Drop database (PMC) or dotnet EF database drop (. Net core CLI)
#2. After confirming to delete the database, delete the migration
#Remove migration (PMC) or dotnet EF migrations remove (. Net core CLI)
#Then update the data model, add migration, and convert it into the corresponding database
PM> Add-Migration CreateIdentitySchema2 -c ApplicationDbContext -o Data/Migrations
PM> Update-Database CreateIdentitySchema2

The effects are as follows:

You can find that there are corresponding custom fields.

3.3.5 update and replace

If you have built the project before, you need toIdentityUserClass changed to customApplicationUserClass.

to updatePages/Shared/_LoginPartial, and replaceIdentityUserbyApplicationUser

@using Microsoft.AspNetCore.Identity
@using WebApp1.Areas.Identity.Data
@inject SignInManager SignInManager
@inject UserManager UserManager

Other specific replacement and modification methods will not be explained and demonstrated.

3.4 changing primary key type

Changing the data type of PK column after creating the database has problems on many database systems. Changing PK usually involves deleting and recreating tables.When you create a PK database, you should specify the type in the initial migration。 Here are the steps to change the primary key type:

3.4.1 change table primary key type

Here withApplicationUserClass as an example, modify the relevant code

//Set the primary key of the user table to int
    public class ApplicationUser : IdentityUser
    {
        /// 
        ///User number
        /// 
        public string UserNo { get; set; }
        /// 
        ///Real name
        /// 
        public string UserTrueName { get; set; }
    }

3.4.2 modify context

public class ApplicationDbContext : IdentityDbContext, Guid>

3.4.3 modify service configuration

services.AddDefaultIdentity(options => options.SignIn.RequireConfirmedAccount = true)
                .AddEntityFrameworkStores();

3.4.4 data migration

#1. If there is a database, delete the database first
#Drop database (PMC) or dotnet EF database drop (. Net core CLI)
#2. After confirming to delete the database, delete the migration
#Remove migration (PMC) or dotnet EF migrations remove (. Net core CLI)
#Then update the data model, add migration, and convert it into the corresponding database
PM> Add-Migration CreateIdentitySchema2 -c ApplicationDbContext -o Data/Migrations
PM> Update-Database CreateIdentitySchema2

At this time, the primary key type of the table has been modified, including the foreign key type of the relationship table,

The effects are as follows:  

3.5 changing table and column names

3.5.1 change table name

Call before changing the table name againbase.OnModelCreating。 Then, add a configuration to override the default table name and define the primary key. The example here changes the default table name totblTable name named at the beginning

protected override void OnModelCreating(ModelBuilder builder)
        {
            var maxKeyLength = 256;
            base.OnModelCreating(builder);
            //Custom modification table name, starting with TBL name
            builder.Entity(b =>
            {
                b.ToTable("TblUsers");
            });

            builder.Entity>(b =>
            {
                //Define primary key
                b.HasKey(u => u.Id);
                b.ToTable("TblUserClaims");
            });

            builder.Entity>(b =>
            {
                b.HasKey(u => new { u.LoginProvider, u.ProviderKey });
                b.ToTable("TblUserLogins");
            });

            builder.Entity>(b =>
            {
                b.HasKey(u => new { u.UserId, u.LoginProvider, u.Name });
                b.ToTable("TblUserTokens");
            });

            builder.Entity>(b =>
            {
                b.HasKey(u => u.Id);
                b.ToTable("TblRoles");
            });

            builder.Entity>(b =>
            {
                b.HasKey(u => u.Id);
                b.ToTable("TblRoleClaims");
            });

            builder.Entity>(b =>
            {
                b.HasKey(u => new { u.UserId, u.RoleId });
                b.ToTable("TblUserRoles");
            });
        }
    }

If an application type such asApplicationUser, please configure the type instead of the default type.

3.5.2 changing column names

The following example changes some column names as needed

protected override void OnModelCreating(ModelBuilder builder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity(b =>
    {
         b.Property(e => e.PasswordHash).HasColumnName("Password");
    });
}

3.5.3 change length

Some types of database columns can be configuredaspect(for example,string(allowable) maximum length.

protected override void OnModelCreating(ModelBuilder builder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity(b =>
    {
        b.Property(u => u.UserName).HasMaxLength(128);
        b.Property(u => u.NormalizedUserName).HasMaxLength(128);
        b.Property(u => u.Email).HasMaxLength(128);
        b.Property(u => u.NormalizedEmail).HasMaxLength(128);
    }); 
}

3.5.4 data migration

#Update the data model, add migration, and convert it into the corresponding database
PM> Add-Migration CreateIdentitySchema2 -c ApplicationDbContext -o Data/Migrations
PM> Update-Database CreateIdentitySchema2

At this time, the primary key type of the table has been modified, including the foreign key type of the relationship table,

The effects are as follows:  

3.6 initializing the database

When creating a project, we can prepare the initialization data in advance and migrate the data as seed processing to the created database for initialization.

3.6.1 creating files

Create the seeddata.cs file to initialize the basic data:

public class SeedData
    {
        public static void EnsureSeedData(IServiceProvider serviceProvider)
        {
            Console.WriteLine("Seeding database...");
            using (var scope = serviceProvider.GetRequiredService().CreateScope())
            {
                var context = scope.ServiceProvider.GetService();
                context.Database.Migrate();

                var userMgr = scope.ServiceProvider.GetRequiredService>();
                var alice = userMgr.FindByNameAsync("alice").Result;
                if (alice == null)
                {
                    alice = new ApplicationUser
                    {
                        UserName = "alice"
                    };
                    var result = userMgr.CreateAsync(alice, "Pass123$").Result;
                    if (!result.Succeeded)
                    {
                        throw new Exception(result.Errors.First().Description);
                    }

                    result = userMgr.AddClaimsAsync(alice, new Claim[]{
                        new Claim(JwtClaimTypes.Name, "Alice Smith"),
                        new Claim(JwtClaimTypes.GivenName, "Alice"),
                        new Claim(JwtClaimTypes.FamilyName, "Smith"),
                        new Claim(JwtClaimTypes.Email, "[email protected]"),
                        new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean),
                        new Claim(JwtClaimTypes.WebSite, "http://alice.com")
                    }).Result;
                    if (!result.Succeeded)
                    {
                        throw new Exception(result.Errors.First().Description);
                    }
                    Console.WriteLine("alice created");
                }
                else
                {
                    Console.WriteLine("alice already exists");
                }

                var bob = userMgr.FindByNameAsync("bob").Result;
                if (bob == null)
                {
                    bob = new ApplicationUser
                    {
                        UserName = "bob"
                    };
                    var result = userMgr.CreateAsync(bob, "Pass123$").Result;
                    if (!result.Succeeded)
                    {
                        throw new Exception(result.Errors.First().Description);
                    } 
                    result = userMgr.AddClaimsAsync(bob, new Claim[]{
                        new Claim(JwtClaimTypes.Name, "Bob Smith"),
                        new Claim(JwtClaimTypes.GivenName, "Bob"),
                        new Claim(JwtClaimTypes.FamilyName, "Smith"),
                        new Claim(JwtClaimTypes.Email, "[email protected]"),
                        new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean),
                        new Claim(JwtClaimTypes.WebSite, "http://bob.com"),
                        new Claim("location", "somewhere")
                    }).Result;
                    if (!result.Succeeded)
                    {
                        throw new Exception(result.Errors.First().Description);
                    }
                    Console.WriteLine("bob created");
                }
                else
                {
                    Console.WriteLine("bob already exists");
                }
            }

            Console.WriteLine("Done seeding database.");
            Console.WriteLine();
        }
    }

Configure and add custom user information and identity.

3.6.2 calling method

Then we can go from the main entranceMainMethod calls it:

public static void Main(string[] args)
        {
            var seed = args.Contains("/seed");
            if (seed)
            {
                args = args.Except(new[] { "/seed" }).ToArray();
            }
            var host = CreateHostBuilder(args).Build();
            if (seed)
            {
                SeedData.EnsureSeedData(host.Services);
            }
            host.Run();
        }

3.6.3 program operation

inputdotnet run /seed 

3.6.4 effect

summary

  1. This article briefly introducesIdentityCustomize the user and table structure description, and generate the model according to the custom changes and add it to the sample project.
  2. This identity authentication mechanism will be applied toIdentityServer4Used in to perform user role management and storage operations.
  3. If there is something wrong or incomprehensible, I hope you can make more corrections, ask questions, discuss together, keep learning and make common progress.
  4. Project address

additional

Model customization in identity asp.net core