2014年1月28日星期二

PHP成长记(三) —— SSO单点登录/登出

本邮件内容由第三方提供,如果您不想继续收到该邮件,可 点此退订
PHP成长记(三) ―― SSO单点登录/登出  阅读原文»

用户名:村长爱技术
文章数:24
评论数:19
访问量:6533无忧币:430博客积分:434:3 注册日期:2013-12-18

  • 三日内更新
PHP成长记(三) ―― SSO单点登录/登出

你有没有遇到过公司每一个产品,都要开发一个用户系统,浪费开发成本不说,用户体验还不好,用户要记住每一个产品的用户密码,每个产品都要重复登录,问题重重,要是能登录一次别的一些列产品就都登录了那该多好,SSO(Single Sign On)单点登录,能帮你解决这些问题。

我在这里来说一下单点登录实现原理,希望能让大家对单点登录更了解,并应用之。我这里以phpCAS的例子来说讲解。

首先我们想到单点登录可能是通过在根域下设置一个cookie,然后只要在这个域下的都可以共享cookie,这种方式最大的问题就是不能实现跨域(虽然网上有很多跨域的解决方案,比如header方式,但是各个浏览器支持的都不是很好),并且很不安全。所以CAS实现了另一种方式――cas server和cas client分离,也就是说单点登录服务器单独部署,其他client调用它,从而实现单点登录。具体流程如下图

wKioL1LmEoXAB32OAAKlQIXqKhA161.jpg

1、浏览器访问单点登录的网站,如果session存在就返回数据,如果不存在就跳转到cas server(一个单独域名的服务)

2、如果有已经登录过,通过检测cookie,cas server就会302跳转通过url返回Ticket(Ticket是随机且唯一)到网站,如果没有登录过,就跳转到cas server登录页面进行登录,登录成功设置cookie,然后302跳转返回Ticket到网站,同时cas server会存储改Ticket、cookie和网站host的对应关系

3、 网站接收到Ticket后通过cas server提供的校验url去校验Ticket,cas server确认后返回成功,网站把当前的sessionid换成Ticket(session_id(Ticket)),然后返回浏览器请求数据

4、当用户再访问网站的时候,就会判断当前session是否登录

单点登录就说完了,其实没有太复杂的技术,主要是实现的思想,说完单点登录大家一定想知道的是如何单点登出呢?接下来我再分析一下单点登出的原理:

wKiom1LmGDLTq923AAG-XpjR3Fo623.jpg

1、用户点击登出,网站先清除当前网站的session信息,然后跳转到cas server退出URL

2、cas server接收到登出请求后删除cookie或session信息,并且通过单点登录时候记录的cookie和host、Ticket对应关系,遍历host给网站发送post登出请求

3、网站接收到登出请求后,获取Ticket,并且把Ticket换成当前的sessionid,模拟用户登录,然后销毁session完成登出

以上是本人通过研究phpCAS获得的结论,这个只是最基本的模式,还有很多复杂模式,比如代理模式等等,有经验的朋友欢迎交流!

谢谢!

本文出自 "村长爱技术" 博客,请务必保留此出处http://weijingwu.blog.51cto.com/8376555/1355109

foreach循环对异步委托的影响  阅读原文»

foreach循环对异步委托的影响

在使用foreach对异步委托赋值的时候,发现一个问题。代码如下:

static void Main(string[] args)
List<Task> lst_tsk = new List<Task>();
List<int> lst_item = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
foreach (var item in lst_item)
Task tsk = new Task(() =>
Console.WriteLine(item);
Console.ReadLine();

往Task中,赋值一个拉姆达表达式,期待运行的结果应该是1,2,3,4,5,6,7,8,9,10乱序输出。但是实际上的结果是10,10,10,10,10,10,10,10,10,10。很多人都认为这是c#编译器的一个bug。Eric做出了解释,根据Eric的文章,在foreach循环语句中的变量只有一个item,该变量在循环过后,被赋值为10了。当异步线程启动的时候,取到的item早就变成10了,因此就得出上面的结果。

根据Eric的文章,foreach只是一个语法糖,它对应的代码如下

IEnumerator<int> e = ((IEnumerable<int>)values).GetEnumerator();
int m; // OUTSIDE THE ACTUAL LOOP
while(e.MoveNext())
m = (int)(int)e.Current;
funcs.Add(()=>m);
if (e != null) ((IDisposable)e).Dispose();

可以看到m并不包括在while语句中,而且()=>m的意思是返回当前m变量的值,而不是返回委托创建时m变量的值。因此当这个委托真正运行的时候,找到的m可能已经是其它值了。

如果把语法糖改成如下的方式:

while(e.MoveNext())
m = (int)(int)e.Current;

那么m在while内部,每一个m都是单独的。根据Eric,不这样改的一个原因就是,它可能会增加了在循环中使用闭包的次数,(因为异步线程在启动时,都会用到循环中的m,这个m的生命周期在while循环中,只能通过闭包机制,使得其值能够继续保留在内存中,能够让异步委托在调用的时候继续访问到该值)。而且,如果这样修改了,用户会觉得foreach每一个循环都使用了一个新的变量,而不是一个存储了新值的旧变量。

因此,一开始的演示代码,只需要如下修改既可以了:

static void Main(string[] args)
List<Task> lst_tsk = new List<Task>();
List<int> lst_item = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
foreach (var item in lst_item)
var copy= item;//增加一个临时的拷贝变量
Task tsk = new Task(() =>
Console.WriteLine(copy );
Console.ReadLine();

这样的话,每次委托运行的时候,都会去找copy 变量了。

可能是很多人的意见影响了C#编译器团队,在C#5.0中,他们决定修改这个问题,foreach循环中的变量存在于循环中,因此每次循环都使用的是一个新的变量。for循环暂时不做修正。因此,演示代码在VS2012下,使用C#5.0的编译器编译,得到的结果是如预期那样的乱序输出。

本文出自 "一只博客" 博客,请务必保留此出处http://cnn237111.blog.51cto.com/2359144/1355032

分享至 一键收藏,随时查看,分享好友!

阅读更多内容

没有评论:

发表评论