zoukankan      html  css  js  c++  java
  • 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例子(以前看过他的例子,我写的更简单点,能实现效果就成了)

  • 相关阅读:
    day12 Python操作rabbitmq及pymsql
    day11 队列、线程、进程、协程及Python使用缓存(redis/memcache)
    day10 Python作用域 Python2.7与Python3.x的类继承的区别、异步IO、多进程,多线程简介
    day09 Python socket编程
    day08 面向对象补充及单例模式
    day07 configparser xml subprocess 面向对象
    day06 Python的一些内建变量、反射、hashlib模块、re模块、os模块、sys模块
    day05 Python多层装饰器、模块、序列化、字符串格式化、生成器和迭代器、递归、time、datetime模块、logging模块
    day04 Python一些内置函数及装饰器
    查看旧版jexus命令
  • 原文地址:https://www.cnblogs.com/elvinle/p/6471967.html
Copyright © 2011-2022 走看看