基于ASP.NET實(shí)現(xiàn)單點(diǎn)登錄(SSO)的示例代碼
背景
先上個(gè)圖,看一下效果:

SSO英文全稱Single Sign On(單點(diǎn)登錄)。SSO是在多個(gè)應(yīng)用系統(tǒng)中,用戶只需要登錄一次就可以訪問所有相互信任的應(yīng)用系統(tǒng)。它包括可以將這次主要的登錄映射到其他應(yīng)用中用于同一個(gè)用戶的登錄的機(jī)制。
它是目前比較流行的企業(yè)業(yè)務(wù)整合的解決方案之一。(本段內(nèi)容來自百度百科) 話不多說,開擼!
邏輯分析
Client1:用戶A在電腦1上登錄管理員賬號(hào)
Service:驗(yàn)證用戶A登陸成功生成Admin賬號(hào)的Token令牌,分別存儲(chǔ)電腦1的cookie中和服務(wù)器的全局變量中(可以是session,緩存,全局變量,數(shù)據(jù)庫)
Client2:用戶B在電腦2上登錄管理員賬號(hào)
Service:驗(yàn)證用戶B登陸成功重新生成Admin賬號(hào)的Token令牌,分別存儲(chǔ)電腦2的cookie中和服務(wù)器的全局變量中(可以是session,緩存,全局變量,數(shù)據(jù)庫)
Client1:觸發(fā)驗(yàn)證:
1,判斷服務(wù)器全局變量是否過期,提示:身份信息過期,請(qǐng)重新登錄。
2,判斷客戶端的cookie是否過期,提示:長(zhǎng)時(shí)間未登錄,已下線。
3,判斷電腦1上的cookie與服務(wù)器全局變量相比是否一致。提示:此用戶已在別處登陸!你被強(qiáng)制下線!
代碼實(shí)現(xiàn)
Service
1,創(chuàng)建一個(gè)服務(wù)器校驗(yàn)登錄類,代碼如下
using Coldairarrow.Business;
using Coldairarrow.Util;
using System;
using System.Text;
using System.Web.Mvc;
namespace Coldairarrow.Web
{
/// <summary>
/// 校驗(yàn)登錄
/// </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
{
//若為本地測(cè)試,則不需要登錄
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("身份信息以失效,請(qǐng)重新登陸!");
SessionHelper.Session["UserId"] = "";
}
else
{
//判斷用戶是否重復(fù)登陸
if (Sguid != Cguid)
{
// 過期 轉(zhuǎn)到登錄
ReturnLogin("此用戶已在別處登陸!你被強(qiáng)制下線!");
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)建一個(gè)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 = "";
//分配一個(gè)唯一標(biāo)識(shí)符
Guid_str = GuidHelper.GuidTo16String();
HttpContext.Current.Response.Cookies["CToken"].Value = Guid_str;
//給系統(tǒng)變量存儲(chǔ)一個(gè)值,Uid代表哪個(gè)用戶,GUID則是唯一標(biāo)識(shí)符
CacheHelper.Cache.SetCache(userId + "_SToken", Guid_str, new TimeSpan(0, 0, 30, 0, 0), ExpireType.Absolute);
SessionHelper.Session["UserId"] = userId;
}4,這個(gè)時(shí)候基本就結(jié)束了,還需要增加一個(gè)忽略驗(yàn)證的類,這個(gè)特性加在登錄頁面。意思是登錄頁面不需要觸發(fā)驗(yàn)證;


5,服務(wù)器驗(yàn)證的核心代碼有點(diǎn)不優(yōu)雅,不過實(shí)現(xiàn)邏輯了。有問題可以評(píng)論區(qū)溝通一下。本人用的是將token分別存儲(chǔ)在服務(wù)器緩存+客戶端cookie完成 ,大家服務(wù)器上可以用session,緩存,全局變量,數(shù)據(jù)庫等任意方式實(shí)現(xiàn);
總結(jié)
當(dāng)用戶沒有重復(fù)登陸時(shí),系統(tǒng)分配一個(gè)guid給用戶,并記錄用戶id和對(duì)應(yīng)的guid,這個(gè)用戶在線時(shí)系統(tǒng)變量存儲(chǔ)的用戶id以及對(duì)應(yīng)的guid值是不會(huì)變的,這時(shí)候有另外一個(gè)人用相同的賬號(hào)登陸時(shí),會(huì)改變系統(tǒng)變量中用戶id對(duì)應(yīng)的guid。
這時(shí)候服務(wù)器就判斷出系統(tǒng)變量存儲(chǔ)的guid與用戶cookie存儲(chǔ)的guid不同時(shí),就會(huì)強(qiáng)制用戶下線。
這個(gè)可以升級(jí)為指定N臺(tái)設(shè)備登錄,并且可以增加socket的方式通知其他電腦下線。由于業(yè)務(wù)不需要,就沒有增加即時(shí)通訊。
到此這篇關(guān)于基于ASP.NET實(shí)現(xiàn)單點(diǎn)登錄(SSO)的示例代碼的文章就介紹到這了,更多相關(guān)ASP.NET單點(diǎn)登錄內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
asp.net 關(guān)于字符串內(nèi)范圍截取的一點(diǎn)方法總結(jié)
前兩天有一位網(wǎng)友提出了一個(gè)字符串內(nèi)截取字符串的問題,除了用普通的字符串截取的方式外,我推薦的是用LINQ方式來截取。兩者實(shí)際上差別不是很大,都是采用字符串截取方式,但后者從寫法和觀察效果會(huì)比前者簡(jiǎn)單實(shí)用得多。2010-02-02
asp.net4.0框架下驗(yàn)證機(jī)制失效的原因及處理辦法
asp.net4.0框架下驗(yàn)證機(jī)制失效的原因及處理辦法,需要的朋友可以參考一下2013-06-06
Asp.net中時(shí)間格式化的6種方法詳細(xì)總結(jié)
數(shù)據(jù)控件綁定時(shí)格式化日期方法/用DataBinder.Eval進(jìn)行數(shù)據(jù)綁定時(shí)/直接用ToString方法轉(zhuǎn)換日期顯示格式/用String類轉(zhuǎn)換日期顯示格式等等,感興趣的你了解下哦,或許對(duì)你學(xué)習(xí)時(shí)間格式化有所幫助2013-02-02
ASP.NET使用HttpWebRequest讀取遠(yuǎn)程網(wǎng)頁源代碼
本文分享了一個(gè)使用HttpWebRequest讀取遠(yuǎn)程網(wǎng)頁的案例,供大家參考學(xué)習(xí)。2016-03-03
ASP.NET MVC 導(dǎo)出Word報(bào)表
本文主要介紹了ASP.NET MVC 導(dǎo)出Word報(bào)表的方法,具有很好的參考價(jià)值,下面跟著小編一起來看下吧2017-02-02
visual studio 2019使用net core3.0創(chuàng)建winform無法使用窗體設(shè)計(jì)器
這篇文章主要介紹了visual studio 2019使用net core3.0創(chuàng)建winform無法使用窗體設(shè)計(jì)器,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03
在.Net?Framework應(yīng)用中請(qǐng)求HTTP2站點(diǎn)的問題解析
隨著各大瀏覽器支持和蘋果的帶頭效應(yīng),HTTP2的應(yīng)用會(huì)越來越廣泛,但是規(guī)模龐大的.NET?Framework應(yīng)用卻也不能為了連接HTTP2就升級(jí)到NET?Core平臺(tái)。通過本文提供的方案,可以最小成本的實(shí)現(xiàn).NET?Framework應(yīng)用成功訪問HTTP2站點(diǎn),感興趣的朋友跟隨小編一起看看吧2022-07-07
asp.net core webapi項(xiàng)目配置全局路由的方法示例
這篇文章主要介紹了asp.net core webapi項(xiàng)目配置全局路由的方法示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-09-09

