1. 程式人生 > 實用技巧 >使用IdentityServer4簡單的實現一個SSO登入

使用IdentityServer4簡單的實現一個SSO登入

一.簡介

本文使用 ASP.NET Core +IdentityServer4來實現SSO單點登入(登出沒有實現),我們其中使用Implicit模式和OIDC協議來實現功能,這裡先記錄實現核心程式碼,然後再來記錄IdentityServer幫我們做了什麼(從請求的流程來訴說)。

二.預備工作

建立 ASP.NET Core 三個專案,分別是Client_IdentityServerClient2_IdentityServerIdentityServer_SSO

然後修改Window的host檔案,註冊三個站點,分別是a.cn b.net c.cn

然後設定三個專案的啟動地址,讓三個專案地址分別對應上面的三個站點,我這裡設定兩個客戶端分別是 www.a.cn 和 www.b.net ,服務端為 www.c.cn

舉例


依次按照這種方式來修改剩下的兩個專案啟動地址就可以了。

服務端配置

然後在服務端點開NuGet管理包搜尋IdentityServer4進行下載

新建一個Config類 ,用來配置IdentityServer4.

    public class config
    {
        public static IEnumerable<ApiResource> GetApiResource()
        {
            return new List<ApiResource>()
            {
                new ApiResource("api","this is api")
            };
        }
        
        public static IEnumerable<IdentityResource> GetIdentityResources()
        {
            return new List<IdentityResource>()
            {
                new IdentityResources.OpenId(),
                new IdentityResources.Profile(),
                new IdentityResources.Phone()
            };
        }

         public static List<TestUser> GetTestUsers()
        {
            return new List<TestUser>{
              new TestUser()
              {
                    SubjectId="123",
                    Username="Mr.wen",
                    Password="123465",
                    Claims=new Claim[]
                    {
                        new Claim(ClaimTypes.Role,"管理員")
                    }
              },
              new TestUser()
              {
                    SubjectId="456",
                    Username="123",
                    Password="123456",
                    Claims=new Claim[]
                    {
                        new Claim(ClaimTypes.Role,"閱覽者")
                    }
                    
              }
            };
        }
        
         public static IEnumerable<Client> GetClients()
        {
            return new List<Client>()
            {
                new Client()
                {
                    ClientId="mvc_imp",
                    ClientName="Mvc_Name",        
                    AllowedGrantTypes=GrantTypes.Implicit, 
                    設定是否要授權
                    //RequireConsent=false,
                    
                    //指定允許令牌或授權碼返回的地址(URL)
                    RedirectUris={ "http://www.b.net:5001/signin-oidc","http://www.a.cn:5002/signin-oidc" },
                    //指定允許登出後返回的地址(URL),這裡寫兩個客戶端
                    PostLogoutRedirectUris={ "http://www.b.net:5001/signout-callback-oidc","http://www.a.cn:5002/signout-callback-oidc" },
                    ClientSecrets={new Secret("secret".Sha256())},
                    AllowedScopes = new List<string>
                    {
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                    }, 
                }
             };
         }

接下來配置服務端的Startup.cs的Configure和ConfigureServices方法

public void ConfigureServices(IServiceCollection services)
  {
       services.AddIdentityServer(option=>
       {
           //可以通過此設定來指定登入路徑,預設的登陸路徑是/account/login
           option.UserInteraction.LoginUrl = "/account/login";
                
       })
       .AddDeveloperSigningCredential()
       .AddInMemoryApiResources(Config.GetApiResource())
       .AddInMemoryClients(Config.GetClients())
       .AddInMemoryIdentityResources(Config.GetIdentityResources())
       .AddTestUsers(Config.GetTestUsers());
         
         services.AddMvc();
  }
   
       
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
 {
      if (env.IsDevelopment())
      {
            app.UseBrowserLink();
            app.UseDeveloperExceptionPage();
      }
      else
      {
           app.UseExceptionHandler("/Home/Error");
      }
      
       app.UseIdentityServer();
        
       app.UseStaticFiles();
         
       app.UseMvc(routes =>
       {
               routes.MapRoute(
                  name: "default",
                  template: "{controller=Home}/{action=Index}/{id?}"
                );
       });
 }
       

客戶端配置

配置Startup.cs檔案

 public void ConfigureServices(IServiceCollection services)
 {  
            //DefaultChallengeScheme的名字要和下面AddOpenIdConnect方法第一個引數名字保持一致
            services.AddAuthentication(options =>
            {
                options.DefaultScheme = "Cookies";
                options.DefaultChallengeScheme = "oidc";
            })
           .AddCookie("Cookies")
           .AddOpenIdConnect("oidc", options =>
           {
               options.Authority = "http://www.c.cn:5000";
               options.RequireHttpsMetadata = false;
               
               //指定允許服務端返回的地址,預設是new PathString("/signin-oidc")
               //如果這裡地址進行了自定義,那麼服務端也要進行修改
               options.CallbackPath = new PathString("/signin-oidc");
               
               //指定使用者登出後,服務端可以呼叫客戶端登出的地址,預設是new PathString("signout-callback-oidc")
               options.SignedOutCallbackPath = new PathString("/signout-callback-oidc");     
                
               options.ClientId = "mvc_imp";
               options.ClientSecret = "secret";
           });
          services.AddMvc();
 }
        
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
      if (env.IsDevelopment())
      {
           app.UseBrowserLink();
           app.UseDeveloperExceptionPage();
      }
     else
     {
         app.UseExceptionHandler("/Home/Error");
     }
     app.UseAuthentication();
     
     app.UseStaticFiles();
    
     app.UseMvc(routes =>
     {
        routes.MapRoute(
           name: "default",
           template: "{controller=Home}/{action=Index}/{id?}"
        );
     });
}

到這裡,總的配置就配好了,接下來是主要實現程式碼

服務端

建立AccountController控制器,其中有login方法,在login方法裡寫我們登入的邏輯

       [HttpPost]
       public async Task<IActionResult> Login(LoginInputModel model)
       {
            //當登入提交給後臺的model為null,則返回錯誤資訊給前臺
            if (model == null)
            {
                //這裡我只是簡單處理了
                return View();
            }
            //這裡同理,當資訊不完整的時候,返回錯誤資訊給前臺
            if (string.IsNullOrEmpty(model.Username) || string.IsNullOrEmpty(model.Password))
            {
                //這裡只是簡單處理了
                return View();
            }
            
            //model.Username == "123" && model.Password == "123456"
            //if裡面的是驗證賬號密碼,可以用自定義的驗證,
            //我這裡使用的是TestUserStore的的驗證方法,
            if (_users.FindByUsername(model.Username)!=null&&_users.ValidateCredentials(model.Username,model.Password))
            {
                   var user = _users.FindByUsername(model.Username);
                //配置Cookie
                AuthenticationProperties properties = new AuthenticationProperties()
                {
                    IsPersistent = true,
                    ExpiresUtc = DateTimeOffset.UtcNow.Add(TimeSpan.FromMinutes(30))
                };
                
                //使用IdentityServer的SignInAsync來進行註冊Cookie
                await HttpContext.SignInAsync(user.SubjectId, model.Username);
                
                //使用IIdentityServerInteractionService的IsValidReturnUrl來驗證ReturnUrl是否有問題
                if (_interaction.IsValidReturnUrl(model.ReturnUrl))
                {
                    return Redirect(model.ReturnUrl);
                }
                return View();
             }
             return View();
        }

客戶端

客戶端的程式碼很簡單,即想要驗證的方法或控制器新增一個Authorize特性就可以了

       [Authorize]
        public IActionResult About()
        {
             ViewData["Message"] = "Your application description page.";
             return View();
        }

到這裡,客戶端和服務端的核心程式碼基本結束了,如果想要看程式的UI,或者想要看完整的後臺,可以檢視我的GitHub

https://github.com/MDZZ3/Identity_SSO

參考

https://www.cnblogs.com/RainingNight/p/oidc-authentication-in-asp-net-core.html