Identity Server WebAPI



DAL
安裝所需套件
install-package AutoMapper

新增 Models目錄 新增 ApplicationUser.cs
using Microsoft.AspNetCore.Identity;
    public class ApplicationUser :IdentityUser
    {
          ...新增所需要之欄位
    }
新增 Models目錄 新增 ApplicationRole.cs
using Microsoft.AspNetCore.Identity;
    public class ApplicationRole :IdentityRole
    {
          ...新增所需要之欄位
    }
專案目錄下 新增 ApplicationDbContext.cs
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
    public class ApplicationDbContext :IdentityDbContext<ApplicationUser,ApplicationRole,string>
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
    : base(options)
        {
        }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);
        }
    }
新增 專案名稱 IdentityWebAPI 並選擇 API

將DAL 專案加入參考
安裝所需套件
install-package AutoMapper
install-package IdentityServer4
install-package IdentityServer4.AccessTokenValidation
install-package IdentityServer4.AspNetIdentity
install-package Swashbuckle.AspNetCore
install-package Serilog.Extensions.Logging.File
install-package Microsoft.VisualStudio.Web.CodeGeneration.Design -Version 2.1.0
修改 appsettings.json,新增資料庫連結字串
"ConnectionStrings": {
  "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=MyDbWebApi;Trusted_Connection=True;MultipleActiveResultSets=true"
}

加入 Swagger 參考頁面:使用 Swagger 的 ASP.NET Core Web API 說明頁面
開啟 Startup.cs 新增下列 code 

using Swashbuckle.AspNetCore.Swagger;

public void ConfigureServices(IServiceCollection services)
        {
            ...
            services.AddSwaggerGen(c =>
                {
                    c.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" });
                });
            ...
        }

 public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            ...
            app.UseSwagger();
            // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), 
            // specifying the Swagger JSON endpoint.
            app.UseSwaggerUI(c =>
              {
                c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
                c.RoutePrefix = "swagger"; //在 LocalHost:????/Swagger 顯示
              });
            ...
        }
CTRL+F5 出現下列畫面
配置 Serial.log
參照 https://github.com/serilog/serilog/wiki/Getting-Started
參照 https://itnext.io/loggly-in-asp-net-core-using-serilog-dc0e2c7d52eb
參照 https://github.com/serilog/serilog-aspnetcore/tree/dev/samples/SimpleWebSample
參照 https://github.com/serilog/serilog/wiki/Configuration-Basics

我們已經準備好實施Serilog。我們將從Program.cs文件開始。
using Serilog;
using Serilog.Events;
using Serilog.Sinks.SystemConsole.Themes;

  public class Program
    {
        public static void Main(string[] args)
        {
            var webHost = BuildWebHost(args);
            webHost.Run();
        }

        public static IWebHost BuildWebHost(string[] args)
        {
            Log.Logger = new LoggerConfiguration()
               .MinimumLevel.Debug()
               .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
               .MinimumLevel.Override("System", LogEventLevel.Warning)
               .MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Information)
               .Enrich.FromLogContext()
               .WriteTo.File(@"identityserver4_log.txt")
               .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Literate)
               .CreateLogger();


            return WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>()
                .ConfigureLogging(builder =>
                {
                    builder.ClearProviders();
                    builder.AddSerilog();
                })
                .Build();
        }

加入 AutoMapper
開啟 Startup.cs 新增下列 code 
Using AddAutoMapper;
services.AddAutoMapper();
services.AddMvc();

Now, you just have to create your Mapping profiles.
Here is an example of mine:

using AutoMapper;

public class MappingProfile : Profile
{
    public MappingProfile()
    {
        CreateMap()
           .ForMember(a => a.ModelId, b=> b.MapFrom(c=> c.ModelName));
     }
}

Identity 配置
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity;

            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"), b => b.MigrationsAssembly("Your Project Name")));


資料庫建成
add-migration init0708
update-database

using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity;

            // SQLServer & Identity.
            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"), b => b.MigrationsAssembly("Your Project Name")));


            services.AddIdentity<ApplicationUser, ApplicationRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultTokenProviders();

            // Identity options.
            services.Configure<IdentityOptions>(options =>
            {
                // Password settings.
                options.Password.RequireDigit = true;
                options.Password.RequiredLength = 8;
                options.Password.RequireNonAlphanumeric = false;
                options.Password.RequireUppercase = true;
                options.Password.RequireLowercase = false;
                // Lockout settings.
                options.Lockout.AllowedForNewUsers = true;
                options.Lockout.MaxFailedAccessAttempts = 3;
                options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromDays(1);
            });
參照 https://github.com/robisim74/AngularSPAWebAPI/blob/master/EXPLANATION.md
修改 Satraup.cs 添加相關的服務,SQLite&Identity 放在所有ID4最上層
Seed Data
Data 目錄下新增 DbInitializer.cs
    public class DbInitializer : IDbInitializer
    {
        private readonly UserManager _userManager;
        private readonly RoleManager _roleManager;

        public DbInitializer(
            UserManager<ApplicationUser> userManager,
            RoleManager<ApplicationRole> roleManager
            )
        {
            this._userManager = userManager;
            this._roleManager = roleManager;
        }

        public async Task Initialize(ApplicationDbContext context)
        {
            context.Database.EnsureCreated();

            if (context.Users.Any())
            {
                return; // Db has been seeded.
            }

            // Creates Roles.
            // await _roleManager.CreateAsync(new IdentityRole("administrator"));
            // await _roleManager.CreateAsync(new IdentityRole("user"));
           
            var role = new ApplicationRole
            {
                Name = "administrator",
                Description= "管理者"
            };
            await _roleManager.CreateAsync( role );

            var role1 = new ApplicationRole
            {
                Name = "user",
                Description = "使用者"
            };

            await _roleManager.CreateAsync(role1);


            // Seeds an admin user.
            var user = new ApplicationUser
            {
                GivenName = "Admin",
                FamilyName = "Admin",
                AccessFailedCount = 0,
                Email = "admin@gmail.com",
                EmailConfirmed = false,
                LockoutEnabled = true,
                NormalizedEmail = "ADMIN@GMAIL.COM",
                NormalizedUserName = "ADMIN@GMAIL.COM",
                TwoFactorEnabled = false,
                UserName = "admin@gmail.com"
            };

            var result = await _userManager.CreateAsync(user, "Admin01*");

            if (result.Succeeded)
            {
                var adminUser = await _userManager.FindByNameAsync(user.UserName);
                // Assigns the administrator role.
                await _userManager.AddToRoleAsync(adminUser, "administrator");
                // Assigns claims.
                var claims = new List<Claim> {
                    new Claim(type: JwtClaimTypes.GivenName, value: user.GivenName),
                    new Claim(type: JwtClaimTypes.FamilyName, value: user.FamilyName),
                };
                await _userManager.AddClaimsAsync(adminUser, claims);
            }
        }
    }

創建 Services 目錄 ,新增 IDbInitializer.cs 
    public interface IDbInitializer
    {
        Task Initialize(ApplicationDbContext context);
    }

Startup.cs
            // Adds application services.
            services.AddTransient();
            services.AddTransient();

Identity Server 4 配置
根目錄下新增 config.cs 配置 identity4 內容 (尚未完整)
using IdentityServer4;
using IdentityServer4.Models;

// Clients want to access resources.
public static IEnumerable GetClients()
{
    // Clients credentials.
    return new List
    {
        // http://docs.identityserver.io/en/release/topics/clients.html
        // http://docs.identityserver.io/en/release/quickstarts/1_client_credentials.html#
        new Client
        {
            ClientId = "AngularSPA",
            AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, // Resource Owner Password Credential grant.
            AllowAccessTokensViaBrowser = true,
            RequireClientSecret = false, // This client does not need a secret to request tokens from the token endpoint.

            AccessTokenLifetime = 900, // Lifetime of access token in seconds.

            AllowedScopes = {
                IdentityServerConstants.StandardScopes.OpenId, // For UserInfo endpoint.
                IdentityServerConstants.StandardScopes.Profile,
                "roles",
                "WebAPI"
            },
            AllowOfflineAccess = true, // For refresh token.
            RefreshTokenUsage = TokenUsage.OneTimeOnly,
            AbsoluteRefreshTokenLifetime = 7200,
            SlidingRefreshTokenLifetime = 900,
            RefreshTokenExpiration = TokenExpiration.Sliding,
            AllowedCorsOrigins = new List
            {
                "http://localhost:4200"
            } // Only for development.
        }
    };
}

正如你所看到的,你可以用自己的配置添加其他客戶端。我們的Angular應用程序被確定為AngularSPA:

使用ROPC ;
不使用秘密密鑰:在客戶端應用程序將是無用的,因為可見的;
有一個訪問令牌 15分鐘,然後需要刷新令牌;
可以訪問範圍:在這種情況下我們的Web API,稱為WebAPI和用戶角色;
具有用於刷新令牌的OfflineAccess ;
刷新令牌的滑動壽命為15分鐘,最長壽命為2小時:刷新令牌的壽命等於或大於訪問令牌,以允許用戶保持身份驗證狀態,但最多2小時。
以下是資源:
// Identity resources (used by UserInfo endpoint).
public static IEnumerable GetIdentityResources()
{
    return new List
    {
        new IdentityResources.OpenId(),
        new IdentityResources.Profile(),
        new IdentityResource("roles", new List { "role" })
    };
}

// Api resources.
public static IEnumerable GetApiResources()
{
    return new List
    {
        new ApiResource("WebAPI" ) {
            UserClaims = { "role" }
        }
    };
}


我們在Startup.cs文件的ConfigureServices方法中添加IdentityServer :
//添加IdentityServer。

services.AddIdentityServer()
     // AddDeveloperSigningCredential擴展創建用於簽署令牌的臨時密鑰材料。
     //這可能對開始有用,但需要由生產場景的一些持久性密鑰材料替換。
     //有關更多信息,請參閱http://docs.identityserver.io/en/release/topics/crypto.html#refcrypto。
     .AddDeveloperSigningCredential()
     .AddInMemoryPersistedGrants()
     //要將IdentityServer配置為使用EntityFramework(EF)作為配置數據的存儲機制(而不是使用內存中的實現),
     //請參閱https://identityserver4.readthedocs.io/en/release/quickstarts/8_entity_framework.html 
    .AddInMemoryIdentityResources(Config.GetIdentityResources())
    .AddInMemoryApiResources(Config.GetApiResources())
    .AddInMemoryClients(Config.GetClients())
    .AddAspNetIdentity<ApplicationUser>(); // IdentityServer4.AspNetIdentity.

留言

這個網誌中的熱門文章

CentOS 7 Install note