SSO - 开篇引例

进公司以来, 所做的产品中, 下面的子系统就没有少于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例子(以前看过他的例子,我写的更简单点,能实现效果就成了)

posted @ 2017-03-07 20:59  Sniper_ZL  阅读(544)  评论(2编辑  收藏  举报