IdentityServer4 combination Mysql IdentityServer4 use OpenIdConnect achieve single sign-on

 

Previous: IdentityServer4 use OpenIdConnect achieve single sign-on .

Written in previous examples, IdeneityServer using a memory cache storage, all configurations are written in Config.cs years. In practice, you should use database storage, at any time convenient configuration, such as adding new users, resources, client, you can also save server memory.

From the three aspects are achieved IdentityServer4 binding Mysql implement database storage, each client and resource data, and the token authorization code data and user data.

First, prepare the content

1, ready to MySql database server, create a new empty database

2, IdentityServer need to install several packages.

IdentityServer4.EntityFramework 
Microsoft.EntityFrameworkCore.Tools 
Pomelo.EntityFrameworkCore.MySql (you can also use MySql official package: MySql.Data.EntityFrameworkCore)

3, appsettings.json add a database connection string

{
  "ConnectionStrings": {
    "MySqlDbConnectString": "server=IP;userid=mysqlUserName;pwd=user's password;database=database name;connectiontimeout=30;Pooling=true;Max Pool Size=300; Min Pool Size=5;"
  }
}  

Second, the client and database storage resources

Earlier we use AddInMemory way to load configuration data

AddInMemoryIdentityResources(Config.GetIdentityResources())
AddInMemoryApiResources(Config.GetApis())
AddInMemoryClients(Config.GetClients())

Comment out these three lines of code, replace the following code

  var connection = Configuration.GetConnectionString("MySqlDbConnectString");
            var builder = services.AddIdentityServer()
                //身份信息资源
                //.AddInMemoryIdentityResources(Config.GetIdentityResources())
                .AddConfigurationStore(opt =>
                {
                    opt.ConfigureDbContext = context =>
                    {
                        context.UseMySql(connection, sql =>
                        {
                            sql.MigrationsAssembly("IdentityServer");
                        });
                    };
                })
                .AddTestUsers(Config.GetUsers());}

Configuration data from a database query, certainly have to add the corresponding data tables, the IdentityServer project is set to start the project, open the Package Manager console, set the default project IdentityServer console, enter the following command in the console

PM> add-migration ConfigDbContext -c ConfigurationDbContext  -o Data/Migrations/IdentityServer/PersistedGrantDb
To undo this action, use Remove-Migration.
PM> update-database
Done.

-c ConfigurationDbContext is designated the current database context. ConfigurationDbContext defined in the namespace: IdentityServer4.EntityFramework.DbContexts, and yesterday they use CodeFirst, the definition of the client and the resources associated with data tables and tables. add-migration generation mobility shift record, update-datase update the database to date.

 

 

 But now the database client and resource data are empty, you need to add the Config configuration data to the database, you can add the database initialization method in the Start.cs

 private void InitializeDatabase(IApplicationBuilder app)
        {
            using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
            {
                var context = serviceScope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
                if (!context.Clients.Any())
                {
                    foreach (var client in Config.GetClients())
                    {
                        context.Clients.Add(client.ToEntity());
                    }
                    context.SaveChanges();
                }

                if (!context.IdentityResources.Any())
                {
                    foreach (var resource in Config.GetIdentityResources())
                    {
                        context.IdentityResources.Add(resource.ToEntity());
                    }
                    context.SaveChanges();
                }

                if (!context.ApiResources.Any())
                {
                    foreach (var resource in Config.GetApis())
                    {
                        context.ApiResources.Add(resource.ToEntity());
                    }
                    context.SaveChanges();
                }
            }
        }

Description: Using app.ApplicationServices.GetService <IServiceScopeFactory> () CreateScope create a new service scopes and isolated from other scopes, does not affect the scope of the release of other contexts, the operating entity to update the database and we usually use the same. After running the program, we found that the database already has data

 

 

 Run IdentityServer, IdentityMvc, IdentityApi three programs tested

 

 

Second, the token and the authorization code stored in the database

Use IIdentityServerBuilder expansion writing method AddOperationalStore

// the client and resource database to store configuration 
                .AddConfigurationStore (opt => 
                { 
                    opt.ConfigureDbContext context = => 
                    { 
                        context.UseMySql (Connection, SQL => 
                        { 
                            sql.MigrationsAssembly ( "IdentityServer"); 
                        }); 
                    }; 
                }) 
                // the token database stores configuration and authorization code 
                .AddOperationalStore (opt => 
                { 
                    opt.ConfigureDbContext context = => 
                    {  
                        context.UseMySql (Connection, SQL =>
                        {
                            sql.MigrationsAssembly("IdentityServer");
                        });
                    };
                    opt.EnableTokenCleanup = true;
                    opt.TokenCleanupInterval = 30;
                })
                .AddTestUsers(Config.GetUsers());

 Similarly, the need to add a data table for storing

PM> add-migration OperationContext -c PersistedGrantDbContext -o Data/Migrations/IdentityServer/OperationDb
Multiple startup projects set.
PM> update-database -c PersistedGrantDbContext
Done.

  Run IdentityServer, IdentityMvc, IdentityApi three programs tested

 

 

 Third, the user database storage

IdentityServer4 IIdentityServerBuilder provide support and resources to the client database storage AddConfigurationStore method to support the token authorization code and database storage AddOperationalStore, but did not provide a user database storage methods. I think because the client storage structure, resources, token, authorization code is mandatory, rather than the user, user-defined everyone's resources, roles, permissions, and other basic information storage structure is different. But no, you can write your own imitation AddConfigurationStore AddUserStore method that supports user database storage.

In IdentityServer project to create a new file called IdentityUserStore folder, create four classes

IdentityServer.IdentityUserStore.IdentityUser: user entity

public class IdentityUser
    {
        [Key]
        [Required]
        public string SubjectId { get; set; }
        [Required]
        public string Username { get; set; }
        [Required]
        public string Password { get; set; }
        public string ProviderName { get; set; }
        public string ProviderSubjectId { get; set; }
        public bool IsActive { get; set; }
     public ICollection<IdentityUserClaim> IdentityUserClaims { get; set; } } public class IdentityUserClaim { [Key] public string ClaimId { get; set; } [Required] public string Name { get; set; } [Required] public string Value { get; set; } [Required] public string UserSubjectId { get; set; } [ForeignKey("UserSubjectId")] public virtual IdentityUser IdentityUser { get; set; } }

IdentityServer.IdentityUserStore.UserStoreDbContext: database for defining operating context, and the role of previously used ConfigurationDbContext, as PersistedGrantDbContext

 public class UserStoreDbContext:DbContext
    {
        public UserStoreDbContext(DbContextOptions opt) : base(opt)
        {
            
        }
        public DbSet<IdentityUser> IdentityUser { get; set; }
        public DbSet<IdentityUserClaim> IdentityUserClaim { get; set; }
    }

IdentityServer.IdentityUserStore.UserStore: used to query the user information and password verification

 public class UserStore 
    {
        private readonly UserStoreDbContext _dbContext;
        public UserStore(UserStoreDbContext dbContext)
        {
            _dbContext = dbContext;
        }
        /// <summary>
        /// 根据SubjectID查询用户信息
        /// </summary>
        /// <param name="subjectId">用户id</param>
        /// <returns></returns>
        public IdentityUser FindBySubjectId(string subjectId) {
            return _dbContext.Set<IdentityUser>().Where(r => r.SubjectId.Equals(subjectId)).Include(r => r.IdentityUserClaims).SingleOrDefault();
        }
        /// <summary>
        /// user query based on user name
        /// </summary>
        /// <param name="username">用户</param>
        /// <returns></returns>
        public IdentityUser FindByUsername(string username)
        {
            return _dbContext.Set<IdentityUser>().Where(r => r.Username.Equals(username)).Include(r => r.IdentityUserClaims).SingleOrDefault();
        }
        /// <summary>
        /// 验证登录密码
        /// </summary>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public bool ValidateCredentials(string username, string password)
        {
            password = Config.MD5Str(password);
            var user = _dbContext.Set<IdentityUser>().Where(r => r.Username.Equals(username)
            &&r.Password.Equals(password)).Include(r => r.IdentityUserClaims).SingleOrDefault();
            return user != null;
        }
    }

  IdentityServer.IdentityUserStore.IIdentityServerBuilderUserStoreExtensions: write IIdentityServerBuilder to expand, add the UserStoreDbContext, UserStoreDbContext service dependency.

  public static class IIdentityServerBuilderUserStoreExtensions
    {
        public static IIdentityServerBuilder AddUserStore(this IIdentityServerBuilder builder, Action<DbContextOptionsBuilder> userStoreOptions = null)
        {
            builder.Services.AddDbContext<UserStoreDbContext>(userStoreOptions);
            builder.Services.AddTransient<UserStore>();
            return builder;
        }
    }

 In the following the code Startup.ConfigureServices process to replace AddTestUser (Config.GetUsers)

          //.AddTestUsers(Config.GetUsers())
                .AddUserStore(opt =>
                {
                    opt.UseMySql(connection, sql =>
                    {
                        sql.MigrationsAssembly("IdentityServer");
                    });
                })

Adding users to store data table, enter the command in the package manager

PM> add-migration UserStoreContext -c UserStoreDbContext -o Data/Migrations/IdentityServer/UserDb
PM> update-database -c UserStoreDbContext
Multiple startup projects set.
Using project 'src\IdentityServer' as the startup project.
Done.
PM> 

Adding user data in the database class Config initial process

 private void InitializeDatabase(IApplicationBuilder app)
        {
            using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
            {
                var context = serviceScope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
                var userContext = serviceScope.ServiceProvider.GetRequiredService<UserStoreDbContext>();
                //添加config中的客户端数据到数据库
                if (!context.Clients.Any())
                {
                    foreach (var client in Config.GetClients())
                    {
                        context.Clients.Add(client.ToEntity());
                    }
                    context.SaveChanges (); 
                } 
                // add data to the database config IdentityResources the 
                IF (context.IdentityResources.Any ()!) 
                { 
                    the foreach (var Resource in Config.GetIdentityResources ()) 
                    { 
                        context.IdentityResources.Add (resource.ToEntity ()); 
                    } 
                    context.SaveChanges (); 
                } 
                // Add ApiResources data into a database config 
                IF (context.ApiResources.Any (!)) 
                { 
                    the foreach (var Resource in Config.GetApis ()) 
                    {
                        context.ApiResources.Add(resource.ToEntity());
                    }
                    context.SaveChanges();
                }
                //添加config中的Users数据到数据库
                if (!userContext.IdentityUser.Any())
                {
                    int index = 0;
                    foreach(var user in Config.GetUsers())
                    {
                        IdentityUser iuser = new IdentityUser()
                        {
                            IsActive = user.IsActive,
                            Password = user.Password,
                            ProviderName = user.ProviderName,
                            ProviderSubjectId = user.ProviderSubjectId,
                            SubjectId = user.SubjectId,
                            Username = user.Username,
                            IdentityUserClaims = user.Claims.Select(r => new IdentityUserClaim()
                            {
                                ClaimId = (index++).ToString(),
                                Name = r.Type,
                                Value = r.Value
                            }).ToList()
                        };
                        userContext.IdentityUser.Add(iuser);
                    }
                    userContext.SaveChanges();
                }
            }
        }

   You can see the database has user data

 

 The final step, use the above new UserStore class to verify the login and password information user queries, and add users Claim.

AccountController Constructor

 //private readonly TestUserStore _users; 这里改成了UserStore
        private readonly UserStore _users;
        private readonly IIdentityServerInteractionService _interaction;
        private readonly IClientStore _clientStore;
        private readonly IAuthenticationSchemeProvider _schemeProvider;
        private readonly IEventService _events;
        public AccountController(
            IIdentityServerInteractionService interaction,
            IClientStore clientStore,
            IAuthenticationSchemeProvider schemeProvider,
            IEventService events,
            //TestUserStore _users=null,这里改成了UserStore
            UserStore users)
        {
            // _users = User ?? new TestUserStore(Config.GetUsers()); 这里改成了UserStore
            _users = users;
            _interaction = interaction;
            _clientStore = clientStore;
            _schemeProvider = schemeProvider;
            _events = events;
        }

 Task <IActionResult> Login (LoginInputModel model, string button), when the log data in the database IdentityUserClaims converted to Claim log data into overloaded methods.

  List<Claim> claims = user.IdentityUserClaims.Select(r => new Claim(r.Name,r.Value) ).ToList();
  await HttpContext.SignInAsync(user.SubjectId, user.Username, props, claims.ToArray());

  Run IdentityServer, after IdentityApi, IdentityMvc three projects Registration Authority - Get access_token- access api

 

 

 

  

  

  

  

 

 

  

  

 

 

 

 

 

  

 

Guess you like

Origin www.cnblogs.com/liujiabing/p/11599146.html