`

SSO相关问题总结

阅读更多

1 概述
      对于单点登录的机制和原理就不在这里赘述了。本文仅是对于单点登录问题研究所得的心得进行一下总结。
想要实现单点登录可以采用的方式有很多种:
1、 利用成熟的软件框架(CAS,OPENSSO等)
2、自己建设单点登录框架(像sohu的单点登录)
3 、还有就是最简单的使用URL模拟登录。

      但是各种方式都各有利弊。最主要的问题是大部分实现方式都需要对单点登录目标系统进行修改,或者在目标系统中放入单点登录代码。如果我们自己对目标系统没有控制能力,那么与目标系统的沟通就成为最大的阻碍。
      如果可以不修改目标系统,或者可以不在目标系统放入代码就可以登录就是最理想的单点登录。基于这一问题,那么建立模仿登录请求的URL进行登录就成为相对较佳的方案。为什么说是“相对较佳”,因为利用URL进行登录,基本上都有页面的刷新或者是跳转。那么单点登录的设计将成为瓶颈,尤其是在想要设计成无刷新登录或者可以Session保持随时进入目标系统,将给系统带来更多的限制。

      实际上,近期主要是针对这一问题进行了大量的研究,采用了各种方法进行尝试。但是并没有得到实质性的成果,主要存在的问题是:浏览器基于安全考虑所采用的“同源策略”(Same Origin Policy)。

下面我分别根据所采用的方法进行阐述

 

2 跨域解决对策
2.1 Ajax
      Ajax异步发送请求是最先采用的方法。原有的单点登录方式,是在目标系统放置代码。通过URL访问该代码,在由该代码发送Ajax请求进行登录。
      因为该种方式,需要在目标系统放置代码,这样不能满足我们的要求,所以尝试将Ajax发送请求登录代码移到“源系统”(即具备单点登录功能的系统,就是我们的门户系统)。但是请求根本无法发出,浏览器直接就弹出提示框:“该页正在访问其控制范围之外的数据,这有些危险,是否继续"。Ajax本身实际上是通过XMLHttpRequest对象来进行数据的交互,而浏览器出于安全考虑,不允许js代码进行跨域操作,所以会警告。

      有一种方法,用服务器端的XmlHttpRequest代理实现跨域访问。
      我们不能在浏览器端直接使用AJAX来跨域访问资源,但是在服务器端是没有这种跨域安全限制的。所以,只需要让服务器端帮我们完成“跨域访问”的工作,然后在浏览器端用AJAX获取服务器端“跨域访问”的结果就可以了。这就是所谓的在服务器端创建一个XmlHttpRequest代理,通过这个代理来访问其他域名下的资源。这里引用Yahoo! JavaScript Developer Center上的几张图来进一步说明这个方案:

使用XmlHttpRequest访问同一域名下的资源:

使用XmlHttpRequest跨域访问资源:

用服务器端的XmlHttpRequest代理来跨域访问资源:


 编写服务器端XmlHttpRequest代理的具体过程就不赘述了,无非是创建一个自定义的HTTP请求。
 实际上,该方法并没有从本质上解决Ajax的跨域问题。

 

2.2 JavaScript
      比较常用的一种解决跨域的方法是用动态script标签实现客户端的跨域访问 。
      很明显,上一个方案必须要在服务器端做相应的改动才能实现跨域访问。但是很多时候是不能改动服务器端的源代码,那么上一个方案就不能满足需求了。

      我们应该能注意到,虽然浏览器有跨域访问的限制,但是我们是可以通过script标签远程引用其他域名下的脚本文件的。而且,script标签的src属性不一定必须是一个存在的js文件,也可以是一个http handler的url,只要这个http handler返回的是一个text/javascript类型的响应就可以了。

      这样,我们的第二个方案就浮出水面了。这个和上个的区别就是请求是使用<script>标签来请求的,这个要求也是两个域都是由你来开发才行。原理就是JS文件注入,在本域内的a内生成一个JS标签,它的SRC指向请求的另外一个域的某个页面b,b返回数据即可,可以直接返回JS的代码。因为script的src属性是可以跨域的。具体看代码,这个也比较简单。

 

2.3 Iframe
Iframe具体使用情况有:
一、本域和子域的相互访问: www.aa.com 和book.aa.com
二、本域和其他域的相互访问: www.aa.comwww.bb.com 用 iframe

      第一种情况如果想做到数据的交互,那么www.aa.com 和book.aa.com必须由你来开发才可以。可以将book.aa.com用iframe添加到www.aa.com 的某个页面下,在www.aa.com 和iframe里面都加上document.domain = "aa.com",这样就可以统一域了,可以实现跨域访问。就和平时同一个域中镶嵌iframe一样,直接调用里面的JS就可以了。(这个办法我没有尝试,不过理论可行,而且下面的跨子域Cookie方法似乎更好)

      第二种情况当两个域不同时,如果想相互调用,那么同样需要两个域都是由你来开发才可以。用iframe可以实现数据的互相调用。解决方案就是用window.location对象的hash属性。hash属性就是http://domian/web/a.htm#dshakjdhsjka 里面的#dshakjdhsjka。利用JS改变hash值网页不会刷新,可以这样实现通过JS访问hash值来做到通信。不过除了IE之外其他大部分浏览器只要改变hash就会记录历史,你在前进和后退时就需要处理,非常麻烦。不过再做简单的处理时还是可以用的,具体的代码我再下面有下载。大体的过程是页面a和页面b在不同域下,b通过iframe添加到a里,a通过JS修改iframe的hash值,b里面做一个监听(因为JS只能修改hash,数据是否改变只能由b自己来判断),检测到b的hash值被修改了,得到修改的值,经过处理返回a需要的值,再来修改a的hash值(这个地方要注意,如果a本身是那种查询页面的话比如http://domian/web/a.aspx?id=3 ,在b中直接parent.window.location是无法取得数据的,同样报没有权限的错误,需要a把这个传过来,所以也比较麻烦),同样a里面也要做监听,如果hash变化的话就取得返回的数据,再做相应的处理。

 

2.4 HttpClient
      下面所采用的办法是利用Apache的HttpClient进行登录。
      通常,我们使用A系统中的URL进行单点登录的流程是这样的。首先创建模拟A系统中登录表单提交的URL进行登录(我们把这个URL叫做URL1)。如果登录成功的话,用A系统的正常访问URL访问该网站就可以看到已经是登录状态(我们把这个URL叫做URL2)。
      其原理就是HTTP协议的原理,在我们利用URL1进行访问以后,服务器就会为该用户创建一个Session,并在响应中设置“Set-Cookie”头信息,信息中包含对应的SessionID。浏览器就会根据该信息在客户端生成Cookie。当我们再访问URL2时,浏览器就会判断该会话中是否已创建Cookie,如果已经创建就会在请求中添加Cookie头信息,信息中包含对应的SessionID。服务器在处理消息时判断SessionID是否相同,相同就认为是同一会话,同一个人。
这就是现在解决HTTP协议无状态的办法。但是这个前提是在使用同一个浏览器。就像现在的IE7,虽然可能会打开多个Tab页,但是还是同一个浏览器。

      利用HttpClient确实可以模拟发送请求,登录进入目标系统。但是HttpClient的原理是每建立一个链接,相当于新打开一个浏览器。那么按照上面所说流程分别访问URL1和URL2将会被模拟成打开两个浏览器,换句话说在访问URL2时,就不会带上Cookie的消息头,服务器就会认为不是同一个会话,也就不会模拟出成功的登录状态。

根据上面的描述,解决问题的症结就在于:在发送访问URL2的请求时可以带上URL1返回的Cookie信息即可。

想要在访问URL2的时候带上Cookie头信息,有以下几个办法
1、 在访问URL1之后,在客户端创建Cookie
2、 在访问URL2时,在消息头中加入Cookie头信息。
3、 在访问URL2时,进行URL重写,在其后加入Cookie头中的信息。

      通常情况下,以上三种操作都不需要我们自己做,完全可以由服务器和系统自动完成。但是我们所要处理的情况是在B系统中,通过访问A系统的URL1和URL2登录到A系统中,这样就会产生问题。

 

2.5 跨域Cookie
1、从B系统中访问A系统的URL1,出于同源策略的安全考虑,浏览器会禁止A系统生成Cookie。
2、很不幸的是J2EE并没有提供在请求头中加入Cookie头的方法。
3、该方法是唯一可行的,但是如果B系统引用A系统的类似于URL2的URL过多,URL重写是一块加到的工作了。
      总结的来说,就是因为同源策略导致不能共享Cookie,也因此不能维持Session的联通。但是同源策略是支持跨子域Cookie的。

 

2.6 跨子域Cookie
      所谓跨子域登录,A,B站点和P站点位于同一个域下面,比如A站点为http://blog.yizhu2000.com ,B站点为 http://forum.yizhu2000.com ,他们和登录站点P的关系可以看到,都是属于同一个父域,yizhu2000.com,不同的是子域不同,一个为blog,一个为forum,一个是passport我们先看看最常用的非跨站点普通登录的情况,一般登录验证通过后,一般会将你的用户名和一些用户信息,通过某一密钥进行加密,写在本地,也就是一个加密的cookie,我们把这个cookie叫做--票(ticket)。

      需要判断用户是否登录的页面,需要读取这个ticket,并从其中解密出用户信息,如果ticket不存在,或者无法解密,意味着用户没有登录,或者登录信息不正确,这时就要跳转到登录页面进行登录,在这里加密的作用有两个,一是防止用户信息被不怀好意者看到,二是保证ticket不会被伪造,后者其实更为重要,加密后,各个应用需要采用与加密同样的密钥进行解密,如果不知道密钥,就不能伪造出ticket,(注:加密和解密的密钥有可能不同,取决于采用什么加密算法,如果是对称加密,则为同一密钥,如果是非对称,就不同了,一般用私钥加密,公钥解密,但是无论怎样,密钥都只有内部知道,这样伪造者既无法伪造也无法解密ticket)

      跨子域的单点登录,和上述普通登录的过程没有什么不同,唯一不同的是写cookie时,由于登录站点P和应用A处于不同的子域,P站写入的cookie的域为passport.yizhu2000.net,而A站点为forum.yizhu2000.net,A在判断用户登录时无法读到P站点的ticket

      解决方法非常简单,当Login完成后P站点写ticket的时候,只需把cookie的域设为他们共同的父域,yizhu2000.net就可以了:cookie.domain="yizhu2000.net",A站点自然就可以读到这个ticket了

 

2.7 P3P
      在网上查过资料,有一种方法是可以做到不同域的Cookie设置,其方法就是在响应头中加入P3P头信息。设置方法举例如下:

Step1:
      首先在hosts文件中设置(其中的192.168.73.1为您本机的ip,写成127.0.0.1不行。) 只是举例在真正服务器上不需要这样。
192.168.73.1 www.a.com
192.168.73.1 www.b.com

Step2:编写文件 b_setcookie.jsp

view plaincopy to clipboardprint?
<%@ page contentType="text/html; charset=utf-8" %>  
<%      
 response.addHeader("Cache-Control", "no-cache"); 
 response.addHeader("Expires", "Thu, 01 Jan 1970 00:00:01 GMT"); 
 String ssocookie="www.sso12345678910.com ";   
%>  
<mce:script src="http://www.a.com/mp/test/a_setcookie.jsp?id=<%=ssocookie% ><!-- 
">  
// --></mce:script> 
Step3:编写文件 a_setcookie.jsp
view plaincopy to clipboardprint?
<%  
 response.setHeader("P3P","CP=\"CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\"");  
 String domainId=request.getParameter("id");  
 Cookie _cookie=new Cookie("test",domainId);  
 _cookie.setMaxAge(30*60*100); 
 _cookie.setPath("/"); 
   response.addCookie(_cookie); 
%> 

Step4:编写文件 a_getcookie.jsp
view plaincopy to clipboardprint?
<%@ page contentType="text/html; charset=utf-8" %>  
<%     
 Cookie cookies[]=request.getCookies();Cookie sCookie=null;      
 String sname=null;     
 String name=null;     
 if(cookies==null) // 如果没有任何cookie       
  out.print("none any cookie"); 
 else {       
  out.print(cookies.length + "<br>");       
  for(int i=0;i<cookies.length; i++) {         
  sCookie=cookies[i];  
  sname=sCookie.getName();         
  name = sCookie.getValue();     
  out.println("comment==>>>"+sCookie.getComment()+"\n"); 
  out.println("getDomain==>>>"+sCookie.getDomain()+"\n");       
  out.println("getSecure==>>"+sCookie.getSecure()+"\n");         
  out.println("getVersion==>>"+sCookie.getVersion()+"\n");         
  out.println("cookiename==>>"+sname + "->" + "cookievalue==>>>"+name + "<br>"); 
 }     
 }   
%> 

Step5:测试
依次请求
http://www.b.com/mp/test/b_setcookie.jsp
http://www.a.com/mp/test/a_getcookie.jsp
便可看到通过跨域设置的cookie的值!
      这种方法虽然可以实现跨域设置Cookie,但还是需要在目标系统进行代码的注入。

 

3 题外话
       实际上水晶易表Flash不能跨域访问WebService获取数角也是因为类似于同源策略的安全沙盒问题引起。
 对于安全沙盒问题,倒是也有相应的解决办法。
       如果想要在Flash里面跨域获取数据,就必须在对方server上配置crossdomain.xml。具体来说,比如你的Flash在domain A下面,而你想要访问domain B暴露的web service,那么domain B的server根目录下必须要有一个crossdomain.xml文件来配置说你有这个权限。这个是Flash Player的安全限制。
       对于Flash Player 9之前的版本,这个crossdomain.xml文件大概如下:

view plaincopy to clipboardprint?
<?xml version="1.0" encoding="UTF-8"?>   
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd " >   
<cross-domain-policy>   
    <allow-access-from domain="*" secure="true" />   
</cross-domain-policy> 

 
      以上配置允许所有domain访问当前server所暴露的数据(比如web service)。你可以在domain属性里面指定特殊的规则。secure属性用来设置你所暴露的数据是否走https协议。
      但是对于Flash Player 9而言,crossdomain.xml文件内容出现了较大的变化,原因是Flash Player 9的security机制有所改变。所以当我用Flex 3调用cross domain的web service时,还使用上面的crossdomain.xml文件,结果就报错说security error。于是稍微研究了一下,得到如下解决方案,其实就是要改变crossdomain.xml的内容:
view plaincopy to clipboardprint?
<?xml version="1.0" encoding="UTF-8"?>   
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd " >   
<cross-domain-policy>   
    <site-control permitted-cross-domain-policies="all" />   
    <allow-access-from domain="*" />   
    <allow-http-request-headers-from domain="*" headers="*"/>   
</cross-domain-policy> 

     以上是Flash Player 9所要求的crossdomain.xml的内容。可以看到多了两个tag。其中site-control是可选的,但是allow-http-request-headers-from对于cross domain的web service确实必须的。如果没有允许header,就会像我之前一样报错。这些配置项的具体含义以及其他可选配置项,可以参考http://www.adobe.com/devnet/flashplayer/articles/flash_player_9_security.pdf
      当然在生成的Flash当中,需要有代码来调用相应的crossdomain.xml。但是水晶易表所导出的Flash当中,并不包含该代码。

 

分享到:
评论

相关推荐

    sso解决方案汇总

    单点登录(Single Sign-On,简称SSO)是一种流行的认证技术,它允许用户在一个系统上登录后,无需再次输入凭证即可访问多个相关的应用系统。这种技术不仅提升了用户体验,还极大地提高了系统的安全性和管理效率。...

    Laravel开发-sso

    **Laravel 开发中的 SSO 实现** 在 Laravel 框架中,Single Sign-On (SSO) 是一种用户身份验证方法,允许用户在一个应用系统中登录后,无需再次输入凭证即可访问其他关联的应用系统。这提高了用户体验,尤其是在...

    sso servert端 注销

    总结起来,实现SSO服务器端的注销功能是一个涉及会话管理、票据控制、跨域通信、事件通知等多个方面的复杂过程。通过理解这些关键技术点,并结合Java编程,我们可以构建一个安全、高效且易于扩展的SSO系统。

    Redis实现SSO

    总结一下,本项目通过Java和Redis展示了SSO的基本实现方式,涉及到的主要知识点有: 1. Redis的安装和配置,包括默认密码的修改。 2. 使用Java的Redis客户端库(如Jedis)进行数据操作,如设置、获取和过期票据。 3....

    SSO概念pdf

    #### 五、总结 SSO作为一种高效的认证机制,极大地提升了用户体验,并简化了多系统间的登录流程。通过对SSO体系结构和认证流程的深入了解,我们可以更好地理解和应用这项技术,以满足不同场景下的需求。无论是对于...

    sso_prog_guide_5_5.pdf

    文档还定义了与vCenter SSO交互相关的数据结构,如: - RequestSecurityTokenType:用于请求安全令牌的类型。 - RequestSecurityTokenResponseCollectionType:安全令牌响应的集合类型。 - ...

    sso-use-cookie单点登录

    总结,"sso-use-cookie"项目通过SpringBoot和Cookie实现了单点登录功能,适合有一定Spring基础的学习者了解SSO的基本原理和实践。通过深入学习和理解这个项目,开发者能够掌握如何在实际项目中应用SSO,提升系统的...

    CAS--SSO单点用到的jar包

    总结一下,CAS-SSO的实现依赖于如cas-client-core.jar这样的核心组件,以及如commons-lang和SLF4J这样的辅助库,它们共同构成了SSO系统的关键部分,使开发者能够轻松地在Java应用中集成SSO功能。通过理解这些库的...

    DOMINO SSO 单点登录

    2. **配置WebSSO**: 在WebSSO配置文件中设置SSO相关的参数,如认证源、SSO服务URL等。 3. **测试WebSSO**: 完成配置后,进行WebSSO的功能测试,确保用户可以正常登录并访问受保护资源。 #### 四、SSO实现过程 #####...

    Infor EAM SSO(单点登录)手册

    根据提供的文件信息,以下是对Infor EAM SSO(单点登录)手册涉及的重要知识点进行的详细解析: ### 一、Infor EAM SSO(单点登录)概述 Infor EAM SSO(单点登录)是Infor公司提供的一种解决方案,用于实现企业级...

    跨服务器登录验证(单点登录SSO)过程和Java实现

    4. **配置过滤器**:配置Filter以处理SSO相关请求,如CasAuthenticationFilter负责处理从SSO中心返回的ST。 5. **服务验证**:实现ServiceTicketValidator接口,用于验证从SSO中心接收到的服务票证。 6. **自定义...

    sso的原理与java实现

    SSO(Single Sign-On)是单点登录的缩写,是一种网络用户身份验证的机制,允许用户在一次登录后访问多个应用系统而无需再次输入凭证。这种技术在大型企业或组织中广泛使用,以提高用户体验并增强安全性。本文将深入...

    SSO单点登录客户端

    下面将详细阐述SSO单点登录客户端的相关知识点。 1. SSO原理: SSO的核心在于共享用户认证信息。当用户在SSO环境中首次登录一个应用(称为服务提供者或SP)时,会通过身份验证服务器(称为身份提供者或IDP)进行...

    SSO分布式系统单点登陆入门到基础到原理实战

    ### SSO分布式系统单点登录入门到基础到原理实战 #### 一、SSO(Single Sign-On)概念 SSO,即单点登录(Single Sign-On),是一种基于Web的应用程序认证处理方法。用户只需进行一次身份验证即可访问多个应用系统。...

    TIM和webseal的sso Portal与webseal的sso

    根据提供的标题、描述、标签及部分内容,本文将详细介绍IBM Tivoli身份管理(TIM)与WebSEAL单点登录(SSO)的相关配置方法,并进一步解释Portal与WebSEAL的SSO配置过程。以下内容旨在帮助读者理解并实现这些配置。 #...

    SSO单点登录

    ### 总结 SSO单点登录作为一种高效的身份验证机制,可以显著提升用户体验,并简化多系统间的认证管理。无论是通过CAS框架还是其他自定义实现方式,SSO都能够在保证安全性的同时,提高整个系统的可用性和便捷性。

    为PHPBB加一个oauth认证方式统一登陆SSO

    ### 为PHPBB添加OAuth2认证方式实现统一登录SSO #### 一、背景与目的 随着互联网技术的发展,统一登录系统(Single Sign-On, SSO)已成为网站间资源共享的重要手段之一。它允许用户在一次登录后即可访问多个关联的...

    Speed2000_同步开关分析SSO.rar

    下面我们将深入探讨同步开关分析SSO的相关知识点,并结合Speed2000系统进行详细讲解。 1. 同步开关基础 同步开关主要指的是在数字系统中,开关操作与系统时钟同步进行,以减少电源噪声和提高效率。这种技术广泛应用...

    ofbiz sso 单点登录

    总结来说,ofbiz 的 SSO 实现是通过集成 CAS 和 LDAP 来提供统一的登录体验。配置和管理 SSO 需要对 LDAP、CAS 以及 ofbiz 的组件配置有深入理解。通过定制和优化,可以根据不同的业务需求调整 SSO 的行为,例如...

    基于webspere实现的smal单点sso

    单点登录(Single Sign-On,SSO)是一种身份验证机制,允许用户通过一次登录即可访问多个相关但独立的应用程序。这种机制不仅提高了用户体验,还增强了系统的安全性。在实现SSO的过程中,Security Assertion Markup ...

Global site tag (gtag.js) - Google Analytics