<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>crazysoul</title>
    <description></description>
    <link>http://crazysoul.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
          <item>
        <title>对单点认证的理解（不是讨论怎么解决）</title>
        <author>crazysoul</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://crazysoul.javaeye.com">crazysoul</a>&nbsp;
                    链接：<a href="http://crazysoul.javaeye.com/blog/23674" style="color:red;">http://crazysoul.javaeye.com/blog/23674</a>&nbsp;
          发表时间: 2006年01月03日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          好比你去一个公园游玩，去到每个摊点时却又要你买一次票，是不是很麻烦？<br />而单点认证就是只要在门口买一次票后，只需在进去时要向摊主展示一下你的门票<br />，同一个公园内的摊点就任意随你参观了。<br /><br />即统一一个登录入口,经过验证后发给客户端一张"门票"作为买过票的评据，可以访问所有"公园设施"，至于"门票"是怎么保存到游人手上(客户端)的,就是单点认证的核心难点。<br /><br />很多人说用COOKIE。不错,在B/S模式中，COOKIE可以说是唯一合法的、能主动由服务器存放数据到客户端的途径(SESSION也是由COOKIE来保存SESSIONID的，若是临时的则由当前浏览器进程,SESSION和COOKIE的关系已在另一篇文章里讨论过了)。<br />但COOKIE有个限制，就是"跨域"问题，即一个站点只能控制以它的根域为名的COOKIE数据，如WWW.W3C.COM则只能控制W3C.COM这个域的COOKIE，其他如GOOGLE.COM、microsoft.com等的它都管不了。<br /><br />当架设一个单点认证系统时，用户访问应用服务器A（AS.A），AS.A在自己的服务器上找不到有登记信息，则会导向到登录服务器（LS）为用户做登录认证，通过认证后，LS记录下用户的信息，并给AS.A发一个信息说"这个用户买票了"，AS.A就在自己的服务器上接收LS传过来的复本作登记了（则以后不用每次都向LS查询），同时回馈给用户一张"票根"即认证根据（TOKEN ID），再把用户导向回AS.A，用户就可以到AS.A上进行他的活动了。<br /><br />过了会用户在AS.A上玩厌了，想到隔壁的AS.B上看看时，AS.B也要问用户查看他的"票根"，这时问题来了。<br /><br />如果AS.A和AS.B是不同域的，一个是A.COM一个是B.COM，用户（USER）要访问AS.A则只要判断COOKIE里有没"票根"并根据这个"票根"来核对是不是他本人。<br />这个"票根"对AS.B也是通用的，只要向AS.B展示这个"票根"就行了。但问题正是，"票根"是在通过LS认证后，AS.A存放到A.COM的COOKIE里，而在访问AS.B时，AS.B是不能访问A.COM的COOKIE数据的，或者说它根本不知道A.COM的存在。<br />（注：A1.A.COM和A2.A.COM这样是同域的，都可以访问A.COM的COOKIE）<br /><br />所以USER在访问AS.B时又要得到LS做一次认证，这就违反了单点认证的本意。<br />这就是COOKIE的跨域限制问题。<br /><br /><br /><br />一个简陋的解决方案是在AS.A的每个连接上都追加一个TOKEN ID（类似&amp;TOKENID=asrw34rzx242），当用户顺着这个连接到AS.B时，AS.B就能根据这个贴在用户脑袋后面的"票根"来向LS做认证查询了，若在LS上有对应TOKEN ID的合法信息存在，则也保留一份到自己的服务器上备用，并且也有样学样在每个连接上贴上这个"票根"，好让用户顺着连接到别的"摊点"（AS.C、AS.D.....）上继续游玩。<br /><br />但这种这方法实现很简单，但有很多缺点，如要求所有认证系统内的页面都要贴上一段TOKEN ID，即不美观又麻烦，而且，当用户不是顺着页面的连接，而是新开一个窗口来访问AS.B时，取不到TOKEN ID的值，也需要重新到LS认证一次。<br />所以这种做法只适合简单的会话型或一条龙服务型的。<br /><br />还有一种做法是LS群发，即用户登录后，LS向所有在它名下所有登记的"摊点"都发一次通知，让他们自己做记录。如果用户无聊登录一下又马上退出又登录...，那LS就可能忙得不可开交了，接到通知的AS们也要跟着瞎闹个不停；又假设同时有100人登录，而园内有上100个摊点，LS就要发通知100X100次，真是一百遍又一百变；很有可能一个用户永远只去一个"摊点"，其他的一次都没有去过，而其他的也要为他做登记，已供侯用户的"突击访问"，这样就比较浪费资源和表情了。<br />所以这种做法也不是很妥当，适合园子比较小的：）<br />（后来认真想想也不行，就算每台服务器里都有登录信息了，但用户手上还是没有证明他身份的凭据：TOKEN ID，因此此方案否决！）<br /><br />一种比较可行的方案：<br />借用这里了一个很巧妙的思路（纯指思路，代码不成熟）<br />http://blog.csdn.net/liyujie2000/archive/2004/03/11/13511.aspx?Pending=true<br /><br />即在访问AS.B时，AS.B得不到TOKEN ID则不是主动向LS查询了，而是"悄悄"地引导客户到LS，再"悄悄地"由LS把客户的TOKEN ID帖到客户的脑后带回到AS.B，AS.B就可以根据根据带回来的TOKEN ID再向LS做一次验证（如此麻烦是为了防止欺骗，当然LS和AS.B之间的会话也要校验），这个过程是"瞒着"客户利用客户的浏览器（就是为了取回LS的COOKIE"票根"）悄悄进行的，用户体验上最多在第一次访问时比第一次登录AS.A时慢一点，因为多了一步AS.B向LS校验的过程，但总的来说是简单可行的。<br />（注1：因为每次合法认证都需经过LS，所以LS的COOKIE是一定存在的，若不存在呢？废话，提示他登录咯。所以LS的COOKIE是这个跨域方案的基础）<br /><br /><br />能不能不用COOKIE呢？<br />这跟客户端的身份识别技术有很大关联，如果能确认唯一客户端，那就可以不依靠COOKIE了，这种技术也有不少，但都不是很方便，如客户证书等。另外有人说取用户的网卡的MAC值，如果你能用"合法"的手段做到，那么我也没意见的。<br /><br /><br />还有COOKIE的一个"伪造"和"顶替"问题（COOKIES欺骗），所谓"伪造"那是知道服务器生成COOKIE的格式和算法，伪造者再重新生成的一个COOKIE，如SERVER A的COOKIE的结构为：USERID = XXX；USERPASD = ****；TOKEN=ASD34AF23SDF3；<br />TOKEN是根据USERID和USERPASD再加上服务器的私钥生成的一段摘要，<br />而伪造者只有知道它的私钥和TOKEN的具体算法才能用任意的USERID和USERPASD来伪造一个合法的COOKIE。<br />而顶替呢，则只需要得到这段"USERID = XXX；USERPASD = ****；TOKEN=ASD34AF23SDF3；"完整的数据，根本不需要知道服务器的私钥和TOKEN的算法，只需把这段COOKIE发给服务器，在SESSION ID的有效期内就能冒充这个COOKIE本身的所有者来与服务器进行会话。<br />有些人对这两个概念老是混淆不清就一昧地说COOKIE不安全、容易被窃取，那只是看到"顶替"这一方面，但若要随意伪造COOKIE却不会那么容易，如果你能随意得到服务器的私钥（就算TOKEN的算法是公开的），那只能说是那台服务器的管理有问题，你把银行帐号的密码写到本子上锁到柜子里，而柜子被小偷撬开，根据密码到柜员机取走了钱，难道可以说是银行不安全吗？<br /><br /><br />相关讨论连接(有些思路不是完全正确的，不要随便盲从)：<br />http://forum.javaeye.com/viewtopic.php?t=11655&amp;highlight=%BF%E7%D3%F2<br />http://forum.javaeye.com/viewtopic.php?t=11582&amp;highlight=%BF%E7%D3%F2<br />http://forum.javaeye.com/viewtopic.php?t=6473&amp;highlight=%BF%E7%D3%F2
          <br/><br/>
          <span style="color:red;">
            <a href="http://crazysoul.javaeye.com/blog/23674#comments" style="color:red;">已有 <strong>0</strong> 人发表留言，猛击-&gt;&gt;<strong>这里</strong>&lt;&lt;-参与讨论</a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">Windows7在微软WinHEC 2008上揭开神秘面纱</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 03 Jan 2006 05:03:41 +0800</pubDate>
        <link>http://crazysoul.javaeye.com/blog/23674</link>
        <guid>http://crazysoul.javaeye.com/blog/23674</guid>
      </item>
      </channel>
</rss>