使用IdentityServer4簡單的實現一個SSO登入
一.簡介
本文使用 ASP.NET Core +IdentityServer4來實現SSO單點登入(登出沒有實現),我們其中使用Implicit模式和OIDC協議來實現功能,這裡先記錄實現核心程式碼,然後再來記錄IdentityServer幫我們做了什麼(從請求的流程來訴說)。
二.預備工作
建立 ASP.NET Core 三個專案,分別是Client_IdentityServer和Client2_IdentityServer和IdentityServer_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