基于ASP.NET實現(xiàn)單點登錄(SSO)的示例代碼
背景
先上個圖,看一下效果:
SSO英文全稱Single Sign On(單點登錄)。SSO是在多個應(yīng)用系統(tǒng)中,用戶只需要登錄一次就可以訪問所有相互信任的應(yīng)用系統(tǒng)。它包括可以將這次主要的登錄映射到其他應(yīng)用中用于同一個用戶的登錄的機制。
它是目前比較流行的企業(yè)業(yè)務(wù)整合的解決方案之一。(本段內(nèi)容來自百度百科) 話不多說,開擼!
邏輯分析
Client1:用戶A在電腦1上登錄管理員賬號
Service:驗證用戶A登陸成功生成Admin賬號的Token令牌,分別存儲電腦1的cookie中和服務(wù)器的全局變量中(可以是session,緩存,全局變量,數(shù)據(jù)庫)
Client2:用戶B在電腦2上登錄管理員賬號
Service:驗證用戶B登陸成功重新生成Admin賬號的Token令牌,分別存儲電腦2的cookie中和服務(wù)器的全局變量中(可以是session,緩存,全局變量,數(shù)據(jù)庫)
Client1:觸發(fā)驗證:
1,判斷服務(wù)器全局變量是否過期,提示:身份信息過期,請重新登錄。
2,判斷客戶端的cookie是否過期,提示:長時間未登錄,已下線。
3,判斷電腦1上的cookie與服務(wù)器全局變量相比是否一致。提示:此用戶已在別處登陸!你被強制下線!
代碼實現(xiàn)
Service
1,創(chuàng)建一個服務(wù)器校驗登錄類,代碼如下
using Coldairarrow.Business; using Coldairarrow.Util; using System; using System.Text; using System.Web.Mvc; namespace Coldairarrow.Web { /// <summary> /// 校驗登錄 /// </summary> public class CheckLoginAttribute : FilterAttribute, IActionFilter { public IOperator _operator { get; set; } public ILogger _logger { get; set; } /// <summary> /// Action執(zhí)行之前執(zhí)行 /// </summary> /// <param name="filterContext">過濾器上下文</param> public void OnActionExecuting(ActionExecutingContext filterContext) { var request = filterContext.RequestContext.HttpContext.Request; try { //若為本地測試,則不需要登錄 if (GlobalSwitch.RunModel == RunModel.LocalTest) { return; } //判斷是否需要登錄 bool needLogin = !filterContext.ContainsAttribute<IgnoreLoginAttribute>(); //獲取session里面的用戶id var uid = SessionHelper.Session["UserId"]?.ToString(); if (needLogin) { if (string.IsNullOrEmpty(uid)) { //轉(zhuǎn)到登錄 RedirectToLogin(); } else { var Cguid = filterContext.HttpContext.Request.Cookies["CToken"].Value?.ToString(); var Sguid = CacheHelper.Cache.GetCache(uid + "_SToken")?.ToString(); //判斷是否過期 if (string.IsNullOrEmpty(Cguid) || string.IsNullOrEmpty(Sguid)) { // 過期 轉(zhuǎn)到登錄 ReturnLogin("身份信息以失效,請重新登陸!"); SessionHelper.Session["UserId"] = ""; } else { //判斷用戶是否重復(fù)登陸 if (Sguid != Cguid) { // 過期 轉(zhuǎn)到登錄 ReturnLogin("此用戶已在別處登陸!你被強制下線!"); SessionHelper.Session["UserId"] = ""; //message = "已登陸"; } } } } //if (needLogin && !_operator.Logged()) //{ //轉(zhuǎn)到登錄 // RedirectToLogin(); //} //else //{ // string Id = _operator.UserId; // _operator.Login(Id); // return; //} } catch (Exception ex) { _logger.Error(ex); RedirectToLogin(); } void RedirectToLogin() { if (request.IsAjaxRequest()) { filterContext.Result = new ContentResult { Content = new AjaxResult { Success = false, ErrorCode = 1, Msg = "未登錄" }.ToJson(), ContentEncoding = Encoding.UTF8, ContentType = "application/json" }; } else { UrlHelper urlHelper = new UrlHelper(filterContext.RequestContext); string loginUrl = urlHelper.Content("~/Home/Login"); string script = $@" <html> <script> top.location.href = '{loginUrl}'; </script> </html> "; filterContext.Result = new ContentResult { Content = script, ContentType = "text/html", ContentEncoding = Encoding.UTF8 }; } } void ReturnLogin(string msg) { UrlHelper urlHelper = new UrlHelper(filterContext.RequestContext); string loginUrl = urlHelper.Content("~/Home/Login"); string script = $@" <html> <script> alert('{msg}'); top.location.href = '{loginUrl}'; </script> </html> "; filterContext.Result = new ContentResult { Content = script, ContentType = "text/html", ContentEncoding = Encoding.UTF8 }; } } /// <summary> /// Action執(zhí)行完畢之后執(zhí)行 /// </summary> /// <param name="filterContext"></param> public void OnActionExecuted(ActionExecutedContext filterContext) { } } }
2,創(chuàng)建一個mvc基控制器繼承Controller并且引用特性【CheckLogin】
3,業(yè)務(wù)控制器繼承BaseMvcController,并編寫登錄代碼。登陸成功后調(diào)用login方法,代碼如下:
/// <summary> /// 登錄 /// </summary> /// <param name="userId">用戶邏輯主鍵Id</param> public void Login(string userId) { //保存登陸成功的令牌 string Guid_str = ""; //分配一個唯一標識符 Guid_str = GuidHelper.GuidTo16String(); HttpContext.Current.Response.Cookies["CToken"].Value = Guid_str; //給系統(tǒng)變量存儲一個值,Uid代表哪個用戶,GUID則是唯一標識符 CacheHelper.Cache.SetCache(userId + "_SToken", Guid_str, new TimeSpan(0, 0, 30, 0, 0), ExpireType.Absolute); SessionHelper.Session["UserId"] = userId; }
4,這個時候基本就結(jié)束了,還需要增加一個忽略驗證的類,這個特性加在登錄頁面。意思是登錄頁面不需要觸發(fā)驗證;
5,服務(wù)器驗證的核心代碼有點不優(yōu)雅,不過實現(xiàn)邏輯了。有問題可以評論區(qū)溝通一下。本人用的是將token分別存儲在服務(wù)器緩存+客戶端cookie完成 ,大家服務(wù)器上可以用session,緩存,全局變量,數(shù)據(jù)庫等任意方式實現(xiàn);
總結(jié)
當用戶沒有重復(fù)登陸時,系統(tǒng)分配一個guid給用戶,并記錄用戶id和對應(yīng)的guid,這個用戶在線時系統(tǒng)變量存儲的用戶id以及對應(yīng)的guid值是不會變的,這時候有另外一個人用相同的賬號登陸時,會改變系統(tǒng)變量中用戶id對應(yīng)的guid。
這時候服務(wù)器就判斷出系統(tǒng)變量存儲的guid與用戶cookie存儲的guid不同時,就會強制用戶下線。
這個可以升級為指定N臺設(shè)備登錄,并且可以增加socket的方式通知其他電腦下線。由于業(yè)務(wù)不需要,就沒有增加即時通訊。
到此這篇關(guān)于基于ASP.NET實現(xiàn)單點登錄(SSO)的示例代碼的文章就介紹到這了,更多相關(guān)ASP.NET單點登錄內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
asp.net 關(guān)于字符串內(nèi)范圍截取的一點方法總結(jié)
前兩天有一位網(wǎng)友提出了一個字符串內(nèi)截取字符串的問題,除了用普通的字符串截取的方式外,我推薦的是用LINQ方式來截取。兩者實際上差別不是很大,都是采用字符串截取方式,但后者從寫法和觀察效果會比前者簡單實用得多。2010-02-02ASP.NET使用HttpWebRequest讀取遠程網(wǎng)頁源代碼
本文分享了一個使用HttpWebRequest讀取遠程網(wǎng)頁的案例,供大家參考學習。2016-03-03visual studio 2019使用net core3.0創(chuàng)建winform無法使用窗體設(shè)計器
這篇文章主要介紹了visual studio 2019使用net core3.0創(chuàng)建winform無法使用窗體設(shè)計器,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-03-03在.Net?Framework應(yīng)用中請求HTTP2站點的問題解析
隨著各大瀏覽器支持和蘋果的帶頭效應(yīng),HTTP2的應(yīng)用會越來越廣泛,但是規(guī)模龐大的.NET?Framework應(yīng)用卻也不能為了連接HTTP2就升級到NET?Core平臺。通過本文提供的方案,可以最小成本的實現(xiàn).NET?Framework應(yīng)用成功訪問HTTP2站點,感興趣的朋友跟隨小編一起看看吧2022-07-07asp.net core webapi項目配置全局路由的方法示例
這篇文章主要介紹了asp.net core webapi項目配置全局路由的方法示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-09-09