日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

浅析C#中单点登录的原理和使用

發布時間:2023/12/4 C# 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 浅析C#中单点登录的原理和使用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

是單點登錄?
我想肯定有一部分人“望文生義”的認為單點登錄就是一個用戶只能在一處登錄,其實這是錯誤的理解(我記得我第一次也是這么理解的)。
單點登錄指的是多個子系統只需要登錄一個,其他系統不需要登錄了(一個瀏覽器內)。一個子系統退出,其他子系統也全部是退出狀態。
如果你還是不明白,我們舉個實際的例子把。比如博客園首頁:https://www.cnblogs.com,和博客園的找找看http://zzk.cnblogs.com。這就是兩個系統(不同的域名)。如果你登錄其中一個,另一個也是登錄狀態。如果你退出一個,另一個也是退出狀態了。
那么這是怎么實現的呢?這就是我們今天要分析的問題了。

單點登錄(SSO)原理

  • 首先我們需要一個認證中心(Service),和兩個子系統(Client)。

  • 當瀏覽器第一次訪問Client1時,處于未登錄狀態 -> 302到認證中心(Service) -> 在Service的登錄頁面登錄(寫入Cookie記錄登錄信息) -> 302到Client1(寫入Cookie記錄登錄信息)

  • 第二次訪問Client1 -> 讀取Client1中Cookie登錄信息 -> Client1為登錄狀態

  • 第一次訪問Client2 -> 讀取Client2中Cookie中的登錄信息 -> Client2為未登錄狀態 -> 302到在Service(讀取Service中的Cookie為登錄狀態) -> 302到Client2(寫入Cookie記錄登錄信息)

我們發現在訪問Client2的時候,中間時間經過了幾次302重定向,并沒有輸入用戶名密碼去登錄。用戶完全感覺不到,直接就是登錄狀態了。

圖解:

手擼一個SSO

環境:.NET Framework 4.5.2
Service:

/// <summary>
/// 登錄
/// </summary>
/// <param name="name"></param>
/// <param name="passWord"></param>
/// <param name="backUrl"></param>
/
<returns></returns>
[HttpPost]
public string Login(string name, string passWord, string backUrl){
? ?if (true)//TODO:驗證用戶名密碼登錄{ ? ? ? ?//用Session標識會話是登錄狀態Session["user"] = "XX已經登錄"; ? ? ? ?//在認證中心 保存客戶端Client的登錄認證碼TokenIds.Add(Session.SessionID, Guid.NewGuid());} ? ?else//驗證失敗重新登錄{ ? ? ? ?return "/Home/Login";} ? ?return backUrl + "?tokenId=" + TokenIds[Session.SessionID];//生成一個tokenId 發放到客戶端}

Client:

public static List<string> Tokens = new List<string>();

public async Task<ActionResult> Index(){ ?
?var tokenId = Request.QueryString["tokenId"]; ? ?//如果tokenId不為空,則是由Service302過來的。if (tokenId != null){ ? ? ? ?
? ? ? ? using (HttpClient http = new HttpClient()){ ? ? ? ? ? ?//驗證Tokend是否有效var isValid = await http.GetStringAsync("http://localhost:8018/Home/TokenIdIsValid?tokenId=" + tokenId); ? ? ? ? ?
? ? ? ? ??if (bool.Parse(isValid.ToString())){ ? ? ? ?
? ? ? ? ??? ? ? ?if (!Tokens.Contains(tokenId)){ ? ? ? ? ? ? ? ? ? ?//記錄登錄過的Client (主要是為了可以統一登出)Tokens.Add(tokenId);
? ? ? ? ??? ? ? ?}Session["token"] = tokenId;}}} ? ?//判斷是否是登錄狀態if (Session["token"] == null || !Tokens.Contains(Session["token"].ToString())){ ? ? ? ?return Redirect("http://localhost:8018/Home/Verification?backUrl=http://localhost:26756/Home");} ?
? ? else{ ? ? ? ?if (Session["token"] != null)Session["token"] = null;} ? ?return View(); }

效果圖:

當然,這只是用較少的代碼擼了一個較簡單的SSO。僅用來理解,勿用于實際應用。

IdentityServer4實現SSO

環境:.NET Core 2.0
上面我們手擼了一個SSO,接下來我們看看.NET里的IdentityServer4怎么來使用SSO。
首先建一個IdentityServer4_SSO_Service(MVC項目),再建兩個IdentityServer4_SSO_Client(MVC項目)
在Service項目中用nuget導入IdentityServer4 2.0.2、IdentityServer4.AspNetIdentity 2.0.0、IdentityServer4.EntityFramework 2.0.0
在Client項目中用nuget導入IdentityModel 2.14.0
然后分別設置Service和Client項目啟動端口為 5001(Service)、5002(Client1)、5003(Client2)

在Service中新建一個類Config:

public class Config{ ? ? ? ?public static IEnumerable<IdentityResource> GetIdentityResources() ? ? ? ?{ ? ? ? ? ? ?return new List<IdentityResource>{ ? ? ? ? ? ? ? ?new IdentityResources.OpenId(), ? ? ? ? ? ? ? ?new IdentityResources.Profile(),};} ? ?public static IEnumerable<ApiResource> GetApiResources() ? ?{ ? ? ? ?return new List<ApiResource>{ ? ? ? ? ? ?new ApiResource("api1", "My API")};} ? ?// 可以訪問的客戶端public static IEnumerable<Client> GetClients() ? ? ? ?{ ? ? ? ? ? return new List<Client>{ ? ? ? ? ? ? ? // OpenID Connect hybrid flow and client credentials client (MVC)//Client1new Client{ClientId = "mvc1",ClientName = "MVC Client1",AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,RequireConsent = true,ClientSecrets ={ ? ? ? ? ? ? ? ? ? ? ? ?new Secret("secret".Sha256())},RedirectUris = { "http://localhost:5002/signin-oidc" }, //注意端口5002 是我們修改的Client的端口PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },AllowedScopes ={IdentityServerConstants.StandardScopes.OpenId,IdentityServerConstants.StandardScopes.Profile, ? ? ? ? ? ? ? ? ? ? ? ?"api1"},AllowOfflineAccess = true}, ? ? ? ? ? ? ? ? //Client2new Client{ClientId = "mvc2",ClientName = "MVC Client2",AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,RequireConsent = true,ClientSecrets ={ ? ? ? ? ? ? ? ? ? ? ? ?new Secret("secret".Sha256())},RedirectUris = { "http://localhost:5003/signin-oidc" },PostLogoutRedirectUris = { "http://localhost:5003/signout-callback-oidc" },AllowedScopes ={IdentityServerConstants.StandardScopes.OpenId,IdentityServerConstants.StandardScopes.Profile, ? ? ? ? ? ? ? ? ? ? ? ?"api1"},AllowOfflineAccess = true}};} }

新增一個ApplicationDbContext類繼承于IdentityDbContext:

public class ApplicationDbContext : IdentityDbContext<IdentityUser> { ? ?public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options): base(options){} protected override void OnModelCreating(ModelBuilder builder){ ? ? ? ?base.OnModelCreating(builder);} }

在文件appsettings.json中配置數據庫連接字符串:

"ConnectionStrings": { ? ?"DefaultConnection": "Server=(local);Database=IdentityServer4_Demo;Trusted_Connection=True;MultipleActiveResultSets=true"}

在文件Startup.cs的ConfigureServices方法中增加:

public void ConfigureServices(IServiceCollection services) {services.AddDbContext<ApplicationDbContext>(options =>options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); //數據庫連接字符串services.AddIdentity<IdentityUser, IdentityRole>().AddEntityFrameworkStores<ApplicationDbContext>().AddDefaultTokenProviders();services.AddMvc();string connectionString = Configuration.GetConnectionString("DefaultConnection"); ?
?var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;services.AddIdentityServer().AddDeveloperSigningCredential().AddAspNetIdentity<IdentityUser>() .AddConfigurationStore(options =>{options.ConfigureDbContext = builder =>builder.UseSqlServer(connectionString,sql => sql.MigrationsAssembly(migrationsAssembly));}).AddOperationalStore(options =>{options.ConfigureDbContext = builder =>builder.UseSqlServer(connectionString,sql => sql.MigrationsAssembly(migrationsAssembly));options.EnableTokenCleanup = true;options.TokenCleanupInterval = 30;}); }

并在Startup.cs文件里新增一個方法InitializeDatabase(初始化數據庫):

/// <summary>/// 初始數據庫/// </summary>/// <param name="app"></param>private void InitializeDatabase(IApplicationBuilder app){ ? ?using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope()){serviceScope.ServiceProvider.GetRequiredService<ApplicationDbContext>().Database.Migrate();//執行數據庫遷移serviceScope.ServiceProvider.GetRequiredService<PersistedGrantDbContext>().Database.Migrate(); ? ? ?
? ?var context = serviceScope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();context.Database.Migrate(); ? ? ?
? ? ??if (!context.Clients.Any()){ ? ? ? ? ? ?foreach (var client in Config.GetClients())//循環添加 我們直接添加的 5002、5003 客戶端{context.Clients.Add(client.ToEntity());}context.SaveChanges();} if (!context.IdentityResources.Any())? ?
? ? ? ? { ? ? ? ? ? ? ? ?
? ?foreach (var resource in Config.GetIdentityResources()){context.IdentityResources.Add(resource.ToEntity());}context.SaveChanges();} if (!context.ApiResources.Any()){ ? ? ? ? ? ? ? ? ? ?foreach (var resource in Config.GetApiResources()){context.ApiResources.Add(resource.ToEntity());}context.SaveChanges();}} }

修改Configure方法:

public void Configure(IApplicationBuilder app, IHostingEnvironment env){ ? ? //初始化數據InitializeDatabase(app); ? ?
? ? if (env.IsDevelopment()){app.UseDeveloperExceptionPage();app.UseBrowserLink();app.UseDatabaseErrorPage();} ? ?
? ? else{app.UseExceptionHandler("/Home/Error");}app.UseStaticFiles();app.UseIdentityServer();app.UseMvc(routes =>{routes.MapRoute(name: "default", ? ? ? ? ?
? ? ? template: "{controller=Home}/{action=Index}/{id?}");});}

然后新建一個AccountController控制器,分別實現注冊、登錄、登出等。
新建一個ConsentController控制器用于Client回調。
然后在Client的Startup.cs類里修改ConfigureServices方法:

public void ConfigureServices(IServiceCollection services){services.AddMvc();JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();services.AddAuthentication(options =>{options.DefaultScheme = "Cookies";options.DefaultChallengeScheme = "oidc";}).AddCookie("Cookies").AddOpenIdConnect("oidc", options =>{options.SignInScheme = "Cookies";options.Authority = "http://localhost:5001";options.RequireHttpsMetadata = false;options.ClientId = "mvc2";options.ClientSecret = "secret";options.ResponseType = "code id_token";options.SaveTokens = true;options.GetClaimsFromUserInfoEndpoint = true;options.Scope.Add("api1");options.Scope.Add("offline_access");}); }


對于Client的身份認證就簡單了:

[Authorize]//身份認證public IActionResult Index(){ ? ?return View(); }/// <summary>/// 登出/// </summary>/// <returns></returns>public async Task<IActionResult> Logout(){ ? ?await HttpContext.SignOutAsync("Cookies"); ? ?await HttpContext.SignOutAsync("oidc"); ? ?return View("Index"); }

效果圖:

?

?

源碼地址(demo可配置數據庫連接后直接運行)

  • https://github.com/zhaopeiym/BlogDemoCode/tree/master/sso(%E5%8D%95%E7%82%B9%E7%99%BB%E5%BD%95)

推薦閱讀

  • http://www.cnblogs.com/ywlaker/p/6113927.html

  • https://identityserver4.readthedocs.io/en/release

相關文章:

  • IdentityServer4(OAuth2.0服務)折騰筆記

  • IdentityServer4 實現 OpenID Connect 和 OAuth 2.0

  • IdentityServer4 使用OpenID Connect添加用戶身份驗證

  • IdentityServer4 ASP.NET Core的OpenID Connect OAuth 2.0框架學習保護API

  • IdentityServer4 指定角色授權(Authorize(Roles="admin"))

  • IdentityServer4 SigningCredential(RSA 證書加密)

  • IdentityServer4 實現自定義 GrantType 授權模式

  • IdentityServer4 配置負載均衡

  • 學習Identity Server 4的預備知識

原文地址:http://www.cnblogs.com/zhaopei/p/SSO.html


.NET社區新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關注


總結

以上是生活随笔為你收集整理的浅析C#中单点登录的原理和使用的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。