網站首頁 編程語言 正文
背景
先上個圖,看一下效果:
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 = '{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執行完畢之后執行
/// </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 = 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.cnblogs.com/BFMC/p/16250371.html
相關推薦
- 2023-07-03 python?遍歷可迭代對象的實現方法_python
- 2022-08-06 詳解Android中motion_toast的使用_Android
- 2022-12-23 Android入門之彈出式對話框的實現_Android
- 2022-04-05 Python利用prettytable庫輸出好看的表格_python
- 2022-07-26 更新kali源并,安裝dirseach
- 2022-07-08 一文詳解C++中運算符的使用_C 語言
- 2022-07-15 Python中列表索引?A[?:?2?]與A[?:?,?2]的區別說明_python
- 2024-01-31 在 Nginx 配置中,root 和 alias 指令的區別是什么
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支