《前言》

(一) Winner2.0 框架基础分析

(二)PLSQL报表系统

(三)SSO单点登录

(四) 短信中心与消息中心

(五)钱包系统

(六)GPU支付中心

(七)权限系统

(八)监控系统

(九)会员中心

(十) APP版本控制系统

(十一)Winner前端框架与RPC接口规范讲解

(十二)上层应用案例

(十三)总结

《SSO及应用案例》

先说说SSO(单点登录)这种产物是怎么来的? 场景是这样的假设一个大型应用平台(web)下面有几个模块比如:商城,机票,酒店。

我上商城时候我没有登录,则登录一下,而又从商城跳转到酒店,发现酒店没登录则又需要登录一下酒店网站。

这里人们就会想有没有,我在我当前域名下一处登录,就可以在当前域名(子域名)随处浏览以及操作,所以这时候就诞生了SSO。

SSO不单单解决了我们“一处登录,到处操作”的问题以外,还省去了我们每个项目开发登录模块的时间。SSO的基本原理如下:

SSO虽然叫单点登录,但是我们更愿意叫他“统一登录中心”,因为的他的职责就是承担了所有的登录工作,虽然SSO在Web时代很盛行,但是在APP时代

就又有很大的不同了,这个我后面会讲到。这里先讲一下上面一张图中SSO 以及客户端分别是做了哪些事情

这里画的还是比较抽象,有几点可以说一下:

1,上图中应用里用Session存储用户信息,有的做法是用Cookie。这里用Session或Cookie都可以,但是Cookie本身存在客户端浏览器中,

所以从安全性上来说不如Session,Cookie的优点是不会随浏览器的关闭而销毁,下次访问网站时可以无需登录。这里各取所需,我们从安全性

上考虑说选择了用Session。

2,关于创建Ticket后将Ticket传给子站。Ticket叫做“令牌”,本身包含用户的基础信息(比如账号、用户名)还有子站要访问的页面地址,以及过期时间等等。

Ticket要回传给子站有的是直接往SSO站的Cookie里面存,然后子站通过设置domain参数共享cookie读写。这个本身没有问题但是还是个第一点一样。

把握好安全性就行。

3,图一我画的是用cookie的方式,尽管本地有用户信息(Ticket)但是为是安全还是要上SSO上请验证一下,登录是否过期。这种做法有的甚至每个页面都去

请求SSO看是否有过期,过期了则退出登录,这个是根据业务需求的不同做的。比如邮箱,没操作一次授权时间加长10分钟,如果十分钟没有任何操作

再操作的时候就被退出了。这个看具体应用,用法不同而已。

4,如果客户端(浏览器)禁用Cookie那Session是拿不到的。这个其实都知道每个浏览器的Session_Id不同,Session本身是键值对,但是唯一性标识

不是Session的key,是Session_id,而session_id 是保存在浏览器的Cookie中的,其实就等于禁用了Cookie,Session也废了。

============================华丽的分割线===================================

接下来要说重点了,其实在上一篇《理解Oauth2.0》中就讲到了很多和SSO类似的概念,其实两者本质是一样的。但是我们也可以

分开来看。我就更习惯分开来看,我的理解是这样的,我认为OAuth更关注的是“授权”,SSO则侧重是“登录”。

所以从概念上来说,OAuth的设计天生就不用去关注比如跨域这样的问题,SSO则更多是本平台下一站登录,随处操作。

前期我们Winner框架中是SSO来扩展OAuth,今年Jason重构了一个版本则是OAuth来兼任SSO。这里没有好坏技术高低之分,只是场景不同。

现在基本是一个APP的时代,所以SSO的功能被弱化了,更多时候我们使用APP就没有一个所谓的“一处登录,随处操作”的说法,就一个登录。

我们来看看Winner中的核心代码:

首先,我在前面讲《Winner.FrameWork.MVC》 的时候有说到,以前我们使用基类去验证用户是否登录,而现在我们使用更灵活的特性类去处理

我们Winner中特性类的验证最常用的是[AuthLogin] 和 [AuthRight] 两者的不同在于 [AuthLogin] 只验证是否有登录,没有登录就去登录。

意思就是说该页面所有人都有权限访问,前提是有注册。而[AuthRight] 则不单单是验证了是否登录,还验证了是否有权限访问本页面。

关于权限那一块,在后面的文章中我再单独讲权限系统时再细讲。

我们重点来看一下[AuthLogin] 的核心代码:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Dynamic;
  4. using System.Linq;
  5. using System.Net;
  6. using System.Web;
  7. using System.Web.Mvc;
  8. using System.Xml;
  9. using Winner.Framework.MVC.Attribute;
  10. using Winner.Framework.MVC.GlobalContext;
  11. using Winner.Framework.MVC.Models;
  12. using Winner.Framework.MVC.Models.Account;
  13. using Winner.Framework.Utils;
  14.  
  15. namespace Winner.Framework.MVC
  16. {
  17. /// <summary>
  18. /// PC Web用户登陆检查
  19. /// </summary>
  20. public class AuthLoginAttribute : AuthorizationFilterAttribute
  21. {
  22. /// <summary>
  23. /// 实例化一个新的验证对象
  24. /// </summary>
  25. /// <param name="ignore">是否忽略检查</param>
  26. public AuthLoginAttribute(bool ignore = false)
  27. : base(ignore)
  28. {
  29. }
  30.  
  31. /// <summary>
  32. /// 登陆验证
  33. /// </summary>
  34. /// <param name="context">当前上下文</param>
  35. protected override bool OnAuthorizationing(AuthorizationContext context)
  36. {
  37. //Ajax请求但又未登录时则返回信息
  38. if (!ApplicationContext.Current.IsLogined && base.ContextProvider.IsAjaxRequest)
  39. {
  40. OutputResult("未登录或者会话已过期,请重新登录!", );
  41. return false;
  42. }
  43. if (context.HttpContext.Session == null)
  44. {
  45. throw new Exception("服务器Session不可用!");
  46. }
  47. try
  48. {
  49. //调用提供者进行登陆
  50. ProviderManager.LoginProvider.Login();
  51. }
  52. catch (Exception ex)
  53. {
  54. Log.Error(ex);
  55. if (ex.InnerException != null)
  56. {
  57. Log.Error(ex.InnerException);
  58. }
  59. OutputResult("登陆时出现系统繁忙,请稍后再试!", );
  60. return false;
  61. }
  62.  
  63. //如果没有登陆则返回
  64. if (!ApplicationContext.Current.IsLogined)
  65. {
  66. OutputResult("未登录", );
  67. return false;
  68. }
  69. return true;
  70. }
  71. }
  72. }

我们看到一开始我们有base(ignore);这个我在前面的篇章中有讲到过,这个可以通过配置文件配置,目的是省去我们每个项目开发的时候都要去登录。

在配置文件中默认一个登录账号,这样调试时候能省很多时间。

我们判断的步骤是这样的:

第一步:如果用户是ajax请求,并且用户信息不存在的话直接返回false。这里是应对用户登录之后 用户长时间未操作造成用户信息过期失效

因为我们的Winner框架基本都是Ajax请求,所以当两个条件都存在的时候就直接返回401错误。如果界面显示401则重新刷新一下,

因为刷新就不是Ajax了,所以就会跳到登录页去登录。

第二步:判断Session是否可用,不可用就直接抛异常了,就是我上面说的禁用Session这种情况。

第三步:在ProviderManager.LoginProvider.Login(),我们才是做了具体的操作,我们看一下Login()代码:

  1. public void Login()
  2. {
  3. //检查是否有SSO站点POST过来的用户退出数据
  4. string str = HttpContext.Current.Request.Url.Query;
  5. if (str.Contains("logout"))
  6. {
  7. //TODO:退出本地登陆
  8. Logout();
  9. return;
  10. }
  11. //检查本地系统是否已登陆
  12. if (ApplicationContext.Current.IsLogined)
  13. return;
  14.  
  15. //判断是否有配置自动登陆
  16. if (GlobalConfig.IsAutoLogin)
  17. {
  18. //代理登陆配置文件所配置的用户
  19. var autoResult = ApplicationContext.UserLogin(GlobalConfig.DefaultAutoLoginUserId, true);
  20. if (!autoResult.Success)
  21. {
  22. throw new Exception(autoResult.Message);
  23. }
  24. HttpCookie cookie = new HttpCookie("ticket");
  25. cookie.Value = GlobalConfig.DefaultAutoLoginToken;
  26. HttpContext.Current.Response.AppendCookie(cookie);
  27. return;
  28. }
  29.  
  30. //如果没有Ticket直接跳转到SSO进行检查
  31. int userId;
  32. if (!ApplicationContext.GetNodeIdByTicket(out userId))
  33. {
  34. SSOLogin();
  35. return;
  36. }
  37. Log.Debug("user_id={0}", userId);
  38. //登陆到本地系统
  39. var result = ApplicationContext.UserLogin(userId, false);
  40. if (!result.Success)
  41. {
  42. throw new Exception(result.Message);
  43. }
  44. }
  1. private void SSOLogin()
  2. {
  3. string service = HttpContext.Current.Request.Url.AbsoluteUri;
  4. service = Regex.Replace(service, @"\?ticket[^&]*.", "");
  5. string url = string.Concat(GlobalConfig.SSO_LoginURL, "?service=", HttpContext.Current.Server.UrlEncode(service));
  6. HttpContext.Current.Response.Redirect(url);
  7. }

这里ApplicationContext.Current.IsLogined为True的话,就是用户已经登录过了,登录过了就返回,IsLogined属性里面是判断了用户信息是否存在。

如果配置了自动登录,则装载自动登录的用户信息,从配置文件中读取。最后,上面判断都False的话,就跳到SSO系统去登录获取ticket。

===================================华丽的分割线===========================

下面就是SSO系统做的事情,SSO最基本的职责就是登录,首先就是登录界面。根据用户填写的账号密码判断用户是否注册,没有注册则注册。

说白了就是登录注册流程。用户在SSO登录成功之后则创建Session保存用户账号,然后生成一个ticket字符串。每个团队对于Ticket字符串的内容

都不太相同,但是大抵就是要请求界面的url,账户号,授权码这些。

当然子系统判断URL中有ticket值的时候,就将Ticket 写入子项目的Session,其实我们会有一个UserInfo的基础对象,这个Userinfo是一个用户信息的model。

这个是根据Ticket带过来用户账户再到数据库查了一次的。

Jason重构一次SSO,方式上有点变动,更多的是采用Oauth2.0的方式。不清楚Oauth的可以看我上篇文章《理解Oauth2.0》

这里我公开一下我们SSO项目的源码,所以我就不一一的贴出来了。有兴趣的朋友可以自己看代码不懂的可以在QQ群里咨询。

SSO登录中心GitHub下载地址:https://github.com/demon28/OAuth2.git

就写到这里。有兴趣一起探讨Winner框架的可以加我们QQ群:261083244。或者扫描左侧二维码加群。

程序员的自我救赎---3.2:SSO及应用案例的更多相关文章

  1. 程序员的自我救赎---1.4.2: 核心框架讲解(BLL&Tool)

    <前言> <目录> (一) Winner2.0 框架基础分析 (二) 短信中心 (三)SSO单点登录 (四)PLSQL报表系统 (五)钱包系统 (六)GPU支付中心 (七)权限 ...

  2. 程序员的自我救赎---11.3:WinService服务

    <前言> (一) Winner2.0 框架基础分析 (二)PLSQL报表系统 (三)SSO单点登录 (四) 短信中心与消息中心 (五)钱包系统 (六)GPU支付中心 (七)权限系统 (八) ...

  3. 程序员的自我救赎---11.4:FileSystem文件服务

    <前言> (一) Winner2.0 框架基础分析 (二)PLSQL报表系统 (三)SSO单点登录 (四) 短信中心与消息中心 (五)钱包系统 (六)GPU支付中心 (七)权限系统 (八) ...

  4. 程序员的自我救赎---11.1:RPC接口使用规范

    <前言> (一) Winner2.0 框架基础分析 (二)PLSQL报表系统 (三)SSO单点登录 (四) 短信中心与消息中心 (五)钱包系统 (六)GPU支付中心 (七)权限系统 (八) ...

  5. 程序员的自我救赎---12.2.3: 虚拟币交易平台(区块链) 下 【C#与以太坊通讯】

    <前言> (一) Winner2.0 框架基础分析 (二)PLSQL报表系统 (三)SSO单点登录 (四) 短信中心与消息中心 (五)钱包系统 (六)GPU支付中心 (七)权限系统 (八) ...

  6. 程序员的自我救赎---3.1:理解Oauth2.0

    <前言> (一) Winner2.0 框架基础分析 (二)PLSQL报表系统 (三)SSO单点登录 (四) 短信中心与消息中心 (五)钱包系统 (六)GPU支付中心 (七)权限系统 (八) ...

  7. 程序员的自我救赎---1.4.3: 核心框架讲解(MVC)

    <前言> (一) Winner2.0 框架基础分析 (二)PLSQL报表系统 (三)SSO单点登录 (四) 短信中心与消息中心 (五)钱包系统 (六)GPU支付中心 (七)权限系统 (八) ...

  8. 程序员的自我救赎---10.1:APP版本控制系统

    <前言> (一) Winner2.0 框架基础分析 (二)PLSQL报表系统 (三)SSO单点登录 (四) 短信中心与消息中心 (五)钱包系统 (六)GPU支付中心 (七)权限系统 (八) ...

  9. 程序员的自我救赎---1.4.1:核心框架讲解(DAL)

    <前言> (一) Winner2.0 框架基础分析 (二)PLSQL报表系统 (三)SSO单点登录 (四) 短信中心与消息中心 (五)钱包系统 (六)GPU支付中心 (七)权限系统 (八) ...

随机推荐

  1. Matrix Again(最大费用最大流)

    Matrix Again Time Limit: 5000/2000 MS (Java/Others) Memory Limit: 102400/102400 K (Java/Others) Tota ...

  2. solr安装血泪史

    Windows10专业版,solr6.5.1,tomcat8.5.14,jdk1.8 转自http://www.jianshu.com/p/dd7a59b3f0b5 科普篇 来自百度百科:Solr简介 ...

  3. javascript中toString和valueOf方法的区别

    toString():将对象转为字符串 valueOf():获取对象的原始值, 1.针对基本类型的变量:如在string,number,boolean类型的变量上调用这两个方法时,直接返回原始值,即变 ...

  4. 单元测试 Qunit

    http://api.qunitjs.com/category/assert/    测试方法   选中 "Check for Globals" 会暴露全局对象,看你的代码会不会无 ...

  5. ruby 安装 mysql2 命令

    sudo apt-get install libmysql-ruby libmyclient-dev

  6. linux 安装 Elasticsearch5.6.x 详细步骤以及问题解决方案

    在网上有很多那种ES步骤和问题的解决 方案的,不过没有一个详细的整合,和问题的梳理:我就想着闲暇之余,来记录一下自己安装的过程以及碰到的问题和心得:有什么不对的和问题希望及时拍砖. 第一步:环境 li ...

  7. spring AOP 和自定义注解进行身份验证

    一个SSH的项目(springmvc+hibernate),需要提供接口给app使用.首先考虑的就是权限问题,app要遵循极简模式,部分内容无需验证,用过滤器不能解决某些无需验证的方法 所以最终选择用 ...

  8. JavaScript基本知识点整理(超实用)

      絮叨絮叨                   今天给大家分享一下这两天自己整理的JavaScript部分的笔记,下面都是我觉得比较常用的,希望能帮助到大家! 1. 导入JS的三种方式 ①在HTML ...

  9. sqoop: mysql to hive

    sqoop import --connect 数据库连接 --username 数据库用户名--password 数据库密码 --table 导入的表 -m 1 --hive-import --tar ...

  10. Python 面向对象(一) 基础

    Python 中一切皆对象 什么是面向对象? 面向对象就是将一些事物的共有特征抽象成类,从类来创建实例. 类class 可以理解为模版 比如人类,都具有身高.体重.年龄.性别.籍贯...等属性,但属性 ...