iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 14
1

本篇同步發文在個人Blog: 一袋.NET要扛幾樓?打造容器化的ASP.NET Core網站!系列文章 - (14) 建立會員系統 - 3

1. 新增Config.cs

在專案AuthApi的根目錄新增類別Config.cs,這裡包含所有IdentityServer4要建立的Client、Scope、Resource。

Client定義應用程式的授權流程以及OIDC的網址,本專案對WebMvc採用混和(Hybrid)。Scope代表資源能存取的範圍、Resource代表有哪些資源。

    using IdentityServer4;
    using IdentityServer4.Models;
    using Microsoft.Extensions.Configuration;
    using System.Collections.Generic;
    
    namespace TokenServiceApi
    {
        public class Config
        {
            public static Dictionary<string, string> GetUrls(IConfiguration configuration)
            {
                Dictionary<string, string> urls = new Dictionary<string, string>();
                urls.Add("Mvc", configuration.GetValue<string>("MvcClient"));
    
                return urls;
            }
    
            public static IEnumerable<ApiScope> GetApiScopes()
            {
                return new List<ApiScope>
                {
                    new ApiScope("basket", "basket api"),
                    new ApiScope("order", "order api"),
                    new ApiScope("report", "report api")
                };
            }
    
            public static IEnumerable<ApiResource> GetApiResources()
            {
                return new List<ApiResource>
                {
                    new ApiResource("basket", "Shopping Cart Api")
                    {
                        Scopes = new List<string>
                        {
                            "basket"
                        }
                    },
                    new ApiResource("order", "Ordering Api")
                    {
                        Scopes = new List<string>
                        {
                            "order"
                        }
                    },
                    new ApiResource("report", "Report Api"){
                        Scopes = new List<string>
                        {
                            "report"
                        }
                    }
                };
            }
    
            public static IEnumerable<IdentityResource> GetIdentityResources()
            {
                return new List<IdentityResource>
                {
                    new IdentityResources.OpenId(),
                    new IdentityResources.Profile()
                };
            }
    
            public static IEnumerable<Client> GetClients(Dictionary<string, string> clientUrls)
            {
                return new List<Client>
                {
                    new Client
                    {
                        ClientId = "mvc",
                        ClientSecrets = new []{new Secret("secret".Sha256())},
                        AllowedGrantTypes = GrantTypes.Hybrid,
                        RedirectUris = {$"{clientUrls["Mvc"]}/signin-oidc"},
                        PostLogoutRedirectUris = {$"{clientUrls["Mvc"]}/signout-callback-oidc"},
                        AllowAccessTokensViaBrowser = false,
                        AllowOfflineAccess = true,
                        RequireConsent = false,
                        RequirePkce = false,
                        AlwaysIncludeUserClaimsInIdToken  =true,
                        AllowedScopes = new List<string>
                        {
                            IdentityServerConstants.StandardScopes.OpenId,
                            IdentityServerConstants.StandardScopes.Profile,
                            IdentityServerConstants.StandardScopes.OfflineAccess,
                            "order",
                            "basket",
                            "report"
                        }
                    }
                };
            }
        }
    }

2. 新增ApplicationUser類別

  在Models資料夾新增ApplicationUser.cs,並繼承IdentityUser,這個ApplicationUser可以自己加想要的屬性,在這我們都用空的。

3. 修改DbContext

  在Data資料夾會有ApplicationDbContext,將它改成繼承IdentityDbContext

4. 新增初始化的登入帳號的類別

  在Data資料夾新增IdentityDbInit.cs,注入ApplicationDbContext和UserManager,每次程式啟動時會檢查Migrations,並確認是否有測試的帳號,沒有的話則新增它。

    using AuthApi.Models;
    using Microsoft.AspNetCore.Identity;
    using Microsoft.EntityFrameworkCore;
    using System.Linq;
    using System.Threading.Tasks;
    
    namespace AuthApi.Data
    {
        public class IdentityDbInit
        {
            public static async Task Initialize(
                ApplicationDbContext context,
                UserManager<ApplicationUser> userManager)
            {
                context.Database.Migrate();
                if (context.Users.Any(r => r.UserName == "test@test.com"))
                {
                    return;
                }
                    
    
                string user = "test@test.com";
                string password = "P@ssword1";
                await userManager.CreateAsync(new ApplicationUser { UserName = user, EmailConfirmed = true }, password);
            }
        }
    }

5. 修改Program.cs

程式啟動都會呼叫IdentityDbInit作資料庫的Migrations和測試帳號的新增與否

    public static void Main(string[] args)
            {
                var host = CreateHostBuilder(args).Build();
    
                using (var scope = host.Services.CreateScope())
                {
                    var services = scope.ServiceProvider;
    
                    try
                    {
                        var context = services.GetRequiredService<ApplicationDbContext>();
                        var userManager = services.GetRequiredService<UserManager<ApplicationUser>>();
                        IdentityDbInit.Initialize(context, userManager).Wait();
                    }
                    catch (Exception ex)
                    {
                        var logger = services.GetRequiredService<ILogger<Program>>();
                        logger.LogError(ex, "An error occurred while seeding the Authorization Server database.");
                    }
                }
    
                host.Run();
            }

6. 修改Startup.cs

註冊IdentityServer4和資料庫整合的相關服務,並在Pipeline新增IdentityServer4的middleware

    using AuthApi.Data;
    using AuthApi.Models;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.Identity;
    using Microsoft.EntityFrameworkCore;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    using TokenServiceApi;
    
    namespace AuthApi
    {
        public class Startup
        {
            public Startup(IConfiguration configuration)
            {
                Configuration = configuration;
            }
    
            public IConfiguration Configuration { get; }
    
            // This method gets called by the runtime. Use this method to add services to the container.
            public void ConfigureServices(IServiceCollection services)
            {
                string server = Configuration["DatabaseServer"];
                string database = Configuration["DatabaseName"];
                string user = Configuration["DatabaseUser"];
                string password = Configuration["DatabasePassword"];
                string connectionString = string.Format("Server={0};Database={1};User={2};Password={3};", server, database, user, password);
                services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(connectionString));
    
                services.AddIdentity<ApplicationUser, IdentityRole>()
                    .AddEntityFrameworkStores<ApplicationDbContext>()
                    .AddDefaultTokenProviders();
    
                services.AddControllersWithViews();
                services.AddRazorPages();
    
                var builder = services.AddIdentityServer(options =>
                {
                    options.Events.RaiseErrorEvents = true;
                    options.Events.RaiseInformationEvents = true;
                    options.Events.RaiseFailureEvents = true;
                    options.Events.RaiseSuccessEvents = true;
    
                    options.EmitStaticAudienceClaim = true;
                })
                .AddDeveloperSigningCredential()
                .AddInMemoryPersistedGrants()
                .AddInMemoryApiScopes(Config.GetApiScopes())
                .AddInMemoryIdentityResources(Config.GetIdentityResources())
                .AddInMemoryApiResources(Config.GetApiResources())
                .AddInMemoryClients(Config.GetClients(Config.GetUrls(Configuration)))
                .AddAspNetIdentity<ApplicationUser>();
    
                services.AddControllersWithViews();
                services.AddAuthentication();
    
            }
    
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                    app.UseDatabaseErrorPage();
                }
                else
                {
                    app.UseExceptionHandler("/Home/Error");
                    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                    app.UseHsts();
                }
                app.UseHttpsRedirection();
                app.UseStaticFiles();
    
                app.UseRouting();
    
                app.UseIdentityServer();
                app.UseAuthentication();
                app.UseAuthorization();
    
                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapControllerRoute(
                        name: "default",
                        pattern: "{controller=Home}/{action=Index}/{id?}");
                    endpoints.MapRazorPages();
                });
            }
        }
    }

7. 修改appSettings.json

  新增資料庫的連線資訊,和CatalogApi專案的方式一樣,只是資料庫名稱是AuthDb:

  "DatabaseServer": "localhost,1445",

  "DatabaseName": "AuthDb",

  "DatabaseUser": "sa",

  "DatabasePassword": "JustTest!",

8. 在專案啟用Db Migrations

  專案在初始化後已經有預設的Migrations類別,在根目錄使用cmd執行遷移:

    dotnet ef database update

===

9. Debug執行程式

  使用VS執行AuthApi,在Account/Login做登入,成功的話在diagnostics會顯示JWT相關的值,如圖1與圖2

圖1

圖2


上一篇
[Day13] 建立會員系統 - 2
下一篇
[Day15] 建立會員系統 - 4
系列文
一袋.NET要扛幾樓?打造容器化的ASP.NET Core網站!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言