1. 程式人生 > >使用Identity Server 4建立Authorization Server (5)

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

連接字符串 mapr path 框架 ise network edit setting pin

預備知識: http://www.cnblogs.com/cgzl/p/7746496.html

第一部分: http://www.cnblogs.com/cgzl/p/7780559.html

第二部分: http://www.cnblogs.com/cgzl/p/7788636.html

第三部分: http://www.cnblogs.com/cgzl/p/7793241.html

第四部分: http://www.cnblogs.com/cgzl/p/7795121.html

之前的配置都是在內存中, 下面將如何把這些數據存儲到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 客戶端

這將是最後一部分.

這個要等到我把項目的angular5客戶端基礎框架搭建好之後才能寫.

先告一段落

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