aspnetcore 應用 接入Keycloak快速上手指南

張善友發表於2021-05-10

登入及身份認證是現代web應用最基本的功能之一,對於企業內部的系統,多個系統往往希望有一套SSO服務對企業使用者的登入及身份認證進行統一的管理,提升使用者同時使用多個系統的體驗,Keycloak正是為此種場景而生。本文將簡明的介紹Keycloak的安裝、使用,並給出aspnetcore 應用如何快速接入Keycloak的示例。

Keycloak是什麼

Keycloak是一種面向現代應用和服務的開源IAM(身份識別與訪問管理)解決方案

Keycloak提供了單點登入(SSO)功能,支援OpenID ConnectOAuth 2.0SAML 2.0標準協議,擁有簡單易用的管理控制檯,並提供對LDAP、Active Directory以及Github、Google等社交賬號登入的支援,做到了非常簡單的開箱即用。

官網: https://www.keycloak.org/

Keycloak常用核心概念介紹

首先通過官方的一張圖來了解下整體的核心概念

image

這裡先只介紹4個最常用的核心概念:

  1. Users: 使用者,使用並需要登入系統的物件

  2. Roles: 角色,用來對使用者的許可權進行管理

  3. Clients: 客戶端,需要接入Keycloak並被Keycloak保護的應用和服務

  4. Realms: 領域,領域管理著一批使用者、證書、角色、組等,一個使用者只能屬於並且能登陸到一個域,域之間是互相獨立隔離的, 一個域只能管理它下面所屬的使用者

Keycloak服務安裝及配置

安裝Keycloak

Keycloak安裝有多種方式,這裡使用Docker進行快速安裝

登入後複製

docker run -d --name keycloak \
    -p 8080:8080 \
    -e KEYCLOAK_USER=admin \
    -e KEYCLOAK_PASSWORD=admin \
    jboss/keycloak:13.0.0

訪問http://localhost:8080並點選Administration Console進行登入

 

image

 

建立Realm

建立一個新的realm: demo,後續所有的客戶端、使用者、角色等都在此realm中建立

image

image

image

建立客戶端
建立前端應用客戶端

建立一個新的客戶端:KeycloakAuthaspnet,Access Type選擇confidential

image

關於客戶端的訪問型別(Access Type)

上面建立的客戶端的訪問型別分別是confidential,那麼為什麼分別選擇這種型別,實際不同的訪問型別有什麼區別呢?

事實上,Keycloak目前的訪問型別共有3種:

  • confidential:適用於服務端應用,且需要瀏覽器登入以及需要通過金鑰獲取access token的場景。典型的使用場景就是服務端渲染的web系統。
  • public:適用於客戶端應用,且需要瀏覽器登入的場景。典型的使用場景就是前端web系統,包括採用vue、react實現的前端專案等。
  • bearer-only:適用於服務端應用,不需要瀏覽器登入,只允許使用bearer token請求的場景。典型的使用場景就是restful api。

 

Access Type 裡面選 Confidential,然後才有 Client Secret ,儲存之後,會出現Credentials的Tab,記錄下這裡的secret,後面要用到

image

 

建立使用者和角色
建立角色

建立2個角色:admin、user

image

還可以建立全域性的角色

image

 

建立使用者

建立1個使用者:geffzhang

image

 

繫結使用者和角色
給geffzhang 使用者分配角色admin和user

image

 

aspnetcore 應用整合Keycloak簡明指南

 

新增 Microsoft.AspNetCore.Authentication.OpenIdConnect  和  Microsoft.AspNetCore.Identity 包

 

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
     <TargetFramework>net5.0</TargetFramework>
     <UserSecretsId>afab524d-850e-499a-bc13-98f61ca0eb3b</UserSecretsId>
     <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
   </PropertyGroup>

  <ItemGroup>
     <PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="5.0.5" />
     <PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" />
     <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.10.8" />
     <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="5.0.2" />
   </ItemGroup>

</Project>

 

Appsettings.json

 

image

 

image

 

// This method gets called by the runtime. Use this method to add services to the container.
     public void ConfigureServices(IServiceCollection services)
     {
         services.AddControllersWithViews();
        
         services.AddAuthentication(options =>
         {
             //Sets cookie authentication scheme
             options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
             options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
             options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
         })

        .AddCookie(cookie =>
         {
             //Sets the cookie name and maxage, so the cookie is invalidated.
             cookie.Cookie.Name = "keycloak.cookie";
             cookie.Cookie.MaxAge = TimeSpan.FromMinutes(60);
             cookie.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
             cookie.SlidingExpiration = true;
         })
         .AddOpenIdConnect(options =>
         {
             /*
              * ASP.NET core uses the http://*:5000 and https://*:5001 ports for default communication with the OIDC middleware
              * The app requires load balancing services to work with :80 or :443
              * These needs to be added to the keycloak client, in order for the redirect to work.
              * If you however intend to use the app by itself then,
              * Change the ports in launchsettings.json, but beware to also change the options.CallbackPath and options.SignedOutCallbackPath!
              * Use LB services whenever possible, to reduce the config hazzle :)
             */

            //Use default signin scheme
             options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
             //Keycloak server
             options.Authority = Configuration.GetSection("Keycloak")["ServerRealm"];
             //Keycloak client ID
             options.ClientId = Configuration.GetSection("Keycloak")["ClientId"];
             //Keycloak client secret
             options.ClientSecret = Configuration.GetSection("Keycloak")["ClientSecret"];
             //Keycloak .wellknown config origin to fetch config
             options.MetadataAddress = Configuration.GetSection("Keycloak")["Metadata"];
             //Require keycloak to use SSL
             options.RequireHttpsMetadata = false;
             options.GetClaimsFromUserInfoEndpoint = true;
             options.Scope.Add("openid");
             options.Scope.Add("profile");
             //Save the token
             options.SaveTokens = true;
             //Token response type, will sometimes need to be changed to IdToken, depending on config.
             options.ResponseType = OpenIdConnectResponseType.Code;
             //SameSite is needed for Chrome/Firefox, as they will give http error 500 back, if not set to unspecified.
             options.NonceCookie.SameSite = SameSiteMode.Unspecified;
             options.CorrelationCookie.SameSite = SameSiteMode.Unspecified;
            
             options.TokenValidationParameters = new TokenValidationParameters
             {
                 NameClaimType = "name",
                 RoleClaimType = ClaimTypes.Role,
                 ValidateIssuer = true
             };


         });

        /*
          * For roles, that are defined in the keycloak, you need to use ClaimTypes.Role
          * You also need to configure keycloak, to set the correct name on each token.
          * Keycloak Admin Console -> Client Scopes -> roles -> mappers -> create
          * Name: "role client mapper" or whatever you prefer
          * Mapper Type: "User Client Role"
          * Multivalued: True
          * Token Claim Name: role
          * Add to access token: True
          */

       
         /*
          * Policy based authentication
          */

        services.AddAuthorization(options =>
         {
             //Create policy with more than one claim
             options.AddPolicy("users", policy =>
             policy.RequireAssertion(context =>
             context.User.HasClaim(c =>
                     (c.Value == "user") || (c.Value == "admin"))));
             //Create policy with only one claim
             options.AddPolicy("admins", policy =>
                 policy.RequireClaim(ClaimTypes.Role, "admin"));
             //Create a policy with a claim that doesn't exist or you are unauthorized to
             options.AddPolicy("noaccess", policy =>
                 policy.RequireClaim(ClaimTypes.Role, "noaccess"));
         });


         /*
          * Non policy based authentication
          * Uncomment below and comment the policy section
          */
       
         //services.AddAuthorization();

    }

經過上述的配置,通過oidc 很容易就接入到了Keycloak。具體程式碼請參見:https://github.com/NanoFabricFX/AspNetCore-keycloak/tree/dotnet5

 

執行效果,第一次訪問專案會跳轉Keycloak登入頁

 

image

使用者登陸geffzhang

 

image

總結

Keycloak部署及接入簡單,輕量的同時功能又不失強大,非常適合企業內部的SSO方案。在Identity Server4 收費的背景之下,微軟計劃在.NET 6裡面繼續整合,已經被社群罵的狗血噴頭https://devblogs.microsoft.com/aspnet/asp-net-core-6-and-authentication-servers/ 

 

相關文章:

相關文章