基于.Net的单点登录(SSO)解决方案

为什么80%的码农都做不了架构师?>>>   hot3.png

      前些天一位朋友要我帮忙做一单点登录,其实这个概念早已耳熟能详,但实际应用很少,难得最近轻闲,于是决定通过本文来详细描述一个SSO解决方案,希望对 大家有所帮助。SSO的解决方案很多,但搜索结果令人大失所望,大部分是相互转载,并且描述的也是走马观花。
      闲话少叙,进入正题,我的想法是使用集中验证方式,多个站点集中Passport验证。 如下图所示:

      为方便清晰描述,先定义几个名词,本文中出现之处均为如下含义。

      主站:Passport集中验证服务器 http://www.passport.com/ 。
      分站:http://www.a.com/、http://www.b.com/、http://www.c.com/ 
      凭证:用户登录后产生的数据标识,用于识别授权用户,可为多种方式,DEMO中主站我使用的是Cache,分站使用Session。
      令牌:由Passport颁发可在各分站中流通的唯一标识。
      OK,现在描述一下单点登录的过程:
      情形一、匿名用户:匿名用户访问分站a上的一个授权页面,首先跳转到主站让用户输入帐号、密码进行登录,验证通过后产生主站凭证,同时产生令牌,跳转回分 站a,此时分站a检测到用户已持有令牌,于是用令牌再次去主站获取用户凭证,获取成功后允许用户访问该授权页面。同时产生分站a的本地凭证,当该用户需要 再次验证时将先检查本地凭证,以减少网络交互。
      情形二、在分站a登录的用户访问分站b:因为用户在分站a登录过,已持有令牌,所以分站b会用令牌去主站获取用户凭证,获取成功后允许用户访问授权页面。同时产生分站b的本地凭证。

      设计完成后,接下来是方案实现的一些关键点:
      令牌:令牌 由主站颁发,主站颁发令牌同时生成用户凭证,并记录令牌与用户凭证之间的对应关系,以根据用户提供的令牌响应对应的凭证;令牌要在各跨域分站中进行流通, 所以DEMO中令牌我使用主站的Cookie,并指定Cookie.Domain="passport.com"。各分站如何共享主站的Cookie?从 分站Redirect到主站页面,然后该页面读取Cookie并以URL参数方式回传即可,可在DEMO代码中查看详细实现,当然如果哪位有更好的令牌实 现方式也拿出来分享。

//产生令牌
string tokenValue = Guid.NewGuid().ToString().ToUpper();
HttpCookie tokenCookie = new HttpCookie("Token");
tokenCookie.Values.Add("Value", tokenValue);
tokenCookie.Domain = "passport.com";
Response.AppendCookie(tokenCookie);
      主站凭证:主站凭证是一个关系表,包含了三个字段:令牌、凭证数据、过期时间。有多种实现方式可供选择,要求可靠的话用数据库,要求性能的话用Cache,DEMO中我使用的是Cache中的DataTable。如下代码所示:

/// <summary>
/// 初始化数据结构
/// </summary>
/// <remarks>
/// ----------------------------------------------------
/// | token(令牌) | info(用户凭证) | timeout(过期时间) |
/// |--------------------------------------------------|
/// </remarks>
private static void cacheInit()
{if (HttpContext.Current.Cache["CERT"] == null){DataTable dt = new DataTable();dt.Columns.Add("token", Type.GetType("System.String"));dt.Columns["token"].Unique = true;dt.Columns.Add("info", Type.GetType("System.Object"));dt.Columns["info"].DefaultValue = null;dt.Columns.Add("timeout", Type.GetType("System.DateTime"));dt.Columns["timeout"].DefaultValue = DateTime.Now.AddMinutes(double.Parse(System.Configuration.ConfigurationManager.AppSettings["timeout"]));DataColumn[] keys = new DataColumn[1];keys[0] = dt.Columns["token"];dt.PrimaryKey = keys;//Cache的过期时间为 令牌过期时间*2HttpContext.Current.Cache.Insert("CERT", dt, null, DateTime.MaxValue, TimeSpan.FromMinutes(double.Parse(System.Configuration.ConfigurationManager.AppSettings["timeout"]) * 2));}
}



      分站凭证:分站凭证主要用于减少重复验证时网络的交互,比如用户已在分站a上登录过,当他再次访问分站a时,就不必使用令牌去主站验证了,因为分站a已有该用户的凭证。分站凭证相对比较简单,使用Session、Cookie均可。

      分站SSO页面基类:分站使用SSO的页面会做一系列的逻辑判断处理,如文章开头的流程图。如果有多个页面的话不可能为每个页写一个这样的逻辑,OK,那么把这套逻辑封装成一个基类,凡是要使用SSO的页面继承该基类即可。如下代码所示:

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Text.RegularExpressions;namespace SSO.SiteA.Class
{/// <summary>/// 授权页面基类/// </summary>public class AuthBase : System.Web.UI.Page{protected override void OnLoad(EventArgs e){if (Session["Token"] != null){//分站凭证存在Response.Write("恭喜,分站凭证存在,您被授权访问该页面!");}else{//令牌验证结果if (Request.QueryString["Token"] != null){if (Request.QueryString["Token"] != "$Token$"){//持有令牌string tokenValue = Request.QueryString["Token"];//调用WebService获取主站凭证SSO.SiteA.RefPassport.TokenService tokenService = new SSO.SiteA.RefPassport.TokenService();object o = tokenService.TokenGetCredence(tokenValue);if (o != null){//令牌正确Session["Token"] = o;Response.Write("恭喜,令牌存在,您被授权访问该页面!");}else{//令牌错误Response.Redirect(this.replaceToken());}}else{//未持有令牌Response.Redirect(this.replaceToken());}}//未进行令牌验证,去主站验证else{Response.Redirect(this.getTokenURL());}}base.OnLoad(e);}/// <summary>/// 获取带令牌请求的URL/// 在当前URL中附加上令牌请求参数/// </summary>/// <returns></returns>private string getTokenURL(){string url = Request.Url.AbsoluteUri;Regex reg = new Regex(@"^.*\?.+=.+$");if (reg.IsMatch(url))url += "&Token=$Token$";elseurl += "?Token=$Token$";return "http://www.passport.com/gettoken.aspx?BackURL=" + Server.UrlEncode(url);}/// <summary>/// 去掉URL中的令牌/// 在当前URL中去掉令牌参数/// </summary>/// <returns></returns>private string replaceToken(){string url = Request.Url.AbsoluteUri;url = Regex.Replace(url, @"(\?|&)Token=.*", "", RegexOptions.IgnoreCase);return "http://www.passport.com/userlogin.aspx?BackURL=" + Server.UrlEncode(url);}}//end class
}



      用户退出:用户退出时分别清空主站凭证与当前分站凭证。如果要求A站点退出,B、C站点也退出,可自行扩展接口清空每个分站凭证。
      主站过期凭证/令牌清除:定时清除(DataTable)Cache[“CERT”]中timeout字段超过当前时间的记录。

1.IIS中配置站点

配置4个站点指向相应的目录,并分别指定4个站点的主机头:

http://www.passport.com/

http://www.a.com/

http://www.b.com/

http://www.c.com/ 

2.修改hosts文件以将域名解析到本地站点

127.0.0.1          http://www.passport.com/

127.0.0.1          http://www.a.com/

127.0.0.1          http://www.b.com/

127.0.0.1          http://www.c.com/

转载于:https://my.oschina.net/ind/blog/416103

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/544826.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ajax在请求数据时显示等待动画遮罩

/*** 等待提醒 开始 *********************************************************************************************** -yzy -20150408 说明&#xff1a;在Ajax请求数据的时候&#xff0c;显示等待界面*/// 1、这里显示等待框$(#YWaitDialog).show();// 2、这里进行ajax的请…

c语言 函数的参数传递示例_isgreaterequal()函数以及C ++中的示例

c语言 函数的参数传递示例C isgreaterequal()函数 (C isgreaterequal() function) isgreaterequal() function is a library function of cmath header, it is used to check whether the given first value is greater than or equal to the second value. It accepts two va…

在android中ScrollView嵌套ScrollView解决方案

文章转载自&#xff1a;http://www.jb51.net/article/33054.htm大家好&#xff0c;众所周知&#xff0c;android里两个相同方向的ScrollView是不能嵌套的&#xff0c;那要是有这样的需求怎么办,接下来为您介绍解决方法&#xff0c;感兴趣的朋友可以了解下大家好&#xff0c;众所…

Cucumber 入门一

&#xff08;转自&#xff1a;http://www.cnblogs.com/jarodzz/archive/2012/07/02/2573014.html&#xff09; 第一次看到Cucumber和BDD&#xff08;Behavior Driven Development, 行为驱动开发&#xff09;&#xff0c;是在四年前。那时才開始工作&#xff0c;对软件測试工具相…

Python datetime astimezone()方法与示例

Python datetime.astimezone()方法 (Python datetime.astimezone() Method) datetime.astimezone() method is used to manipulate objects of datetime class of module datetime. datetime.astimezone()方法用于操作模块datetime的datetime类的对象。 It uses an instance …

Java中这7个方法,一不小心就用错了!

最近我们通过sonar静态代码检测&#xff0c;同时配合人工代码review&#xff0c;发现了项目中很多代码问题。除了常规的bug和安全漏洞之外&#xff0c;还有几处方法用法错误&#xff0c;引起了我极大的兴趣。我为什么会对这几个方法这么感兴趣呢&#xff1f;因为它们极具迷惑性…

java 标志一个方法为过时方法

使用 Deprecated 来标记方法 Deprecated//用来判断ip是否合法public boolean checkIp(String tempIp) {String regex "(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)){3}"; // String regex2 "([1-9]|[1-9]\\\\d…

STL之顺序容器

顺序容器&#xff1a; vector&#xff1a;数组 list:链表 deque&#xff1a;双端数组 顺序容器适配器&#xff1a; stack&#xff1a;堆栈 queue&#xff1a;队列 priority_queue&#xff1a;优先级队列 deque是一个动态数组  deque与vector非常类似&#xff1b;  deque可以…

Java StringBuilder reverse()方法与示例

StringBuilder类reverse()方法 (StringBuilder Class reverse() method) reverse() method is available in java.lang package. reverse()方法在java.lang包中可用。 reverse() method is used to reverse this character sequence by the reverse of the sequence. reverse()…

这样设置,让你的 IDEA 好看到爆炸

今天这期我们来分享几个美化 IDEA 设置技巧&#xff0c;让你的 IDEA 与众不同。首先我们来看下 IDEA 默认设置&#xff0c;虽然不丑&#xff0c;但就是太单调&#xff0c;千篇一律。默认主题接着&#xff0c;我们来看下美化以后的界面&#xff0c;总体看起来是不是比默认好看了…

IMP-00002: 无法打开 D:\orcldat\test_20111024.dmp 进行读取,rman备份

文章转自&#xff1a;http://blog.csdn.net/wanglilin/article/details/6900633 首先&#xff0c;我的路径写错了&#xff0c;文件夹是orcldata我掉了个a。 其次&#xff0c;命令后添加 fully。 dos下随便哪个目录> [sql] view plaincopyprint? IMP username/pwddbname BU…

observable_Java Observable setChanged()方法与示例

observable可观察的类setChanged()方法 (Observable Class setChanged() method) setChanged() method is available in java.util package. setChanged()方法在java.util包中可用。 setChanged() method is used to set this Observable object status as changed. setChanged…

RabbitMQ 集群

2019独角兽企业重金招聘Python工程师标准>>> Clustering Guide A RabbitMQ broker is a logical grouping of one or several Erlang nodes, each running the RabbitMQ applicationand sharing users, virtual hosts, queues, exchanges, etc. Sometimes we refer …

使用uuid作为数据库主键,被技术总监怼了!

一、前言在日常开发中&#xff0c;数据库中主键id的生成方案&#xff0c;主要有三种数据库自增ID采用随机数生成不重复的ID采用jdk提供的uuid对于这三种方案&#xff0c;我发现在数据量少的情况下&#xff0c;没有特别的差异&#xff0c;但是当单表的数据量达到百万级以上时候&…

Android设置透明、半透明等效果

首先说明一点&#xff0c;关于透明的 Android 控件 background 问题&#xff0c;从转载来的文章看到最主要的一句有用的代码是&#xff1a; v.getBackground().setAlpha(100);//0~255透明度值 这里的 Alpha 值&#xff0c;实际上是 0-1 的取值范围。 以下内容转自&#xff…

Java Long类shortValue()方法与示例

长类shortValue()方法 (Long class shortValue() method) shortValue() method is available in java.lang package. shortValue()方法在java.lang包中可用。 shortValue() method is used to return the value denoted by this Long object converted to type short (by casti…

ThreadLocal不好用?那是你没用对!

作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;在 Java 中&#xff0c;如果要问哪个类使用简单&#xff0c;但用好最不简单&#xff1f;我想你的脑海中一定会浮现出一次词—…

记一则js替换字符串的问题

2019独角兽企业重金招聘Python工程师标准>>> 软件的一处功能用到EasyUI的表单提交&#xff0c;返回一串字符串&#xff0c;这串字符串里有一段HTML代码&#xff0c;正常的情况下这段HTML代码里的双引号“ 是用 \ 转义过的。在IE中没问题&#xff0c;但是在Firefox和…

『图解Java并发』面试必问的CAS原理你会了吗?

在并发编程中我们都知道i操作是非线程安全的&#xff0c;这是因为 i操作不是原子操作。如何保证原子性呢&#xff1f;常用的方法就是加锁。在Java语言中可以使用 Synchronized和CAS实现加锁效果。Synchronized是悲观锁&#xff0c;线程开始执行第一步就是获取锁&#xff0c;一旦…

美易官方:“圣诞老人行情”美股能否延续近期涨势?

圣诞老人行情”有望上演&#xff0c;美股能否延续近期涨势&#xff1f; 随着圣诞节的临近&#xff0c;市场开始期待所谓的“圣诞老人行情”能够上演。在过去的几年里&#xff0c;这个时期往往会出现一波上涨行情&#xff0c;给投资者带来一些安慰和喜悦。然而&#xff0c;今年的…