单点登陆入门

什么是单点登陆

单点登录( Single Sign On ),简称为  SSO ,是目前比较流行的企业业务整合的解决方案之一。 SSO 的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

较大的企业内部,一般都有很多的业务支持系统为其提供相应的管理和IT服 务。例如财务系统为财务人员提供财务的管理、计算和报表服务;人事系统为人事部门提供全公司人员的维护服务;各种业务系统为公司内部不同的业务提供不同的 服务等等。这些系统的目的都是让计算机来进行复杂繁琐的计算工作,来替代人力的手工劳动,提高工作效率和质量。这些不同的系统往往是在不同的时期建设起来 的,运行在不同的平台上;也许是由不同厂商开发,使用了各种不同的技术和标准。

2 单点登录实现原理

实现单点登录说到底就是要解决如何产生和存储那个信任,再就是其他系统如何验证这个信任的有效性,因此要点也就以下几个:

  • 存储信任
  • 验证信任

只要解决了以上的问题,达到了开头讲得效果就可以说是SSO。最简单实现SSO的方法就是用Cookie,实现流程如下所示:


对于第一个问题一般都是通过加密Cookie来处理,第二个问题是硬伤,其实这种方案的思路的就是要把这个信任关系存储在客户端,要实现这个也不一定只能用Cookie,用flash也能解决,flash的Shared Object API就提供了存储能力。


一般说来,大型系统会采取在服务端存储信任关系的做法,实现流程如下所示:

以上方案就是要把信任关系存储在单独的SSO系统(暂且这么称呼它)里,说起来只是简单地从客户端移到了服务端,但其中几个问题需要重点解决:

  • 如何高效存储大量临时性的信任数据
  • 如何防止信息传递过程被篡改
  • 如何让SSO系统信任登录系统和免登系统
3 WEB-SSO的实现
随着互联网的高速发展, WEB 应用几乎统治了绝大部分的软件应用系统,因此 WEB-SSO SSO 应用当中最为流行。 WEB-SSO 有其自身的特点和优势,实现起来比较简单易用。很多商业软件和开源软件都有对 WEB-SSO 的实现。其中值得一提的是 OpenSSO  https://opensso.dev.java.net ),为用 Java 实现 WEB-SSO 提供架构指南和服务指南,为用户自己来实现 WEB-SSO 提供了理论的依据和实现的方法。
为什么说 WEB-SSO 比较容易实现呢?这是有 WEB 应用自身的特点决定的。
众所周知, Web 协议(也就是 HTTP )是一个无状态的协议。一个 Web 应用由很多个 Web 页面组成,每个页面都有唯一的 URL 来定义。用户在浏览器的地址栏输入页面的 URL ,浏览器就会向 Web Server 去发送请求。如下图,浏览器向 Web 服务器发送了两个请求,申请了两个页面。这两个页面的请求是分别使用了两个单独的 HTTP 连接。所谓无状态的协议也就是表现在这里,浏览器和 Web 服务器会在第一个请求完成以后关闭连接通道,在第二个请求的时候重新建立连接。 Web 服务器并不区分哪个请求来自哪个客户端,对所有的请求都一视同仁,都是单独的连接。这样的方式大大区别于传统的( Client/Server C/S 结构 , 在那样的应用中,客户端和服务器端会建立一个长时间的专用的连接通道。正是因为有了无状态的特性,每个连接资源能够很快被其他客户端所重用,一台 Web 服务器才能够同时服务于成千上万的客户端。
但是我们通常的应用是有状态的。先不用提不同应用之间的 SSO ,在同一个应用中也需要保存用户的登录身份信息。例如用户在访问页面 1 的时候进行了登录,但是刚才也提到,客户端的每个请求都是单独的连接,当客户再次访问页面 2 的时候,如何才能告诉 Web 服务器,客户刚才已经登录过了呢?浏览器和服务器之间有约定:通过使用 cookie 技术来维护应用的状态。 Cookie 是可以被 Web 服务器设置的字符串,并且可以保存在浏览器中。如下图所示,当浏览器访问了页面 1 时, web 服务器设置了一个 cookie ,并将这个 cookie 和页面 1 一起返回给浏览器,浏览器接到 cookie 之后,就会保存起来,在它访问页面 2 的时候会把这个 cookie 也带上, Web 服务器接到请求时也能读出 cookie 的值,根据 cookie 值的内容就可以判断和恢复一些用户的信息状态。
Web-SSO 完全可以利用 Cookie 结束来完成用户登录信息的保存,将浏览器中的 Cookie 和上文中的 Ticket 结合起来,完成 SSO 的功能。
 
为了完成一个简单的 SSO 的功能,需要两个部分的合作:
  1. 统一的身份认证服务。
  2. 修改Web应用,使得每个应用都通过这个统一的认证服务来进行身份效验。
3.1 Web SSO 的样例
根据上面的原理,我用 J2EE 的技术( JSP Servlet )完成了一个具有 Web-SSO 的简单样例。样例包含一个身份认证的服务器和两个简单的 Web 应用,使得这两个  Web 应用通过统一的身份认证服务来完成 Web-SSO 的功能。此样例所有的源代码和二进制代码都可以从网站地址 http://gceclub.sun.com.cn/wangyu/  下载。
 
样例下载、安装部署和运行指南:
  • Web-SSO的样例是由三个标准Web应用组成,压缩成三个zip文件,从http://gceclub.sun.com.cn/wangyu/web-sso/中下载。其中SSOAuthhttp://gceclub.sun.com.cn/wangyu/web-sso/SSOAuth.zip)是身份认证服务;SSOWebDemo1http://gceclub.sun.com.cn/wangyu/web-sso/SSOWebDemo1.zip)和SSOWebDemo2http://gceclub.sun.com.cn/wangyu/web-sso/SSOWebDemo2.zip)是两个用来演示单点登录的Web应用。这三个Web应用之所以没有打成war包,是因为它们不能直接部署,根据读者的部署环境需要作出小小的修改。样例部署和运行的环境有一定的要求,需要符合Servlet2.3以上标准的J2EE容器才能运行(例如Tomcat5,Sun Application Server 8, Jboss 4等)。另外,身份认证服务需要JDK1.5的运行环境。之所以要用JDK1.5是因为笔者使用了一个线程安全的高性能的Java集合类“ConcurrentMap”,只有在JDK1.5中才有。
  • 这三个Web应用完全可以单独部署,它们可以分别部署在不同的机器,不同的操作系统和不同的J2EE的产品上,它们完全是标准的和平台无关的应用。但是有一个限制,那两台部署应用(demo1demo2)的机器的域名需要相同,这在后面的章节中会解释到cookiedomain的关系以及如何制作跨域的WEB-SSO
  • 解压缩SSOAuth.zip文件,在/WEB-INF/下的web.xml中请修改“domainname”的属性以反映实际的应用部署情况,domainname需要设置为两个单点登录的应用(demo1demo2)所属的域名。这个domainname和当前SSOAuth服务部署的机器的域名没有关系。我缺省设置的是“.sun.com”。如果你部署demo1demo2的机器没有域名,请输入IP地址或主机名(如localhost),但是如果使用IP地址或主机名也就意味着demo1demo2需要部署到一台机器上了。设置完后,根据你所选择的J2EE容器,可能需要将SSOAuth这个目录压缩打包成war文件。用“jar -cvf SSOAuth.war SSOAuth/”就可以完成这个功能。
  • 解压缩SSOWebDemo1SSOWebDemo2文件,分别在它们/WEB-INF/下找到web.xml文件,请修改其中的几个初始化参数
    <init-param>
    <param-name>SSOServiceURL</param-name>
    <param-value>http://wangyu.prc.sun.com:8080/SSOAuth/SSOAuth</param-value>
    </init-param>
    <init-param>
    <param-name>SSOLoginPage</param-name>
    <param-value>http://wangyu.prc.sun.com:8080/SSOAuth/login.jsp</param-value>
    </init-param>
    将其中的SSOServiceURLSSOLoginPage修改成部署SSOAuth应用的机器名、端口号以及根路径(缺省是SSOAuth)以反映实际的部署情况。设置完后,根据你所选择的J2EE容器,可能需要将SSOWebDemo1SSOWebDemo2这两个目录压缩打包成两个war文件。用“jar -cvf SSOWebDemo1.war SSOWebDemo1/”就可以完成这个功能。
  • 请输入第一个web应用的测试URLtest.jsp,例如http://wangyu.prc.sun.com:8080/ SSOWebDemo1/test.jsp,如果是第一次访问,便会自动跳转到登录界面,如下图

  • 使用系统自带的三个帐号之一登录(例如,用户名:wangyu,密码:wangyu),便能成功的看到test.jsp的内容:显示当前用户名和欢迎信息。
  • 请接着在同一个浏览器中输入第二个web应用的测试URLtest.jsp,例如http://wangyu.prc.sun.com:8080/ SSOWebDemo2/test.jsp。你会发现,不需要再次登录就能看到test.jsp的内容,同样是显示当前用户名和欢迎信息,而且欢迎信息中明确的显示当前的应用名称(demo2)。
             
3.2 WEB-SSO代码讲解
3.2.1身份认证服务代码解析
Web-SSO 的源代码可以从网站地址 http://gceclub.sun.com.cn/wangyu/web-sso/websso_src.zip 下载。身份认证服务是一个标准的 web 应用,包括一个名为 SSOAuth Servlet ,一个 login.jsp 文件和一个 failed.html 。身份认证的所有服务几乎都由 SSOAuth Servlet 来实现了; login.jsp 用来显示登录的页面(如果发现用户还没有登录过); failed.html 是用来显示登录失败的信息(如果用户的用户名和密码与信息数据库中的不一样)。
SSOAuth 的代码如下面的列表显示,结构非常简单,先看看这个 Servlet 的主体部分
package DesktopSSO;
 
import java.io.*;
import java.net.*;
import java.text.*;
import java.util.*;
import java.util.concurrent.*;
 
import javax.servlet.*;
import javax.servlet.http.*;
 
 
public class SSOAuth extends HttpServlet {
   
     static private ConcurrentMap accounts;
     static private ConcurrentMap SSOIDs;
     String cookiename="WangYuDesktopSSOID";
     String domainname;
   
     public void init(ServletConfig config) throws ServletException {
         super.init(config);
         domainname= config.getInitParameter("domainname");
         cookiename = config.getInitParameter("cookiename");
         SSOIDs = new ConcurrentHashMap();
         accounts=new ConcurrentHashMap();
         accounts.put("wangyu", "wangyu");
         accounts.put("paul", "paul");
         accounts.put("carol", "carol");
     }
 
     protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
         PrintWriter out = response.getWriter();
         String action = request.getParameter("action");
         String result="failed";
         if (action==null) {
             handlerFromLogin(request,response);
         } else if (action.equals("authcookie")){
             String myCookie = request.getParameter("cookiename");
             if (myCookie != null) result = authCookie(myCookie);
             out.print(result);
             out.close();
         } else if (action.equals("authuser")) {
            result=authNameAndPasswd(request,response);
             out.print(result);
             out.close();
         } else if (action.equals("logout")) {
             String myCookie = request.getParameter("cookiename");
             logout(myCookie);
             out.close();
         }
     }
 
.....
 
}
 
从代码很容易看出, SSOAuth 就是一个简单的 Servlet 。其中有两个静态成员变量: accounts SSOIDs ,这两个成员变量都使用了 JDK1.5 中线程安全的 MAP 类: ConcurrentMap ,所以这个样例一定要 JDK1.5 才能运行。 Accounts 用来存放用户的用户名和密码,在 init() 的方法中可以看到我给系统添加了三个合法的用户。在实际应用中, accounts 应该是去数据库中或 LDAP 中获得,为了简单起见,在本样例中我使用了 ConcurrentMap 在内存中用程序创建了三个用户。而 SSOIDs 保存了在用户成功的登录后所产生的 cookie 和用户名的对应关系。它的功能显而易见:当用户成功登录以后,再次访问别的系统,为了鉴别这个用户请求所带的 cookie 的有效性,需要到 SSOIDs 中检查这样的映射关系是否存在。
 
在主要的请求处理方法 processRequest() 中,可以很清楚的看到 SSOAuth 的所有功能
  1. 如果用户还没有登录过,是第一次登录本系统,会被跳转到login.jsp页面(在后面会解释如何跳转)。用户在提供了用户名和密码以后,就会用handlerFromLogin()这个方法来验证。
  2. 如果用户已经登录过本系统,再访问别的应用的时候,是不需要再次登录的。因为浏览器会将第一次登录时产生的cookie和请求一起发送。效验cookie的有效性是SSOAuth的主要功能之一。
  3. SSOAuth还能直接效验非login.jsp页面过来的用户名和密码的效验请求。这个功能是用于非web应用的SSO,这在后面的桌面SSO中会用到。
  4. SSOAuth还提供logout服务。
 
下面看看几个主要的功能函数:
  private void handlerFromLogin(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
         String username = request.getParameter("username");
         String password = request.getParameter("password");
         String pass = (String)accounts.get(username);
         if ((pass==null)||(!pass.equals(password)))
             getServletContext().getRequestDispatcher("/failed.html").forward(request, response);
         else {
             String gotoURL = request.getParameter("goto");
             String newID = createUID();
             SSOIDs.put(newID, username);
             Cookie wangyu = new Cookie(cookiename, newID);
             wangyu.setDomain(domainname);
             wangyu.setMaxAge(60000);
             wangyu.setValue(newID);
             wangyu.setPath("/");
             response.addCookie(wangyu);
             System.out.println("login success, goto back url:" + gotoURL);
             if (gotoURL != null) {
                 PrintWriter out = response.getWriter();
                 response.sendRedirect(gotoURL);
                 out.close();
             }
         }  
     }
handlerFromLogin() 这个方法是用来处理来自 login.jsp 的登录请求。它的逻辑很简单:将用户输入的用户名和密码与预先设定好的用户集合(存放在 accounts 中)相比较,如果用户名或密码不匹配的话,则返回登录失败的页面( failed.html ),如果登录成功的话,需要为用户当前的 session 创建一个新的 ID ,并将这个 ID 和用户名的映射关系存放到 SSOIDs 中,最后还要将这个 ID 设置为浏览器能够保存的 cookie 值。
登录成功后,浏览器会到哪个页面呢?那我们回顾一下我们是如何使用身份认证服务的。一般来说我们不会直接访问身份服务的任何 URL ,包括 login.jsp 。身份服务是用来保护其他应用服务的,用户一般在访问一个受 SSOAuth 保护的 Web 应用的某个 URL 时,当前这个应用会发现当前的用户还没有登录,便强制将也页面转向 SSOAuth login.jsp ,让用户登录。如果登录成功后,应该自动的将用户的浏览器指向用户最初想访问的那个 URL 。在 handlerFromLogin() 这个方法中,我们通过接收 goto” 这个参数来保存用户最初访问的 URL ,成功后便重新定向到这个页面中。
另外一个要说明的是,在设置 cookie 的时候,我使用了一个setMaxAge(6000) 的方法。这个方法是用来设置 cookie 的有效期,单位是秒。如果不使用这个方法或者参数为负数的话,当浏览器关闭的时候,这个 cookie 就失效了。在这里我给了很大的值( 1000 分钟),导致的行为是:当你关闭浏览器(或者关机),下次再打开浏览器访问刚才的应用,只要在 1000 分钟之内,就不需要再登录了。我这样做是下面要介绍的桌面 SSO 中所需要的功能。
其他的方法更加简单,这里就不多解释了。
 
3.2.2具有SSO功能的web应用源代码解析
要实现 WEB-SSO 的功能,只有身份认证服务是不够的。这点很显然,要想使多个应用具有单点登录的功能,还需要每个应用本身的配合:将自己的身份认证的服务交给一个统一的身份认证服务- SSOAuth SSOAuth 服务中提供的各个方法就是供每个加入 SSO Web 应用来调用的。
一般来说, Web 应用需要 SSO 的功能,应该通过以下的交互过程来调用身份认证服务的提供的认证服务:
  • Web应用中每一个需要安全保护的URL在访问以前,都需要进行安全检查,如果发现没有登录(没有发现认证之后所带的cookie),就重新定向到SSOAuth中的login.jsp进行登录。
  • 登录成功后,系统会自动给你的浏览器设置cookie,证明你已经登录过了。
  • 当你再访问这个应用的需要保护的URL的时候,系统还是要进行安全检查的,但是这次系统能够发现相应的cookie
  • 有了这个cookie,还不能证明你就一定有权限访问。因为有可能你已经logout,或者cookie已经过期了,或者身份认证服务重起过,这些情况下,你的cookie都可能无效。应用系统拿到这个cookie,还需要调用身份认证的服务,来判断cookie时候真的有效,以及当前的cookie对应的用户是谁。
  • 如果cookie效验成功,就允许用户访问当前请求的资源。
以上这些功能,可以用很多方法来实现:
  • 在每个被访问的资源中(JSPServlet)中都加入身份认证的服务,来获得cookie,并且判断当前用户是否登录过。不过这个笨方法没有人会用:-)
  • 可以通过一个controller,将所有的功能都写到一个servlet中,然后在URL映射的时候,映射到所有需要保护的URL集合中(例如*.jsp/security/*等)。这个方法可以使用,不过,它的缺点是不能重用。在每个应用中都要部署一个相同的servlet
  • Filter是比较好的方法。符合Servlet2.3以上的J2EE容器就具有部署filter的功能。(Filter的使用可以参考JavaWolrd的文章http://www.javaworld.com/javaworld/jw-06-2001/jw-0622-filters.htmlFilter是一个具有很好的模块化,可重用的编程API,用在SSO正合适不过。本样例就是使用一个filter来完成以上的功能。
 
package SSO;
 
import java.io.*;
import java.net.*;
import java.util.*;
import java.text.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.*;
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.methods.GetMethod;
 
public class SSOFilter implements Filter {
     private FilterConfig filterConfig = null;
     private String cookieName="WangYuDesktopSSOID";
     private String SSOServiceURL= "http://wangyu.prc.sun.com:8080/SSOAuth/SSOAuth";
     private String SSOLoginPage= "http://wangyu.prc.sun.com:8080/SSOAuth/login.jsp";
   
     public void init(FilterConfig filterConfig) {
 
         this.filterConfig = filterConfig;
         if (filterConfig != null) {
             if (debug) {
                 log("SSOFilter:Initializing filter");
             }
         }       
         cookieName = filterConfig.getInitParameter("cookieName");
         SSOServiceURL = filterConfig.getInitParameter("SSOServiceURL");
         SSOLoginPage = filterConfig.getInitParameter("SSOLoginPage");
    
.....
.....
 
}
以上的初始化的源代码有两点需要说明:一是有两个需要配置的参数 SSOServiceURL SSOLoginPage 。因为当前的 Web 应用很可能和身份认证服务( SSOAuth )不在同一台机器上,所以需要让这个 filter 知道身份认证服务部署的 URL ,这样才能去调用它的服务。另外一点就是由于身份认证的服务调用是要通过 http 协议来调用的(在本样例中是这样设计的,读者完全可以设计自己的身份服务,使用别的调用协议,如 RMI SOAP 等等),所有笔者引用了 apache commons 工具包(详细信息情访问 apache  的网站 http://jakarta.apache.org/commons/index.html ),其中的 httpclient” 可以大大简化 http 调用的编程。
下面看看 filter 的主体方法 doFilter():
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
         if (debug) log("SSOFilter:doFilter()");
         HttpServletRequest request = (HttpServletRequest) req;
         HttpServletResponse response = (HttpServletResponse) res;
         String result="failed";
         String url = request.getRequestURL().toString();
         String qstring = request.getQueryString();
         if (qstring == null) qstring ="";
 
         // 检查 http 请求的 head 是否有需要的 cookie
         String cookieValue ="";
         javax.servlet.http.Cookie[] diskCookies = request.getCookies();
         if (diskCookies != null) {
             for (int i = 0; i < diskCookies.length; i++) {
                 if(diskCookies[i].getName().equals(cookieName)){
                     cookieValue = diskCookies[i].getValue();
 
                     // 如果找到了相应的 cookie 则效验其有效性
                     result = SSOService(cookieValue);
                     if (debug) log("found cookies!");
                 }
             }
         }
         if (result.equals("failed")) { // 效验失败或没有找到 cookie ,则需要登录
             response.sendRedirect(SSOLoginPage+"?goto="+url);
         } else if (qstring.indexOf("logout") > 1) {//logout 服务
             if (debug) log("logout action!");
             logoutService(cookieValue);
             response.sendRedirect(SSOLoginPage+"?goto="+url);
         } else {// 效验成功
             request.setAttribute("SSOUser",result);
             Throwable problem = null;
             try {
                 chain.doFilter(req, res);
             } catch(Throwable t) {
                 problem = t;
                 t.printStackTrace();
             }      
             if (problem != null) {
                 if (problem instanceof ServletException) throw (ServletException)problem;
                 if (problem instanceof IOException) throw (IOException)problem;
                 sendProcessingError(problem, res);
             }
         }  
     }
doFilter() 方法的逻辑也是非常简单的,在接收到请求的时候,先去查找是否存在期望的 cookie 值,如果找到了,就会调用 SSOService(cookieValue) 去效验这个 cookie 的有效性。如果 cookie 效验不成功或者 cookie 根本不存在,就会直接转到登录界面让用户登录;如果 cookie 效验成功,就不会做任何阻拦,让此请求进行下去。在配置文件中,有下面的一个节点表示了此 filter URL 映射关系:只拦截所有的 jsp 请求。
<filter-mapping>
<filter-name>SSOFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
 
下面还有几个主要的函数需要说明:
     private String SSOService(String cookievalue) throws IOException {
         String authAction = "?action=authcookie&cookiename=";
         HttpClient httpclient = new HttpClient();
         GetMethod httpget = new GetMethod(SSOServiceURL+authAction+cookievalue);
         try { 
             httpclient.executeMethod(httpget);
             String result = httpget.getResponseBodyAsString();
             return result;
         } finally {
             httpget.releaseConnection();
         }
     }
   
     private void logoutService(String cookievalue) throws IOException {
         String authAction = "?action=logout&cookiename=";
         HttpClient httpclient = new HttpClient();
         GetMethod httpget = new GetMethod(SSOServiceURL+authAction+cookievalue);
         try {
             httpclient.executeMethod(httpget);
             httpget.getResponseBodyAsString();
         } finally {
             httpget.releaseConnection();
         }
     }
这两个函数主要是利用 apache 中的 httpclient 访问 SSOAuth 提供的认证服务来完成效验 cookie logout 的功能。
其他的函数都很简单,有很多都是我的 IDE NetBeans )替我自动生成的。
当前方案的安全局限性
当前这个 WEB-SSO 的方案是一个比较简单的雏形,主要是用来演示 SSO 的概念和说明 SSO 技术的实现方式。有很多方面还需要完善,其中安全性是非常重要的一个方面。
我们说过,采用 SSO 技术的主要目的之一就是加强安全性,降低安全风险。因为采用了 SSO ,在网络上传递密码的次数减少,风险降低是显然的,但是当前的方案却有其他的安全风险。由于 cookie 是一个用户登录的唯一凭据,对 cookie 的保护措施是系统安全的重要环节:
  • cookie的长度和复杂度
    在本方案中,cookie是有一个固定的字符串(我的姓名)加上当前的时间戳。这样的cookie很容易被伪造和猜测。怀有恶意的用户如果猜测到合法的cookie就可以被当作已经登录的用户,任意访问权限范围内的资源
  • cookie的效验和保护
    在本方案中,虽然密码只要传输一次就够了,可cookie在网络中是经常传来传去。一些网络探测工具(如sniff, snoop,tcpdump等)可以很容易捕获到cookie的数值。在本方案中,并没有考虑cookie在传输时候的保护。另外对cookie的效验也过于简单,并不去检查发送cookie的来源究竟是不是cookie最初的拥有者,也就是说无法区分正常的用户和仿造cookie的用户。
  • 当其中一个应用的安全性不好,其他所有的应用都会受到安全威胁
    因为有SSO,所以当某个处于 SSO的应用被黒客攻破,那么很容易攻破其他处于同一个SSO保护的应用。
这些安全漏洞在商业的 SSO 解决方案中都会有所考虑,提供相关的安全措施和保护手段,例如 Sun 公司的 Access Manager cookie 的复杂读和对 cookie 的保护都做得非常好。另外在 OpneSSO  https://opensso.dev.java.net )的架构指南中也给出了部分安全措施的解决方案。
当前方案的功能和性能局限性
除了安全性,当前方案在功能和性能上都需要很多的改进:
  • 当前所提供的登录认证模式只有一种:用户名和密码,而且为了简单,将用户名和密码放在内存当中。事实上,用户身份信息的来源应该是多种多样的,可以是来自数据库中,LDAP中,甚至于来自操作系统自身的用户列表。还有很多其他的认证模式都是商务应用不可缺少的,因此SSO的解决方案应该包括各种认证的模式,包括数字证书,Radius, SafeWord MemberShipSecurID等多种方式。最为灵活的方式应该允许可插入的JAAS框架来扩展身份认证的接口
  • 我们编写的Filter只能用于J2EE的应用,而对于大量非JavaWeb应用,却无法提供SSO服务。
  • 在将Filter应用到Web应用的时候,需要对容器上的每一个应用都要做相应的修改,重新部署。而更加流行的做法是Agent机制:为每一个应用服务器安装一个agent,就可以将SSO功能应用到这个应用服务器中的所有应用。
  • 当前的方案不能支持分别位于不同domainWeb应用进行SSO。这是因为浏览器在访问Web服务器的时候,仅仅会带上和当前web服务器具有相同domain名称的那些cookie。要提供跨域的SSO的解决方案有很多其他的方法,在这里就不多说了。SunAccess Manager就具有跨域的SSO的功能。
  • 另外,Filter的性能问题也是需要重视的方面。因为Filter会截获每一个符合URL映射规则的请求,获得cookie,验证其有效性。这一系列任务是比较消耗资源的,特别是验证cookie有效性是一个远程的http的调用,来访问SSOAuth的认证服务,有一定的延时。因此在性能上需要做进一步的提高。例如在本样例中,如果将URL映射从“.jsp改成“/*,也就是说filter对所有的请求都起作用,整个应用会变得非常慢。这是因为,页面当中包含了各种静态元素如gif图片,css样式文件,和其他html静态页面,这些页面的访问都要通过filter去验证。而事实上,这些静态元素没有什么安全上的需求,应该在filter中进行判断,不去效验这些请求,性能会好很多。另外,如果在filter中加上一定的cache,而不需要每一个cookie效验请求都去远端的身份认证服务中执行,性能也能大幅度提高。
  • 另外系统还需要很多其他的服务,如在内存中定时删除无用的cookie映射等等,都是一个严肃的解决方案需要考虑的问题。
5 真正安全的全方位SSO解决方案:Kerberos
我们的样例程序(桌面 SSO WEB-SSO )都有一个共性:要想将一个应用集成到我们的 SSO 解决方案中,或多或少的需要修改应用程序。 Web 应用需要配置一个我们预制的 filter ;桌面应用需要加上我们桌面 SSO JAAS 模块(至少要修改 JAAS 的配置文件)。可是有很多程序是没有源代码和无法修改的,例如常用的远程通讯程序 telnet ftp 等等一些操作系统自己带的常用的应用程序。这些程序是很难修改加入到我们的 SSO 的解决方案中。
事实上有一种全方位的 SSO 解决方案能够解决这些问题,这就是 Kerberos 协议( RFC 1510 )。 Kerberos 是网络安全应用标准 (http://web.mit.edu/kerberos/) ,由 MIT 学校发明,被主流的操作系统所采用。在采用 kerberos 的平台中,登录和认证是由操作系统本身来维护,认证的凭证也由操作系统来保存,这样整个桌面都可以处于同一个 SSO 的系统保护中。操作系统中的各个应用(如 ftp,telnet )只需要通过配置就能加入到 SSO 中。另外使用 Kerberos 最大的好处在于它的安全性。通过密钥算法的保证和密钥中心的建立,可以做到用户的密码根本不需要在网络中传输,而传输的信息也会十分的安全。
目前支持 Kerberos 的操作系统包括 Solaris, windows,Linux 等等主流的平台。只不过要搭建一个 Kerberos 的环境比较复杂, KDC (密钥分发中心)的建立也需要相当的步骤。 Kerberos 拥有非常成熟的 API ,包括 Java API 。使用 Java Generic Security Services(GSS) API 并且使用 JAAS 中对 Kerberos 的支持(详细信息请参见 Sun Java&Kerberos 教程 http://java.sun.com/ j2se/1.5.0/docs/guide/security/jgss/tutorials/index.html ),要将我们这个样例改造成对 Kerberos 的支持也是不难的。 值得一提的是在 JDK6.0  http://www.java.net/download/jdk6 )当中直接就包含了对 GSS 的支持,不需要单独下载 GSS 的包。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值