進公司以來, 所做的產品中, 下面的子系統就沒有少于10個的, 其中有的是.net做的, 有的是java做的, 還有安卓端, ios端. 那么這么多子系統, 我可能需要訪問其中的多個(同一平臺), 我是否需要登錄多次來操作呢? 這樣是不是太不方便了. 在我們登錄qq之后, 進入qq郵箱, 也并沒有讓我們多登錄一次, 而是直接進去了. 那么在我們的這些系統中, 怎么來實現這種功能呢?
到這里, 就引出今天的主題 - SSO 單點登錄. 在我們登錄系統A之后, 去訪問系統B, 此時并不需要再去登陸一次了. 這里有一個前提, 就是不論系統A還是系統B, 用的都是同一個賬戶和密碼. 如果他們之間的用戶名和密碼不相同, 就麻煩很多了.
在網上搜到的單點登錄解決方案, 比較多的是CAS, 耶魯大學弄得一個單點登錄解決方案。當然,在此篇,并不會介紹到CAS,只是通過一個小例子,來看看輪廓而已。
一、效果展示
在這里,我有三個網站, 當我在Server這個網站登錄之后,ClientA,ClientB我直接輸入backUrl后面的地址, 看看是否要讓我登錄。
輸入之后, 讓我直接進去了, 而沒有讓我再次登錄。
當我在HomeA頁面登出之后, 再去刷新其他的兩個頁面來看。
其他兩個頁面,也被迫登出了。
從效果上看, 是不是一個系統登錄, 多個系統可以免登錄了, 一處登出, 多個系統都被登出。 這就是一個單點登錄的效果了。
二、關鍵代碼
服務器登錄部分:
[NoLogin] public ActionResult LoginCheck(string userName, string userPwd) { if (userName == "admin" && userPwd == "123456") { User user = new User { Guid = Guid.NewGuid().ToString(), Name = "admin", Pwd = "123456" }; var newCookie = new HttpCookie("userCode", user.Guid); newCookie.Expires = DateTime.Now.AddDays(1); Response.Cookies.Add(newCookie); RedisCache.Instance.Set(user.Guid, user, TimeSpan.FromMinutes(1)); return Json(new { status = "ok", guid = user.Guid }); } return Json(new { status = "notMatch" }); }
這里的NoLogin就是一個空的特性,里面啥都沒有,可以放在類和方法上,用來判斷是否不需要登錄就可以直接訪問頁面。
當驗證成功登錄后, 將生成一個憑據Guid, 一會用戶手持這個憑據來系統驗證, 我這邊就只看redis緩存中是否有這個緩存存在, 有則表示已經登錄。并沒有做更多的驗證了。
我這里的redis是部署在ubuntu虛擬機上面的, redis對windows的支持并不是很好, 但是也能找到windows的版本, 如果可能的話, 我還是建議使用 linux 部署 redis。
客戶端驗證部分:
public class SSOClientAttribute : AuthorizeAttribute { public override void OnAuthorization(AuthorizationContext filterContext) { //判斷請求的方法或者控制器上, 有沒有免登陸特性 if (filterContext.ActionDescriptor.IsDefined(typeof(NoLoginAttribute), false) || filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(NoLoginAttribute), false)) { return; } //需要登陸的方法, 繼續驗證 base.OnAuthorization(filterContext); } protected override bool AuthorizeCore(HttpContextBase httpContext) { HttpCookie cookie = httpContext.Request.Cookies["userCode"]; if (cookie == null || string.IsNullOrEmpty(cookie.Value)) return false; var user = RedisCache.Instance.GetOrDefault<User>(cookie.Value); if (user != null) { cookie.Expires = DateTime.Now.AddDays(1); httpContext.Response.SetCookie(cookie); RedisCache.Instance.Set(user.Guid, user, TimeSpan.FromMinutes(10)); return true; } return false; } protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { string loginUrl = ConfigurationManager.AppSettings["LoginUrl"]; string Url = filterContext.RequestContext.HttpContext.Request.Url.AbsoluteUri; loginUrl += "?backUrl=" + Url; filterContext.HttpContext.Response.Redirect(loginUrl, true); } }
登出部分就簡單了, 只要清除cookie和redis緩存就行了。代碼就不貼了。
三、結束語
CAS的單點登錄比這個復雜多了, 這里只是我自己弄得一個小Demo,只是對單點登錄的一個初步認識,后面有機會的話, 希望我會把Cas的搭建、使用方法或者解析貼出來。
參考:
蔡的mysso例子(以前看過他的例子,我寫的更簡單點,能實現效果就成了)
文章列表