認證授權:IdentityServer4

cwsheng發表於2020-09-06

前言

  上一篇文章<學習OIDC>介紹了OIDC協議,本篇開始我們就來具體來學習OIDC的具體實現IdentityServer4 學習。

一、IdentityServer4 是什麼?

   IdentityServer4是用於ASP.NET Core的OpenID Connect和OAuth 2.0框架。

  可以構建(或重新使用)包含登入和登出頁面的應用程式,IdentityServer中介軟體會向其新增必要的協議頭,以便客戶端應用程式可以使用這些標準協議與其對話。

   

   可以在應用程式中使用以下功能:

    • 身份驗證即服務

      所有應用程式(Web,本機,移動,服務)的集中式登入邏輯和工作流。IdentityServer是OpenID Connect 的官方認證實現

    • 單點登入/退出

      多種應用程式型別的單點登入(登出)

    • API的訪問控制

      為各種型別的客戶端(例如,伺服器到伺服器,Web應用程式,SPA和本機/移動應用程式)的API發出訪問令牌

    • 聯合閘道器

      支援外部身份提供程式,例如Azure Active Directory,Google,Facebook等。這使您的應用程式免受如何連線到這些外部提供程式的詳細資訊的影響。

二、簡單使用示例

先建立專案目錄結構(如下圖)

 1、IdentityServer 認證服務實現

  a) 建立一個空的WebApi專案-cz.IdentityServer,並新增IdentityServer4專案引用:如下圖:

Install-Package IdentityServer4

  b) 要啟用IdentityServer服務,不僅要把 IdentityServer 註冊到容器中, 還需要配置一下內容:

    •  Authorization Server 保護了哪些 API (資源);
    • 哪些客戶端 Client(應用) 可以使用這個 Authorization Server;

    • 指定可以使用 Authorization Server 授權的 Users(使用者)

    建立檔案 InMemoryConfig.cs,用於設定以上相關內容:    

認證授權:IdentityServer4
using IdentityServer4;
using IdentityServer4.Models;
using IdentityServer4.Test;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace cz.IdentityServer
{
    public class InMemoryConfig
    {
        public static IEnumerable<IdentityResource> GetIdentityResourceResources()
        {
            return new List<IdentityResource>
            {
                //必須要新增,否則報無效的scope錯誤
                new IdentityResources.OpenId(),
                new IdentityResources.Profile()
            };
        }

        /// <summary>
        /// api資源列表
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<ApiResource> GetApiResources()
        {
            //可訪問的API資源(資源名,資源描述)
            return new List<ApiResource>
            {
                new ApiResource("goods", "Goods Service"),
                new ApiResource("order", "Order Service")
            };
        }

        /// <summary>
        /// 客戶端列表
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<Client> GetClients()
        {
            return new List<Client>
            {
                new Client
                {
                    ClientId = "clientGoods", //訪問客戶端Id,必須唯一
                    //使用客戶端授權模式,客戶端只需要clientid和secrets就可以訪問對應的api資源。
                    AllowedGrantTypes = GrantTypes.ClientCredentials,
                    ClientSecrets =
                    {
                        new Secret("secret".Sha256())
                    },
                    AllowedScopes = {
                        "goods",
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile
                    },
                },
                new  Client
                {
                    ClientId = "clientOrder",
                    ClientSecrets = new [] { new Secret("secret".Sha256()) },
                    //這裡使用的是通過使用者名稱密碼和ClientCredentials來換取token的方式. ClientCredentials允許Client只使用ClientSecrets來獲取token. 這比較適合那種沒有使用者參與的api動作
                    AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
                    AllowedScopes = {
                        "order","goods",
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile
                    }
                }
            };
        }

        /// <summary>
        /// 指定可以使用 Authorization Server 授權的 Users(使用者)
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<TestUser> Users()
        {
            return new[]
            {
                    new TestUser
                    {
                        SubjectId = "1",
                        Username = "cba",
                        Password = "cba"
                    },
                    new TestUser
                    {
                        SubjectId = "2",
                        Username = "chaney",
                        Password = "123"
                    }
            };
        }
    }
}
View Code

    GetApiResources:這裡指定了name和display name, 以後api使用authorization server的時候, 這個name一定要一致

    GetClients: 認證客戶端列表

    Users: 這裡的記憶體使用者的型別是TestUser, 只適合學習和測試使用, 實際生產環境中還是需要使用資料庫來儲存使用者資訊的, 例如接下來會使用asp.net core identity. TestUser的SubjectId是唯一標識.

   在Startup.cs中啟用IdentityServer服務

       修改StartUp.cs中的ConfigureServices方法

public void ConfigureServices(IServiceCollection services)
{
            services.AddControllersWithViews();
            services.AddIdentityServer()
              .AddDeveloperSigningCredential()
              .AddInMemoryApiResources(InMemoryConfig.GetApiResources())
              .AddTestUsers(InMemoryConfig.Users().ToList())
              .AddInMemoryIdentityResources(InMemoryConfig.GetIdentityResourceResources())
              .AddInMemoryClients(InMemoryConfig.GetClients());
}

    修改StartUp.cs中的Configure方法    

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseRouting();
       //啟用IdentityServer
            app.UseIdentityServer();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }

   執行此專案,開啟瀏覽器訪問http://localhost:5600/.well-known/openid-configuration你將會看到IdentityServer的各種後設資料資訊。

    

  c) 引入QuickStartUI介面

   IdentityServer提供了一套UI以使我們能快速的開發具有基本功能的認證/授權介面,下載地址:QuickStartUI

   下載後,把QuickStartUI中:wwwroot、Quickstart、Views拷貝到專案中,如下結構:、

   

   修改Statup.cs內容如下:

認證授權:IdentityServer4
public class Startup
    {
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();

            services.AddIdentityServer()
              .AddDeveloperSigningCredential()
              .AddTestUsers(InMemoryConfig.Users().ToList())
              .AddInMemoryApiResources(InMemoryConfig.GetApiResources())
              .AddInMemoryClients(InMemoryConfig.GetClients());
        }

        // 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.UseRouting();

            app.UseStaticFiles();

            app.UseIdentityServer();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
View Code

   執行如下效果:

   

 2、IdentityServer 整合Api服務

   a)新增web api專案cz.Api.Order,並新增nuget中安裝IdentityServer4.AccessTokenValidation ,如下圖:  

命令:Install-Package IdentityServer4.AccessTokenValidation 

    

 

   b) 修改StartUp.cs檔案    

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)
        {
            services.AddControllers();

            //IdentityServer
            services.AddMvcCore()
                    .AddAuthorization();

            //配置IdentityServer
            services.AddAuthentication("Bearer")
                        .AddIdentityServerAuthentication(options =>
                        {
                            options.RequireHttpsMetadata = false; //是否需要https
                            options.Authority = $"http://localhost:5600";  //IdentityServer授權路徑
                            options.ApiName = "order";  //需要授權的服務名稱
                        });
        }

        // 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.UseHttpsRedirection();

            app.UseRouting();
       //啟用Authentication中介軟體
            app.UseAuthentication();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }

    c) 對cz.Api.Order專案中WebApi新增[Authorize]特性    

[Route("identity")]
[ApiController]
[Authorize]
 public class IdentityController : ControllerBase
{
}

   d) 此時呼叫該服務時提示401,如下圖:

    

 

   e) 可以通過client資訊獲取token,然後通過Header傳遞token 呼叫weapi

  

 

    

三、總結

  通過上面的例子,很簡單就實現了WepApi的認證授權效果;主要步驟如下:

  • 前往IdentityServer服務中,設定需要請求的ApiResource,Client,User內容
  • WebApi服務設定IdentityServerAuthentication地址(對接認證服務)
  • 呼叫WebApi時,先在IdentityServer中根據設定的方式獲取access token,再帶著access token請求介面才能正常訪問

四、後續

  IdentityServer包含的內容有很多,準備從多個內容來學習記錄使用IdentityServer的功能:SSO(單點登入)、各種授權模式使用、三方賬號登入……

  最後來實現一個自己的統一身份認證服務

相關文章