使用Identity Server 4建立Authorization Server (5)

之前的配置都是在内存中, 下面将如何把这些数据存储到Sql Server数据库, 这样更适合生产环境.

这部分基本完全参考官方文档:

https://identityserver4.readthedocs.io/en/release/quickstarts/8_entity_framework.html

安装Entity Framework相关的库

为Authorization Server 添加 IdentityServer4.EntityFramework:

还需要安装Microsoft.EntityFrameworkCore.SqlServer:

最后是Microsoft.EntityFrameworkCore.Tools:

使用它可以进行迁移等操作.

然后使用命令行进入Auth Server项目的目录, 试一下dotnet ef命令:

很不幸, 没找到dotnet ef命令. 这里需要手动修改AuthServer的项目文件, 右键点击项目, 点击Edit AuthServer.csproj.

这部分操作的官方文档在这: https://docs.microsoft.com/en-us/ef/core/miscellaneous/cli/dotnet

我们需要添加这部分代码:

  <ItemGroup>
    <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />
  </ItemGroup>

然后回到命令行, 再执行 dotnet ef:

这次好用了. 接下来就是add migrations了.

幸运的是, 之前装的库里面有封装好的model, 它们可以自动创建migration文件.

这里一共有两个命令(migrations), 一个是为了IdentityServer的配置, 另一个是为了持久化授权.

dotnet ef migrations add InitialIdentityServerPersistedGrantDbMigration -c PersistedGrantDbContext -o Data/Migrations/IdentityServer/PersistedGrantDb
dotnet ef migrations add InitialIdentityServerConfigurationDbMigration -c ConfigurationDbContext -o Data/Migrations/IdentityServer/ConfigurationDb

运行发现了问题, 这是因为我们还没有配置AuthServer来使用数据库.

添加appSettings.json, 并指定连接字符串:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=AuthServer;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "Logging": {
    "IncludeScopes": false,
    "Debug": {
      "LogLevel": {
        "Default": "Warning"
      }
    },
    "Console": {
      "LogLevel": {
        "Default": "Warning"
      }
    }
  }
}

修改Startup.cs:

public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            var connectionString = Configuration.GetConnectionString("DefaultConnection");
            var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;

            services.AddIdentityServer()
                // .AddDeveloperSigningCredential()
                .AddSigningCredential(new X509Certificate2(@"D:\Projects\test\socialnetwork.pfx", "Bx@steel"))
                .AddTestUsers(InMemoryConfiguration.Users().ToList())
                .AddConfigurationStore(options =>
                {
                    options.ConfigureDbContext = builder =>
                        builder.UseSqlServer(connectionString,
                            sql => sql.MigrationsAssembly(migrationsAssembly));
                })
                // this adds the operational data from DB (codes, tokens, consents)
                .AddOperationalStore(options =>
                {
                    options.ConfigureDbContext = builder =>
                        builder.UseSqlServer(connectionString,
                            sql => sql.MigrationsAssembly(migrationsAssembly));

                    // this enables automatic token cleanup. this is optional.
                    options.EnableTokenCleanup = true;
                    options.TokenCleanupInterval = 30;
                });

            services.AddMvc();
        }

首先获取数据库连接字符串, 然后添加两部分配置, 一个是配置数据(clients, resources), 一个是操作数据(tokens, codes, consents同意).

再次运行命令行:

dotnet ef migrations add InitialIdentityServerPersistedGrantDbMigration -c PersistedGrantDbContext -o Data/Migrations/IdentityServer/PersistedGrantDb
dotnet ef migrations add InitialIdentityServerConfigurationDbMigration -c ConfigurationDbContext -o Data/Migrations/IdentityServer/ConfigurationDb

好用了.

看看生成的文件, 是有这两部分:

看一下文件的内容, 会发现有很多的Table.

下一步就是添加自动迁移, 暂且在StartUp里面找个位置新建个方法吧:

        private void InitializeDatabase(IApplicationBuilder app)
        {
            using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
            {
                serviceScope.ServiceProvider.GetRequiredService<PersistedGrantDbContext>().Database.Migrate();

                var context = serviceScope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
                context.Database.Migrate();
                if (!context.Clients.Any())
                {
                    foreach (var client in InMemoryConfiguration.Clients())
                    {
                        context.Clients.Add(client.ToEntity());
                    }
                    context.SaveChanges();
                }

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

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

首先是分别对两个context进行迁移, 然后判断是否这些表里是空的, 如果没有数据, 就把配置的内存数据添加到数据库里面.

别忘了在Configure方法调用:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            InitializeDatabase(app);

            app.UseDeveloperExceptionPage();
            app.UseIdentityServer();
            app.UseStaticFiles();
            app.UseMvcWithDefaultRoute();
        }

运行项目, 重新操作一下登陆, 同意的过程, 依然好用.

看一下数据库:

确实生成了很多表.

查看Clients表, 里面有三条数据.

PersistedGrants里面也有一条数据. 登陆时当你同意请求许可的时候, 就会在这个表里面添加一条数据.

把用户存储到数据库

可以使用自定义的用户表来存储用户数据, 但是我要用的是asp.net core identity, 所以我就不讲别的方式了.

不过首先, 需要重建个项目, 并且把之前讲的所有内容都操作一遍, 因为这里要使用asp.net core mvc 模板并使用Individual User Account的验证方式:

建立好项目后, 需要把之前讲的所有步骤操作一下, 然后安装: IdentityServer4.AspNetIdentity:

修改Startup, 大约成为这个样子, 只看红色部分即可:

namespace AuthorizationServer
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }
        
        public void ConfigureServices(IServiceCollection services)
        {
            var connectionString = Configuration.GetConnectionString("DefaultConnection");
            var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;

            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(connectionString));

            services.AddIdentity<ApplicationUser, IdentityRole>(options =>
            {
                // Password settings
                options.Password.RequireDigit = false;
                options.Password.RequiredLength = 6;
                options.Password.RequireNonAlphanumeric = false;
                options.Password.RequireUppercase = false;
                options.Password.RequireLowercase = false;
                options.Password.RequiredUniqueChars = 2;
                // Lockout settings
                options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
                options.Lockout.MaxFailedAccessAttempts = 5;
                options.Lockout.AllowedForNewUsers = true;
                // Signin settings
                options.SignIn.RequireConfirmedEmail = false;
                options.SignIn.RequireConfirmedPhoneNumber = false;
                // User settings
                options.User.RequireUniqueEmail = false;                
            })
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultTokenProviders();

            services.AddTransient<IEmailSender, EmailSender>();
            services.AddMvc();
            
            services.AddIdentityServer()
                .AddDeveloperSigningCredential()
                // .AddSigningCredential(new X509Certificate2(@"D:\Projects\test\socialnetwork.pfx", "password"))
                .AddConfigurationStore(options =>
                {
                    options.ConfigureDbContext = builder =>
                        builder.UseSqlServer(connectionString,
                            sql => sql.MigrationsAssembly(migrationsAssembly));
                })
                .AddOperationalStore(options =>
                {
                    options.ConfigureDbContext = builder =>
                        builder.UseSqlServer(connectionString,
                            sql => sql.MigrationsAssembly(migrationsAssembly));
                    options.EnableTokenCleanup = true;
                    options.TokenCleanupInterval = 30;
                })
                .AddAspNetIdentity<ApplicationUser>();
        }
        
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.InitializeDatabase();
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseBrowserLink();
                app.UseDatabaseErrorPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            app.UseStaticFiles();
            app.UseIdentityServer();
            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

注意在Configure方法里面不要使用app.UseAuthentication(), 因为app.UseIdentityServer()方法已经包含了这个中间件. 然后使用命令行执行:

dotnet ef database update

或者在Packge Manager Console执行 update-database也行.

我照着官方文档操作出现了一些问题, 有几个重复的controller, 因为项目建立好之后有个HomeController和AccountController, 而使用Quickstart UI里面也有这两个Controller.

所以我最后clone了官方的例子:  https://github.com/IdentityServer/IdentityServer4.Samples/tree/dev/Quickstarts/6_AspNetIdentity

修改了一下, 放到了我这个项目里: https://github.com/solenovex/Learning-Identity-Server-4

其他

有的项目可能需要使用第三方登陆, 例如使用Google账户, 微软账户, QQ等, 这部分请看官方文档自行学习吧. 我要做的是企业内部项目. 所以这块先不研究了.

也有可能会使用Auth0, Stormpath这样的OAuth Provider, Auth0我用过, 登陆有点慢, 但功能很强大. 这个也不讲了, 他们的文档写的很好, 也给出了各种客户端的代码, 很容易集成.

Javascript 客户端

这将是最后一部分.

手头的项目有点急.  过几天再写这个.

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏JadePeng的技术博客

jenkins X实践系列(1) —— 背景知识

Jenkins X 是一个高度集成化的CI/CD平台,基于Jenkins和Kubernetes实现,旨在解决微服务体系架构下的云原生应用的持续交付的问题,简化整...

5832
来自专栏Java帮帮-微信公众号-技术文章全总结

Web-第十三天 基础加强-JDBC高级开发事务【悟空教程】

insert into account values (null,'jack',10000);

802
来自专栏散尽浮华

介绍linux下利用编译bash设置root账号共用的权限审计设置

在日常运维工作中,公司不同人员(一般是运维人员)共用root账号登录linux服务器进行维护管理,在不健全的账户权限审计制度下,一旦出现问题,就很难找出源头,甚...

2158
来自专栏猿天地

Spring Cloud中如何保证各个微服务之间调用的安全性

不是说你想调用就可以调用,一定要有认证机制,是我们内部服务发出的请求,才可以调用我们的接口。

2122
来自专栏散尽浮华

Centos下DNS+NamedManager高可用部署方案完整记录

之前说到了NamedManager单机版的配置,下面说下DNS+NamedManager双机高可用的配置方案:

6517
来自专栏北京马哥教育

linux 内存耗尽的分析

在测试NAS性能,用fstest长时间写,分析性能变差的原因,发现server主机内存使用率很高。 1.首先查看内存 # top -M top - 14:...

5716
来自专栏龙首琴剑庐

OpenJDK8 Windows编译

前言 Open JDK 是开源的JDK。通过对开源JDK的编译调试,我们可以深入了解JVM的运行原理。 以下是我的环境信息: 操作系统: Microsoft W...

71111
来自专栏张戈的专栏

零门槛!使用Docker快速部署ES集群

自从接触 Docker 之后,对 Docker 简直是爱不释手,做什么都是行云流水。遇到部署开源软件需求,第一时间想到的都是有没有现成的 Docker 镜像?能...

2.4K4
来自专栏玄魂工作室

Hacker基础之Linux篇:基础Linux命令十六

今天我们来学习几个小知识,不一定是Linux的命令,都是用于查看Linux的系统信息的

1503
来自专栏PHP在线

重磅资料!Github上的PHP资源汇总大全

依赖管理 ——用于依赖管理的包和框架 Composer/Packagist : 一个包和依赖管理器 Composer Installers: 一个多框架Com...

3124

扫码关注云+社区