預備知識: 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 客戶端
這將是最後一部分.
手頭的專案有點急. 過幾天再寫這個.