老生常谈:利用Membership实现SSO(单点登录)
虽然有一些现成的第三方解决方案比如:OpenID,Passport,SpaceCard等都还不错,但是要么就是收费的(passport),要么就是有点用不习惯(比如OpenID),另外中途跳到一个界面完全不相同的第三方站点上输入,用户感觉也不太放心。
当然博客园和csdn上也有不少人已经给出了相应的解决方案,但是一圈看下来,虽然思路都行得通,貌似就是没找到不同主域名下直接利用Membership实现的例子。
那么还是自己“造轮子”弄一个简陋一点的方案吧,其实只要用过Membership的朋友都知道“用户是否已经登录”以及“用户当前的role是什么”等问题的判断依据就是检测客户端有没有(当前要访问)网站(所在域)的cookie票据,如果各子站都在同一个主域下,这么问题很容易解决,只要设置各子域cookie的domain为统一值即可,但如果各子站的主域名不同,这招就失效了(出于安全考虑,浏览器设计时就约定不同主域的cookie不能互访),所以问题的关键是:如何能让用户在某个域成功登录后,自动把本域下的cookie票据同步复制到其它域下!
另外还有用户注销问题,当用户从一个分站注销时其它分站如何注销?其实把上面的思路反过来,也能找到问题的关键:即一个域下的cookie票据清除后,如何能让其它域下的cookie票据也能清除!
根本这个思路,大概整理了一个流程图:
当然还有一些因素要考虑:比如传递敏感参数(比如用户名)时应该加密;同时各分站专用于接收票据和清除票据的页面,如何防止非法访问等等。此外,最好还要求各分站使用同一套统一的用户名/密码表。(当然如果各分站已经有各自的用户数据了,也有办法解决,比如可以新建一套新的统一帐号/密码库,原来各自的分站用户表上加一个字段,映射到新库帐号表的唯一字段)。
示例代码:
1.解决方案结构图
说明:
Passport:认证中心,用于统一登录和注销的类似passport站点
SiteA:站点A,其中admin需要登录才能访问(规则在web.config中配置),passport目录下的login.aspx/loginout.aspx分别用于接收认证中心发来的票据,以及清除票据
SiteB:站点B,结构代码完全同SiteA相同,仅为了与SiteA比较效果用
WebHelper:工具辅助类
2.技术要点:
(a)Cookie同步问题:因为cookie是基于浏览器的,所以直接用代码以Post或Get方式模拟访问SiteA中的/passport/login.aspx以logout.aspx时,并不能正确生成Cookie或清除Cookie,因此我采用了一个变通的办法(隐藏的iframe)来模拟浏览器访问这二个页面
(b)为了尽量使用Membership的功能,少写代码,同时保留membership通过web.config配置目录访问权限的风格,在passport项目中,分别针对各个站点新建A,B...等分站目录,目的仅仅是让ReturnUrl=/Admin/Default.aspx能自动变成类似ReturnUrl=http://www.SiteA.com/Admin/Default.aspx,以便在页面跳转时不需要额外处理
(3)即使是用iframe来实现跨域读写Cookie,默认情况下,如果用户IE浏览器的“隐私”级别设置为中(及中以上)时,浏览器仍然会阻止iframe跨域设置Cookie(所谓的“同域同源”原则,即:当前浏览器的url以及各frame/iframe里面的页面,如果在同一个域名,就能正常实现cookie的读写,否则禁止。单从这一点看,IE其实要比FF之流安全),所以需要在PassPort以及联盟站点的IIS-->Http头中设置相同的P3P协议值(目的是告诉IE:这一组站点相互之间是“朋友”,不要“阻挡”!),具体办法有二种(任选一种即可):
2.1-直接写代码:Response.AddHeader("P3P","CP="CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR""); 把这一行加入要执行Cookie读写的页面的Page_Load事件第一行(注:“CP=”后面的值可以随便设置,只要是一个其它人不知道的string即可,相当于一个密钥)
2.2-IIS站点属性->HTTP头->添加-->自定义HTTP头为 P3P-->自定义HTTP头值为 CP="CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR”-->保存
3.示例代码说明
示例中对于参数的加密采用了Enterprise Library 4.1的加密模块,所以需要安装Enterprise Library才能编译通过
欢迎转载,但请注明来自“菩提树下的杨过”
示例代码下载地址:http://files.cnblogs.com/yjmyzz/SSO.rar
2013/10/3 更新:
利用HttpModule自动拦截机制,把原来各分站的Login.aspx/LoginOut.aspx这些都去掉了,全都在HttpModule拦截处理,同时用script 引用,代替了原来的iFrame机制,新的示例代码地址:http://files.cnblogs.com/yjmyzz/SSO.zip
- SQL Server基础SQL脚本之内外连接、交叉连接;函数、子查询
- MySQL 面试选择题15道(单选)
- SQL Server基础SQL脚本之分区表、分区方案
- SQL Server基础SQL脚本之创建架构、排序
- 枚举算法(Enumeration algorithm)实例一
- 剑指offer代码解析——面试题16反转单链表
- QMainWindow 和 QWidget 设置layout
- 安全工具Aircrack-ng的使用
- Windows Server 2008 R2 搭建微信小程序
- insert事务产生duplicate key error引发的死锁分析
- 零基础入门深度学习 | 第六章:长短时记忆网络(LSTM)
- 第6章 I/O复用:select和poll函数
- 第7章 套接字选项
- 第8章 基本UDP套接字编程
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Manytasking MATP MOOMFO 中G函数
- 为什么要用TypeScript
- np.clip截取函数
- 常见编程模式之双指针
- python操作txt文件中数据教程[2]-python提取txt文件中的行列元素
- JSON 是什么?它能带来什么?它和 XML 比较?
- 一起来学演化计算-实数空间变异算子
- 卡特兰数入门
- 常见编程模式之动态规划:0-1背包问题
- stat 命令家族(2)- 详解 pidstat
- MTO和MaTO MMZDT
- stat 命令家族(3)- 详解 mpstat
- 知识图谱入门(一)
- PHP判断变量内容是什么编码(gbk?utf-8) mb_detect_encoding
- stat 命令家族(4)- 详解 iostat