背景:
先上個圖,看一下效果:

SSO英文全稱Single Sign On(單點登錄),SSO是在多個應用系統中,用戶只需要登錄一次就可以訪問所有相互信任的應用系統,它包括可以將這次主要的登錄映射到其他應用中用于同一個用戶的登錄的機制,
它是目前比較流行的企業業務整合的解決方案之一,(本段內容來自百度百科) 話不多說,開擼!
邏輯分析:
Client1:用戶A在電腦1上登錄管理員賬號
Service:驗證用戶A登陸成功生成Admin賬號的Token令牌,分別存盤電腦1的cookie中和服務器的全域變數中(可以是session,快取,全域變數,資料庫)
Client2:用戶B在電腦2上登錄管理員賬號
Service:驗證用戶B登陸成功重新生成Admin賬號的Token令牌,分別存盤電腦2的cookie中和服務器的全域變數中(可以是session,快取,全域變數,資料庫)
Client1:觸發驗證:
1,判斷服務器全域變數是否過期,提示:身份資訊過期,請重新登錄,
2,判斷客戶端的cookie是否過期,提示:長時間未登錄,已下線,
3,判斷電腦1上的cookie與服務器全域變數相比是否一致,提示:此用戶已在別處登陸!你被強制下線!
代碼實作
Service:
1,創建一個服務器校驗登錄類,代碼如下
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執行之前執行
/// </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))
{
//轉到登錄
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))
{
// 過期 轉到登錄
ReturnLogin("身份資訊以失效,請重新登陸!");
SessionHelper.Session["UserId"] = "";
}
else
{
//判斷用戶是否重復登陸
if (Sguid != Cguid)
{
// 過期 轉到登錄
ReturnLogin("此用戶已在別處登陸!你被強制下線!");
SessionHelper.Session["UserId"] = "";
//message = "已登陸";
}
}
}
}
//if (needLogin && !_operator.Logged())
//{ //轉到登錄
// 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 = 'https://www.cnblogs.com/BFMC/archive/2022/05/10/{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 = 'https://www.cnblogs.com/BFMC/archive/2022/05/10/{loginUrl}';
</script>
</html>
";
filterContext.Result = new ContentResult { Content = script, ContentType = "text/html", ContentEncoding = Encoding.UTF8 };
}
}
/// <summary>
/// Action執行完畢之后執行
/// </summary>
/// <param name="filterContext"></param>
public void OnActionExecuted(ActionExecutedContext filterContext)
{
}
}
}
2,創建一個mvc基控制器繼承Controller并且參考特性【CheckLogin】

3,業務控制器繼承BaseMvcController,并撰寫登錄代碼,登陸成功后呼叫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 =https://www.cnblogs.com/BFMC/archive/2022/05/10/ Guid_str;
//給系統變數存盤一個值,Uid代表哪個用戶,GUID則是唯一識別符號
CacheHelper.Cache.SetCache(userId + "_SToken", Guid_str, new TimeSpan(0, 0, 30, 0, 0), ExpireType.Absolute);
SessionHelper.Session["UserId"] = userId;
}
4,這個時候基本就結束了,還需要增加一個忽略驗證的類,這個特性加在登錄頁面,意思是登錄頁面不需要觸發驗證;


5,服務器驗證的核心代碼有點不優雅,不過實作邏輯了,有問題可以評論區溝通一下,本人用的是將token分別存盤在服務器快取+客戶端cookie完成 ,大家服務器上可以用session,快取,全域變數,資料庫等任意方式實作;
總結:
當用戶沒有重復登陸時,系統分配一個guid給用戶,并記錄用戶id和對應的guid,這個用戶在線時系統變數存盤的用戶id以及對應的guid值是不會變的,這時候有另外一個人用相同的賬號登陸時,會改變系統變數中用戶id對應的guid,
這時候服務器就判斷出系統變數存盤的guid與用戶cookie存盤的guid不同時,就會強制用戶下線,
這個可以升級為指定N臺設備登錄,并且可以增加socket的方式通知其他電腦下線,由于業務不需要,就沒有增加即時通訊,感謝觀看,
從前慢,車馬慢, 一生只愛一個人,轉載請註明出處,本文鏈接:https://www.uj5u.com/net/472222.html
標籤:.NET技术
上一篇:.NET 中 GC 的模式與風格
