文章出處

進公司以來, 所做的產品中, 下面的子系統就沒有少于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例子(以前看過他的例子,我寫的更簡單點,能實現效果就成了)


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()