单点登录(Single Sign On , 简称 SSO )是目前比较流行的服务于企业业务整合的解决方案之一, SSO 使得在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。CAS(Central Authentication Service)是一款不错的针对 Web 应用的单点登录框架,本文介绍了 CAS 的原理、协议、在 Tomcat 中的配置和使用,对于采用 CAS 实现轻量级单点登录解决方案的入门读者具有一定指导作用。
CAS 介绍
CAS 是 Yale 大学发起的一个开源项目,旨在为 Web 应用系统提供一种可靠的单点登录方法,CAS 在 2004 年 12 月正式成为 JA-SIG 的一个项目。CAS 具有以下特点:
- 开源的企业级单点登录解决方案。
- CAS Server 为需要独立部署的 Web 应用。
- CAS Client 支持非常多的客户端(这里指单点登录系统中的各个 Web 应用),包括 Java, .Net, PHP, Perl, Apache, uPortal, Ruby 等。
CAS 原理和协议
从结构上看,CAS 包含两个部分: CAS Server 和 CAS Client。CAS Server 需要独立部署,主要负责对用户的认证工作;CAS Client 负责处理对客户端受保护资源的访问请求,需要登录时,重定向到 CAS Server。图1 是 CAS 最基本的协议过程:
图 1. CAS 基础协议
CAS Client 与受保护的客户端应用部署在一起,以 Filter 方式保护受保护的资源。对于访问受保护资源的每个 Web 请求,CAS Client 会分析该请求的 Http 请求中是否包含 Service Ticket,如果没有,则说明当前用户尚未登录,于是将请求重定向到指定好的 CAS Server 登录地址,并传递 Service (也就是要访问的目的资源地址),以便登录成功过后转回该地址。用户在第 3 步中输入认证信息,如果登录成功,CAS Server 随机产生一个相当长度、唯一、不可伪造的 Service Ticket,并缓存以待将来验证,之后系统自动重定向到 Service 所在地址,并为客户端浏览器设置一个 Ticket Granted Cookie(TGC),CAS Client 在拿到 Service 和新产生的 Ticket 过后,在第 5,6 步中与 CAS Server 进行身份合适,以确保 Service Ticket 的合法性。
在该协议中,所有与 CAS 的交互均采用 SSL 协议,确保,ST 和 TGC 的安全性。协议工作过程中会有 2 次重定向的过程,但是 CAS Client 与 CAS Server 之间进行 Ticket 验证的过程对于用户是透明的。
另外,CAS 协议中还提供了 Proxy (代理)模式,以适应更加高级、复杂的应用场景,具体介绍可以参考 CAS 官方网站上的相关文档。
准备工作
本文中的例子以 tomcat5.5 为例进行讲解,下载地址:
http://tomcat.apache.org/download-55.cgi
到 CAS 官方网站下载 CAS Server 和 Client,地址分别为:
http://www.ja-sig.org/downloads/cas/cas-server-3.1.1-release.zip
http://www.ja-sig.org/downloads/cas-clients/cas-client-java-2.1.1.zip
部署 CAS Server
CAS Server 是一套基于 Java 实现的服务,该服务以一个 Java Web Application 单独部署在与 servlet2.3 兼容的 Web 服务器上,另外,由于 Client 与 CAS Server 之间的交互采用 Https 协议,因此部署 CAS Server 的服务器还需要支持 SSL 协议。当 SSL 配置成功过后,像普通 Web 应用一样将 CAS Server 部署在服务器上就能正常运行了,不过,在真正使用之前,还需要扩展验证用户的接口。
在 Tomcat 上部署一个完整的 CAS Server 主要按照以下几个步骤:
配置 Tomcat 使用 Https 协议
如果希望 Tomcat 支持 Https,主要的工作是配置 SSL 协议,其配置过程和配置方法可以参考 Tomcat 的相关文档。不过在生成证书的过程中,会有需要用到主机名的地方,CAS 建议不要使用 IP 地址,而要使用机器名或域名。
部署 CAS Server
CAS Server 是一个 Web 应用包,将前面下载的 cas-server-3.1.1-release.zip 解开,把其中的 cas-server-webapp-3.1.1.war 拷贝到 tomcat的 webapps 目录,并更名为 cas.war。由于前面已配置好 tomcat 的 https 协议,可以重新启动 tomcat,然后访问:https://localhost:8443/cas ,如果能出现正常的 CAS 登录页面,则说明 CAS Server 已经部署成功。
虽然 CAS Server 已经部署成功,但这只是一个缺省的实现,在实际使用的时候,还需要根据实际概况做扩展和定制,最主要的是扩展认证 (Authentication) 接口和 CAS Server 的界面。
扩展认证接口
CAS Server 负责完成对用户的认证工作,它会处理登录时的用户凭证 (Credentials) 信息,用户名/密码对是最常见的凭证信息。CAS Server 可能需要到数据库检索一条用户帐号信息,也可能在 XML 文件中检索用户名/密码,还可能通过 LDAP Server 获取等,在这种情况下,CAS 提供了一种灵活但统一的接口和实现分离的方式,实际使用中 CAS 采用哪种方式认证是与 CAS 的基本协议分离开的,用户可以根据认证的接口去定制和扩展。
扩展 AuthenticationHandler
CAS 提供扩展认证的核心是 AuthenticationHandler 接口,该接口定义如清单 1 下:
清单 1. AuthenticationHandler定义
public interface AuthenticationHandler {
/**
* Method to determine if the credentials supplied are valid.
* @param credentials The credentials to validate.
* @return true if valid, return false otherwise.
* @throws AuthenticationException An AuthenticationException can contain
* details about why a particular authentication request failed.
*/
boolean authenticate(Credentials credentials) throws AuthenticationException;
/**
* Method to check if the handler knows how to handle the credentials
* provided. It may be a simple check of the Credentials class or something
* more complicated such as scanning the information contained in the
* Credentials object.
* @param credentials The credentials to check.
* @return true if the handler supports the Credentials, false othewrise.
*/
boolean supports(Credentials credentials);
}
|
该接口定义了 2 个需要实现的方法,supports ()方法用于检查所给的包含认证信息的Credentials 是否受当前 AuthenticationHandler 支持;而 authenticate() 方法则担当验证认证信息的任务,这也是需要扩展的主要方法,根据情况与存储合法认证信息的介质进行交互,返回 boolean 类型的值,true 表示验证通过,false 表示验证失败。
CAS3中还提供了对AuthenticationHandler 接口的一些抽象实现,比如,可能需要在执行authenticate() 方法前后执行某些其他操作,那么可以让自己的认证类扩展自清单 2 中的抽象类:
清单 2. AbstractPreAndPostProcessingAuthenticationHandler定义
public abstract class AbstractPreAndPostProcessingAuthenticationHandler
implements AuthenticateHandler{
protected Log log = LogFactory.getLog(this.getClass());
protected boolean preAuthenticate(final Credentials credentials) {
return true;
}
protected boolean postAuthenticate(final Credentials credentials,
final boolean authenticated) {
return authenticated;
}
public final boolean authenticate(final Credentials credentials)
throws AuthenticationException {
if (!preAuthenticate(credentials)) {
return false;
}
final boolean authenticated = doAuthentication(credentials);
return postAuthenticate(credentials, authenticated);
}
protected abstract boolean doAuthentication(final Credentials credentials)
throws AuthenticationException;
}
|
AbstractPreAndPostProcessingAuthenticationHandler 类新定义了 preAuthenticate() 方法和 postAuthenticate() 方法,而实际的认证工作交由 doAuthentication() 方法来执行。因此,如果需要在认证前后执行一些额外的操作,可以分别扩展 preAuthenticate()和 ppstAuthenticate() 方法,而 doAuthentication() 取代 authenticate() 成为了子类必须要实现的方法。
由于实际运用中,最常用的是用户名和密码方式的认证,CAS3 提供了针对该方式的实现,如清单 3 所示:
清单 3. AbstractUsernamePasswordAuthenticationHandler 定义
public abstract class AbstractUsernamePasswordAuthenticationHandler extends
AbstractPreAndPostProcessingAuthenticationHandler{
...
protected final boolean doAuthentication(final Credentials credentials)
throws AuthenticationException {
return authenticateUsernamePasswordInternal((UsernamePasswordCredentials) credentials);
}
protected abstract boolean authenticateUsernamePasswordInternal(
final UsernamePasswordCredentials credentials) throws AuthenticationException;
protected final PasswordEncoder getPasswordEncoder() {
return this.passwordEncoder;
}
public final void setPasswordEncoder(final PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
...
}
|
基于用户名密码的认证方式可直接扩展自 AbstractUsernamePasswordAuthenticationHandler,验证用户名密码的具体操作通过实现 authenticateUsernamePasswordInternal() 方法达到,另外,通常情况下密码会是加密过的,setPasswordEncoder() 方法就是用于指定适当的加密器。
从以上清单中可以看到,doAuthentication() 方法的参数是 Credentials 类型,这是包含用户认证信息的一个接口,对于用户名密码类型的认证信息,可以直接使用 UsernamePasswordCredentials,如果需要扩展其他类型的认证信息,需要实现Credentials接口,并且实现相应的 CredentialsToPrincipalResolver 接口,其具体方法可以借鉴 UsernamePasswordCredentials 和 UsernamePasswordCredentialsToPrincipalResolver。
JDBC 认证方法
用户的认证信息通常保存在数据库中,因此本文就选用这种情况来介绍。将前面下载的 cas-server-3.1.1-release.zip 包解开后,在 modules 目录下可以找到包 cas-server-support-jdbc-3.1.1.jar,其提供了通过 JDBC 连接数据库进行验证的缺省实现,基于该包的支持,我们只需要做一些配置工作即可实现 JDBC 认证。
JDBC 认证方法支持多种数据库,DB2, Oracle, MySql, Microsoft SQL Server 等均可,这里以 DB2 作为例子介绍。并且假设DB2数据库名: CASTest,数据库登录用户名: db2user,数据库登录密码: db2password,用户信息表为: userTable,该表包含用户名和密码的两个数据项分别为 userName 和 password。
1. 配置 DataStore
打开文件 %CATALINA_HOME%/webapps/cas/WEB-INF/deployerConfigContext.xml,添加一个新的 bean 标签,对于 DB2,内容如清单 4 所示:
清单 4. 配置 DataStore
<bean id="casDataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName">
<value>com.ibm.db2.jcc.DB2Driver</value>
</property>
<property name="url">
<value>jdbc:db2://9.125.65.134:50000/CASTest</value>
</property>
<property name="username">
<value>db2user</value>
</property>
<property name="password">
<value>db2password</value>
</property>
</bean>
|
其中 id 属性为该 DataStore 的标识,在后面配置 AuthenticationHandler 会被引用,另外,需要提供 DataStore 所必需的数据库驱动程序、连接地址、数据库登录用户名以及登录密码。
2. 配置 AuthenticationHandler
在 cas-server-support-jdbc-3.1.1.jar 包中,提供了 3 个基于 JDBC 的 AuthenticationHandler,分别为 BindModeSearchDatabaseAuthenticationHandler, QueryDatabaseAuthenticationHandler, SearchModeSearchDatabaseAuthenticationHandler。其中 BindModeSearchDatabaseAuthenticationHandler 是用所给的用户名和密码去建立数据库连接,根据连接建立是否成功来判断验证成功与否;QueryDatabaseAuthenticationHandler 通过配置一个 SQL 语句查出密码,与所给密码匹配;SearchModeSearchDatabaseAuthenticationHandler 通过配置存放用户验证信息的表、用户名字段和密码字段,构造查询语句来验证。
使用哪个 AuthenticationHandler,需要在 deployerConfigContext.xml 中设置,默认情况下,CAS 使用一个简单的 username=password 的 AuthenticationHandler,在文件中可以找到如下一行:<bean class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePassword
AuthenticationHandler" />,我们可以将其注释掉,换成我们希望的一个 AuthenticationHandler,比如,使用QueryDatabaseAuthenticationHandler 或 SearchModeSearchDatabaseAuthenticationHandler 可以分别选取清单 5 或清单 6 的配置。
清单 5. 使用 QueryDatabaseAuthenticationHandler
<bean class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler">
<property name="dataSource" ref=" casDataSource " />
<property name="sql"
value="select password from userTable where lower(userName) = lower(?)" />
</bean>
|
清单 6. 使用 SearchModeSearchDatabaseAuthenticationHandler
<bean id="SearchModeSearchDatabaseAuthenticationHandler"
class="org.jasig.cas.adaptors.jdbc.SearchModeSearchDatabaseAuthenticationHandler"
abstract="false" singleton="true" lazy-init="default"
autowire="default" dependency-check="default">
<property name="tableUsers">
<value>userTable</value>
</property>
<property name="fieldUser">
<value>userName</value>
</property>
<property name="fieldPassword">
<value>password</value>
</property>
<property name="dataSource" ref=" casDataSource " />
</bean>
|
另外,由于存放在数据库中的密码通常是加密过的,所以 AuthenticationHandler 在匹配时需要知道使用的加密方法,在 deployerConfigContext.xml 文件中我们可以为具体的 AuthenticationHandler 类配置一个 property,指定加密器类,比如对于 QueryDatabaseAuthenticationHandler,可以修改如清单7所示:
清单 7. 添加 passwordEncoder
<bean class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler">
<property name="dataSource" ref=" casDataSource " />
<property name="sql"
value="select password from userTable where lower(userName) = lower(?)" />
<property name="passwordEncoder" ref="myPasswordEncoder"/>
</bean>
|
其中 myPasswordEncoder 是对清单 8 中设置的实际加密器类的引用:
清单 8. 指定具体加密器类
<bean id="passwordEncoder"
class="org.jasig.cas.authentication.handler.MyPasswordEncoder"/>
|
这里 MyPasswordEncoder 是根据实际情况自己定义的加密器,实现 PasswordEncoder 接口及其 encode() 方法。
3. 部署依赖包
在以上配置完成以后,需要拷贝几个依赖的包到 cas 应用下,包括:
- 将 cas-server-support-jdbc-3.1.1.jar 拷贝到 %CATALINA_HOME%/webapps/cas/ WEB-INF/lib 目录。
- 数据库驱动,由于这里使用 DB2,将 %DB2_HOME%/java 目录下的 db2java.zip (更名为 db2java.jar), db2jcc.jar, db2jcc_license_cu.jar 拷贝到 %CATALINA_HOME%/webapps/cas/WEB-INF/lib 目录。对于其他数据库,同样将相应数据库驱动程序拷贝到该目录。
- DataStore 依赖于 commons-collections-3.2.jar, commons-dbcp-1.2.1.jar, commons-pool-1.3.jar,需要到 apache 网站的 Commons 项目下载以上 3 个包放进 %CATALINA_HOME%/webapps/cas/WEB-INF/lib 目录。
扩展 CAS Server 界面
CAS 提供了 2 套默认的页面,分别为“ default ”和“ simple ”,分别在目录“ cas/WEB-INF/view/jsp/default ”和“ cas/WEB-INF/view/jsp/simple ”下。其中 default 是一个稍微复杂一些的页面,使用 CSS,而 simple 则是能让 CAS 正常工作的最简化的页面。
在部署 CAS 之前,我们可能需要定制一套新的 CAS Server 页面,添加一些个性化的内容。最简单的方法就是拷贝一份 default 或 simple 文件到“ cas/WEB-INF/view/jsp ”目录下,比如命名为 newUI,接下来是实现和修改必要的页面,有 4 个页面是必须的:
- casConfirmView.jsp: 当用户选择了“ warn ”时会看到的确认界面
- casGenericSuccess.jsp: 在用户成功通过认证而没有目的Service时会看到的界面
- casLoginView.jsp: 当需要用户提供认证信息时会出现的界面
- casLogoutView.jsp: 当用户结束 CAS 单点登录系统会话时出现的界面
CAS 的页面采用 Spring 框架编写,对于不熟悉 Spring 的使用者,在修改之前需要熟悉该框架。
页面定制完过后,还需要做一些配置从而让 CAS 找到新的页面,拷贝“ cas/WEB-INF/classes/default_views.properties ”,重命名为“ cas/WEB-INF/classes/ newUI_views.properties ”,并修改其中所有的值到相应新页面。最后是更新“ cas/WEB-INF/cas-servlet.xml ”文件中的 viewResolver,将其修改为如清单 9 中的内容。
清单 9. 指定 CAS 页面
<bean id="viewResolver"
class="org.springframework.web.servlet.view.ResourceBundleViewResolver" p:order="0">
<property name="basenames">
<list>
<value>${cas.viewResolver.basename}</value>
<value> newUI_views</value>
</list>
</property>
</bean>
|
部署客户端应用
单点登录的目的是为了让多个相关联的应用使用相同的登录过程,本文在讲解过程中构造 2个简单的应用,分别以 casTest1 和 casTest2 来作为示例,它们均只有一个页面,显示欢迎信息和当前登录用户名。这 2 个应用使用同一套登录信息,并且只有登录过的用户才能访问,通过本文的配置,实现单点登录,即只需登录一次就可以访问这两个应用。
与 CAS Server 建立信任关系
假设 CAS Server 单独部署在一台机器 A,而客户端应用部署在机器 B 上,由于客户端应用与 CAS Server 的通信采用 SSL,因此,需要在 A 与 B 的 JRE 之间建立信任关系。
首先与 A 机器一样,要生成 B 机器上的证书,配置 Tomcat 的 SSL 协议。其次,下载http://blogs.sun.com/andreas/entry/no_more_unable_to_find 的 InstallCert.java,运行“ java InstallCert compA:8443 ”命令,并且在接下来出现的询问中输入 1。这样,就将 A 添加到了 B 的 trust store 中。如果多个客户端应用分别部署在不同机器上,那么每个机器都需要与 CAS Server 所在机器建立信任关系。
配置 CAS Filter
准备好应用 casTest1 和 casTest2 过后,分别部署在 B 和 C 机器上,由于 casTest1 和casTest2,B 和 C 完全等同,我们以 casTest1 在 B 机器上的配置做介绍,假设 A 和 B 的域名分别为 domainA 和 domainB。
将 cas-client-java-2.1.1.zip 改名为 cas-client-java-2.1.1.jar 并拷贝到 casTest1/WEB-INF/lib目录下,修改 web.xml 文件,添加 CAS Filter,如清单 10 所示:
清单 10. 添加 CAS Filter
<web-app>
...
<filter>
<filter-name>CAS Filter</filter-name>
<filter-class>edu.yale.its.tp.cas.client.filter.CASFilter</filter-class>
<init-param>
<param-name>edu.yale.its.tp.cas.client.filter.loginUrl</param-name>
<param-value>https://domainA:8443/cas/login</param-value>
</init-param>
<init-param>
<param-name>edu.yale.its.tp.cas.client.filter.validateUrl</param-name>
<param-value>https://domainA:8443/cas/serviceValidate</param-value>
</init-param>
<init-param>
<param-name>edu.yale.its.tp.cas.client.filter.serverName</param-name>
<param-value>domainB:8080</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CAS Filter</filter-name>
<url-pattern>/protected-pattern/*</url-pattern>
</filter-mapping>
...
</web-app>
|
对于所有访问满足 casTest1/protected-pattern/ 路径的资源时,都要求到 CAS Server 登录,如果需要整个 casTest1 均受保护,可以将 url-pattern 指定为“/*”。
从清单 10 可以看到,我们可以为 CASFilter 指定一些参数,并且有些是必须的,表格 1 和表格 2 中分别是必需和可选的参数:
表格 1. CASFilter 必需的参数
参数名 |
作用 |
edu.yale.its.tp.cas.client.filter.loginUrl |
指定 CAS 提供登录页面的 URL |
edu.yale.its.tp.cas.client.filter.validateUrl |
指定 CAS 提供 service ticket 或 proxy ticket 验证服务的 URL |
edu.yale.its.tp.cas.client.filter.serverName |
指定客户端的域名和端口,是指客户端应用所在机器而不是 CAS Server 所在机器,该参数或 serviceUrl 至少有一个必须指定 |
edu.yale.its.tp.cas.client.filter.serviceUrl |
该参数指定过后将覆盖 serverName 参数,成为登录成功过后重定向的目的地址 |
表格 2. CASFilter 可选参数
参数名 |
作用 |
edu.yale.its.tp.cas.client.filter.proxyCallbackUrl |
用于当前应用需要作为其他服务的代理(proxy)时获取 Proxy Granting Ticket 的地址 |
edu.yale.its.tp.cas.client.filter.authorizedProxy |
用于允许当前应用从代理处获取 proxy tickets,该参数接受以空格分隔开的多个 proxy URLs,但实际使用只需要一个成功即可。当指定该参数过后,需要修改 validateUrl 到 proxyValidate,而不再是 serviceValidate |
edu.yale.its.tp.cas.client.filter.renew |
如果指定为 true,那么受保护的资源每次被访问时均要求用户重新进行验证,而不管之前是否已经通过 |
edu.yale.its.tp.cas.client.filter.wrapRequest |
如果指定为 true,那么 CASFilter 将重新包装 HttpRequest,并且使 getRemoteUser() 方法返回当前登录用户的用户名 |
edu.yale.its.tp.cas.client.filter.gateway |
指定 gateway 属性 |
传递登录用户名
CAS 在登录成功过后,会给浏览器回传 Cookie,设置新的到的 Service Ticket。但客户端应用拥有各自的 Session,我们要怎么在各个应用中获取当前登录用户的用户名呢?CAS Client 的 Filter 已经做好了处理,在登录成功后,就可以直接从 Session 的属性中获取,如清单 11 所示:
清单 11. 在 Java 中通过 Session 获取登录用户名
// 以下两者都可以
session.getAttribute(CASFilter.CAS_FILTER_USER);
session.getAttribute("edu.yale.its.tp.cas.client.filter.user");
|
在 JSTL 中获取用户名的方法如清单 12 所示:
清单 12. 通过 JSTL 获取登录用户名
<c:out value="${sessionScope[CAS:'edu.yale.its.tp.cas.client.filter.user']}"/>
|
另外,CAS 提供了一个 CASFilterRequestWrapper 类,该类继承自HttpServletRequestWrapper,主要是重写了 getRemoteUser() 方法,只要在前面配置 CASFilter 的时候为其设置“ edu.yale.its.tp.cas.client.filter.wrapRequest ”参数为 true,就可以通过 getRemoteUser() 方法来获取登录用户名,具体方法如清单 13 所示:
清单 13. 通过 CASFilterRequestWrapper 获取登录用户名
CASFilterRequestWrapper reqWrapper=new CASFilterRequestWrapper(request);
out.println("The logon user:" + reqWrapper.getRemoteUser());
|
效果
在 casTest1 和 casTest2 中,都有一个简单 Servlet 作为欢迎页面 WelcomPage,且该页面必须登录过后才能访问,页面代码如清单 14 所示:
清单 14. WelcomePage 页面代码
public class WelcomePage extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>Welcome to casTest2 sample System!</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>Welcome to casTest1 sample System!</h1>");
CASFilterRequestWrapper reqWrapper=new CASFilterRequestWrapper(request);
out.println("<p>The logon user:" + reqWrapper.getRemoteUser() + "</p>");
HttpSession session=request.getSession();
out.println("<p>The logon user:" +
session.getAttribute(CASFilter.CAS_FILTER_USER) + "</p>");
out.println("<p>The logon user:" +
session.getAttribute("edu.yale.its.tp.cas.client.filter.user") + "</p>");
out.println("</body>");
out.println("</html>");
}
}
|
在上面所有配置结束过后,分别在 A, B, C上启动 cas, casTest1 和 casTest2,按照下面步骤来访问 casTest1 和 casTest2:
- 打开浏览器,访问 http://domainB:8080/casTest1/WelcomePage ,浏览器会弹出安全提示,接受后即转到 CAS 的登录页面,如图 2 所示:
图 2. CAS 登录页面
- 登录成功后,再重定向到 casTest1 的 WelcomePage 页面,如图 所示:
图 3. 登录后访问 casTest1 的效果
可以看到图 中地址栏里的地址多出了一个 ticket 参数,这就是 CAS 分配给当前应用的 ST(Service Ticket)。
- 再在同一个浏览器的地址栏中输入 http://domainC:8080/casTest2/WelcomePage ,系统不再提示用户登录,而直接出现如图 4 所示的页面,并且显示在 casTest1 中已经登录过的用户。
图 4. 在 casTest1 中登录过后访问 casTest2 的效果
- 重新打开一个浏览器窗口,先输入 http://domainC:8080/casTest2/WelcomePage ,系统要求登录,在登录成功过后,正确显示 casTest2 的页面。之后再在地址栏重新输入 http://domainB:8080/casTest1/WelcomePage ,会直接显示 casTest1 的页面而无需再次登录。
http://www.ibm.com/developerworks/cn/opensource/os-cn-cas/index.html
浅析Web单点登录和安全断言标记语言技术
SAML已经被结构化信息标准促进组织(OASIS)批准为Web 单点登录的执行标准。SAML连同Web单点登录共同构成了现代网络环境中的必备条件。
美国在线和eBay是两个知名网站,拥有大量的用户。通过分析,这两个网站发现他们有30%的用户是重合的,也就是说,这些用户在两个网站都有用户名和密码。因此,他们认为有必要找到一种方法,让这30%的用户在登录其中一个网站时,其身份能够自动被另一网站识别,并可以访问另一网站的资源。最终,他们采用了Web单点登录解决方案。
单点登录:大势所趋
在大多数计算机连入网络之前,各个系统中像身份验证和授权这类安全服务的实现完全是独立的。因此,执行身份验证所需的全部代码,以及密钥、口令,供授权决策所用的用户信息,以及授权策略本身均存放于使用这些信息的系统上。最初,系统连接到网络上时情况变化不大。每个系统都是一个孤岛,各系统都要求用户拥有一个账户才能访问该系统。
这种方法有许多明显的缺点。举例来说,设置多个账户,每个账户有一个密码、组或其他属性,这对用户和管理员来说非常不方便。如果一个用户的职责发生变更而修改其账户属性,或有人离开组织时删除其账户,管理员要浪费大量时间。如果出现更强大的认证方法,各系统还必须单独地去升级。
随着互联网的出现,多台机器作为一个Web站点的主机成为一种普遍现象。但仅仅因为用户要使用不同的机器处理不同的请求而强迫他们多次在网络上进行登录,显然是令人无法接受的。同样,门户也不能要求使用者每访问不同的应用程序就重新登录。于是单点登录(Single Sign-on,SSO)出现了。单点登录最初被视为一种提高生产力的奢侈品,而现在已成为一种必需品。当前有越来越多的身份与访问控制管理(IAM)需求来自Web应用和Web Services,从IDC的市场预测也可以看出,年复合增长率CAGR最高的两个子方向之一就是Web单点登录,达到了20.8%。
单点登录是一种用于方便用户访问网络的技术。无论多么复杂的网络结构,用户只需在登录时进行一次注册,即可获得访问系统和应用软件的授权,以后便可以在网络中自由穿梭,不必多次输入用户名和口令来确定身份。在此条件下,管理员无需修改或干涉用户登录就能方便地实施希望得到的安全控制。
实现单点登录后,认证网络用户能够利用连贯且安全的身份识别信息交换机制,在其自身运行环境或业务合作伙伴运行环境下,便捷地在两个应用程序中移动,而无需重新认证。
SAML:一统Web SSO江湖
当今,越来越多的系统通过Web服务、门户和集成化应用程序彼此链接,对于保证信息安全交换标准的需求也随之日益增多。安全断言标记语言(Security Assertion Markup Language,SAML)提供了一个健壮且可扩展的数据格式集,在各种环境下交换数据和身份识别信息。SAML的出现大大简化了Web单点登录,并被结构化信息标准促进组织(OASIS)批准为Web SSO的执行标准。这里的一个关键概念是身份联邦,它可满足SAML的定义,也就是说可使用独立、受管理的多个信息来源中的信息。SAML连同Web单点登录共同构成现代网络环境中的必备条件。
身份联邦是企业身份和访问管理战略的关键组成部分,提供了多项业务优势。对于企业而言,身份联邦增强了与业务合作伙伴进行合作、管理供应链、为客户提供新型创收服务的能力,并且保护了企业资源,降低了成本。对于最终用户而言,身份联邦使网站访问更加便捷,提高了工作效率;提供了更广泛的访问信息与服务;有效保护了个人信息。
另外,随着互联网规模的不断扩大,把一个用户的所有信息全部收集到一个地方,既不可能也不需要。不同个人和组织在与不同的对象打交道时会使用不同类型的信息,例如医生掌管病历卡、会计师保存财政和税款记录等等。经常性地将此信息移动到一个地点,只能使保持数据的准确性和及时更新更加困难。同时,移动信息还会增加数据在传输中丢失和被窃的可能性。
尽管如此,为进行身份验证和授权,还是必须在网络上保留许多类型的信息。这也正是身份联邦的目的。出于授权等目的,身份联邦将来自多个数据源的同一用户数据综合在一起。不同的组织可能希望使用不同的产品去管理其身份数据,那么自然就需要制订一个在网络上传送这些数据(从数据当前所在的地方,到现在正需要数据的地方)的标准。虽然许多产品提供Web单点登录,同样需要一种标准使这种跨不同产品的传送成为可能,这就是SAML关注的领域。
举一个简单的例子:用户在一个站点上取得认证授权,当用户需要访问另一个相关站点的资源时,目的站点(保护资源的持有者)能够使用SAML从源站点调取用户的证书信息。此时SAML对信息交换的处理发生在后台,因此用户的资源实际上被不同的安全系统进行了定位。
SAML是什么
Web安全方面最具挑战性的一个问题是维持一次无缝操作和安全环境时, 使各不相同的安全系统达到一体化。比如在电子商务活动过程中,经常需要通过网络来交换机密的资料或数据,因此,对于安全功能的要求十分严格。
OASIS建立的安全标准—SAML是基于XML(可扩展标记语言)、面向Web服务的架构。SAML通过互联网对不同安全系统的信息交换进行处理。
SAML是一种语言,进行单一的XML描述,允许不同安全系统产生的信息进行交换。通常来说,一个企业在物理或逻辑的范围已经界定了企业的IT安全;然而,由于在线合作需要共享更可靠的安全服务环境,因此IT安全越来越成为人们关注的重点问题。
SAML正是为解决网络安全性问题而发挥其作用。SAML在传统意义上的安全界定与商务站点之间建立了一种安全信息的交换渠道。SAML作为安全信息交换的“中间人”,促使一个站点上的交易业务能够在另一个信任的站点上得到处理完成。由此可见,实现交易双方商业协议或合作的一个先决条件,是要求使用SAML作为共享安全架构的一部分。
SAML在标准行业传输协议环境里工作,例如HTTP、SMTP和FTP;同时也服务于各种各样的XML文件交换框架,例如SOAP和BizTalk。SAML具备的一个最突出的好处,是使用户能够通过互联网进行安全证书移动。也就是说,使用SAML标准作为安全认证和共享资料的中间语言,能够在多个站点之间实现单点登录。
SAML是一种基于XML语言用于传输认证及授权信息的框架,以与主体相关的断言形式表达。在这里,主体是一个实体(人或计算机),这个实体在某个安全域中拥有一个特定身份,断言可传递主体执行的认证信息、属性信息及关于是否允许主体访问其资源的授权决定。针对以上不同目的,SAML提供以下几种不同类型的安全断言:
● 认证断言(Authentication Assertion):认证断言用来声称消息发布者已经认证特定的主体。
● 属性断言(Attribute Assertion):属性断言声称特定主体具有特定的属性。属性可通过URI(统一资源标识)或用来定义结构化属性的一种扩展模式进行详细说明。
● 决定断言(Decision Assertion):一个决定断言报告了一个具体授权请求的结果。
● 授权断言(Authorization Assertion):授权断言声称一个主体被给予访问一个或多个资源的特别许可。
SAML断言以XML结构描述且具有嵌套结构,由此一个断言可能包括几个关于认证、授权和属性的不同内在断言(包括认证声明的断言仅仅描述那些先前发生的认证行为)。
主流标准:SAML 2.0
在2005年底,随着监控、移动设备、宽带业务以及应用安全领域的四家主要厂商通过了最后一回合的联邦身份互操作性测试,自由联盟(Liberty Alliance)公布了SAML 2.0。
SAML 2.0版在2005年3月刚刚被OASIS批准。Liberty Alliance的目的是让尽可能多的厂商把SAML加入到他们的产品线中。
通过互操作性测试的有四种产品,分别是IBM的Tivoli Federated Identity Manager、NEC 的Mobile Internet Platform、NTT Communication的I-dLive 宽带与网络服务身份联邦工具以及RSA Security公司的Federated Identity Manager。为了证明其互操作性,每个产品必须能够与至少两个厂商共享一个给定的SAML文件。
Oracle和Sun公司在2005年7月已经通过了SAML2.0互操作性测试。现在,Liberty宣称已经有超过70种产品被授予了SAML证书。
Liberty Alliance Conformance Expert Group的主席Roger Sulliva说:“互操作性是必须的。联邦的本质就是需要至少两个公司之间能够进行互操作。只有当你可以在网络中的任何访问点都能做到即插即用,这个标准才能起作用。”
Sullivan表示,“我们的目标是为联邦制定出事实上的标准。”SAML并非惟一想达到这个目标的标准。OASIS最近在组建一个旨在建立一套新的安全标准的委员会。而尚在开发中的WS-Federation规格说明书却没有加入进来,人们预计它明年会跟进。
有些人对WS-Federation的出现是否会在与SAML的结合部引起标准之间的争斗存在疑虑,Sullivan认为那将是两败俱伤的结局。他说:“假如标准之间不融合,那么厂商就不得不支持所有的标准。”
Microsoft和IBM在建立WS-Federation方面很积极。Microsoft的产品现在还没有获得SAML证书,而IBM的Tivoli产品现在已经支持SAML以及WS-Federation。
随着新的Web 服务安全标准被批准,Sullivan相信SAML会被采纳、赞同并获得优势地位。
HP公司软件技术顾问王志刚在接受记者采访时表示,目前业界基本上是两大阵营,OASIS/Liberty与WS-*,即SAML2.0与WS-Federation,二者其实有些地方是重复的,未来统一的单一标准究竟会以哪个为基础,还没有定论,或许会建立在二者之上。
以前,联邦身份一直受到标准过多问题的困扰。5个不兼容的协议(OASIS SAML1.0和1.1、自由联盟ID-FF 1.1和1.2以及Shibboleth)给企业和消费者的应用带来了麻烦,延缓了发展速度,增加了联邦身份部署的费用。寻求部署联邦身份的机构必须与每一位联邦合作伙伴协商选择协议。很多机构必须通过协议映射和转换技术来支持多个协议,而这些技术造成关键特性或功能的支持空隙。
SAML 2.0消除了阻碍进一步采用联邦身份的最大障碍—多协议复杂性,因而大大改变了联邦身份的局面。SAML 2.0将来自每一个前任协议的各种关键使用情况和特性融入到一项标准中。由于SAML 2.0代表5个前任协议中所有功能性的集合,因此它将淘汰以前的协议。
SAML 2.0说明实现联邦的两个角色。服务提供者是为用户提供应用或资源的实体,而身份提供商负责认证用户。服务提供者和身份提供者交换信息,以实现单一登录和退出。这些信息交换可以由身份提供者或服务提供者发起。
在进行单一登录时,身份提供者负责创建包含用户身份的SAML断言,然后安全地将这个断言发送给服务提供者。服务提供者负责在让用户访问应用之前验证SAML断言的有效性。
优势明显
利用SAML,网络服务不需要借助安全认证机构就可验证令牌的有效性,不仅简化了单点登录步骤,还带来了许多传统单点登录方式所不具备的优点:
1.SAML为认证声明和认证属性建立了一个数据格式,其参数取决于安全服务产生的基于政策的认证结果。使用SAML标准作为安全认证和共享资料的中间语言,能够在多个站点之间实现单点登录。
2. SAML针对不同的安全系统提供了一个共有的框架,允许企业及其供应商、客户与合作伙伴进行安全的认证、授权和基本信息交换。由于SAML是通过XML对现有的安全模式进行描述,因此它是一个中立的平台并且不需要依赖于供应商的基础结构。
3. SAML的消息格式能够从一个源站点(站点起到SAML认证管理机构的作用)将断言发送给一个接受者。使电子商务合作中的事务处理速度得到加快,并且使认证环境的复杂性得到全面的简化。
编看编想
关于单点登录的几点认识
单点登录是个热门概念,所有身份与访问控制管理(IAM)产品中都有单点登录的身影。但是,众多的单点登录概念和实现技术让我仿佛坠入了五里雾中,在撰写本文之初,我既分不清单点登录之间的区别,也不明白单点登录和SAML之间的关系。还好,通过向专业人士请教,我大概明白了一二。
根据我的理解,首先,单点登录可以分为企业内部的单点登录和Web单点登录两大类。如果企业内的应用都是B/S架构,或者企业内有C/S架构、B/S架构和Telnet应用,想实现单点登录,就需要采用企业内部单点登录解决方案。如果要在不同域名或不同企业间,或者同一企业的不同分支机构之间实现单点登录,Web单点登录则是更好的选择。
其次,Web单点登录已经有了统一的标准,就是SAML 2.0。而企业内部的单点登录则没有任何标准,由于各企业内部应用的复杂性和独特性,每个厂商都在按照自己的设计思路和用户需求来提供解决方案。
第三,国内的Web单点登录应用远远落后于国外。据RSA公司的工程师陈海林介绍,在国内,企业内部单点登录的应用较多,而Web单点登录才刚刚起步,这和国内的互联网发展进程密切相关。
本博客为学习交流用,凡未注明引用的均为本人作品,转载请注明出处,如有版权问题请及时通知。由于博客时间仓促,错误之处敬请谅解,有任何意见可给我留言,愿共同学习进步。