本文转载自:http://www.coin163.com/java/docs/201305/d_2785029006.html
一、 CAS 简介
1.1 CAS 是什么
CAS (Central Authentication Service) 是 Yale 大学发起的一个 Java 开源项目,旨在为 Web应用系统提供一种可靠的 单点登录 解决方案( Web SSO ), CAS 在 2004 年 12 月正式成为 JA-SIG 的一个项目。 CAS 具有以下特点:
1、 开源的企业级单点登录解决方案;
2、 CAS Server 为需要独立部署的 Web 应用;
3、 CAS Client 支持非常多的客户端 ( 指单点登录系统中的各个 Web 应用 ) ,包括 Java, .Net, PHP, Perl, 等。
1.2 CAS 原理
从结构上看, CAS 包含两个部分: CAS Server 和 CAS Client 。
CAS Server 需要独立部署,主要负责对用户的认证工作, 它会处理用户名 / 密码等凭证 (Credentials) ;
CAS Client 部署在客户端, 负责处理 对本地 Web 应用(客户端)受保护资源的访问请求,并且当需要对请求方进行身份认证时,重定向到 CAS Server 进行认证, 下图是 CAS 最基础协议:
1 、 CAS Client 与受保护的客户端应用部署在一起,以 Filter 方式保护受保护的资源。对于访问受保护资源的每个 Web 请求, CAS Client 会分析该请求的 Http 请求中是否包含 Service Ticket ( ST )和 Ticket Granting tieckt(TGT) (??),如果没有,则说明当前用户尚未登录,于是将请求重定向到指定好的 CAS Server 登录地址,并传递 Service (也就是要访问的目的资源地址),以便登录成功过后转回该地址。用户在第 3 步中输入认证信息,如果登录成功, CAS Server 随机产生一个相当长度、唯一、不可伪造的 Service Ticket ,并缓存以待将来验证,之后系统自动重定向到 Service 所在地址,并为客户端浏览器设置一个 Ticket Granted Cookie ( TGC ), CAS Client 在拿到 Service 和新产生的 Ticket 过后,在第 5 , 6 步中与 CAS Server 进行身份核实,以确保 Service Ticket 的合法性。
2 、在该协议中,所有与 CAS 的交互均采用 SSL 协议确保 ST 和 TGC 的安全性。协议工作过程中会有 2 次重定向的过程,但是 CAS Client 与 CAS Server 之间进行 Ticket 验证的过程对于用户是透明的。
3 、 CAS 如何实现 SSO
当用户访问另一服务再次被重定向到 CAS Server 的时候, CAS Server 会主动获到这个 TGC cookie ,然后做下面的事情:
1) 如果 User 的持有 TGC 且其还没失效,那么就走基础协议图的 Step4 ,达到了 SSO 的效果;
2) 如果 TGC 失效,那么用户还是要重新认证 ( 走基础协议图的 Step3) 。
1.3 CAS 相关词汇及概念
TGC(ticket-granting cookie)-- ------- 授权的票据证明,由 CAS Server 通过 SSL 方式发送给终端用户;
KDC( Key Distribution Center ) ---------- 密钥发放中心;
ST (Service ticket) --------- 服务票据, 由 KDC 的 TGS 发放, ST 只能被尝试验证一次。 任何一台 Workstation 都需要拥有一张有效的 Service Ticket 才能访问域内部的应用 (Applications) 。 如果能正确接收 Service Ticket ,说明在 CASClient-CASServer 之间的信任关系已经被正确建立起来,通常为一张数字加密的证书;
Ticket Granting tieckt(TGT) --------- 票据授权票据,由 KDC 的 AS 发放。即获取这样一张票据后,以后申请各种其他服务票据 (ST) 便不必再向 KDC 提交 Credentials (凭证或身份认证信息 ) ;
Authentication Service (AS) --------- 认证服务,索取 Crendential ,发放 TGT ;
Ticket-Granting Service (TGS) --------- 票据授权服务,索取 TGT ,发放 ST 。
1.4 CAS 安全性
CAS 的安全性从 v1 到 v3 ,都很依赖于 SSL ,它假定了这样一个事实,用户在一个非常不安全的网络环境中使用 SSO , Hacker 的 Sniffer 会很容易抓住所有的 Http Traffic ,包括通过 Http传送的密码甚至 Ticket 票据。
1、 TGC 安全性
对于一个 CAS 用户来说,最重要是要保护它的 TGC ,如果 TGC 不慎被 CAS Server 以外的实体获得, Hacker 能够找到该 TGC ,然后冒充 CAS 用户访问所有授权资源。
TGC 是 CAS Server 通过 SSL 方式发送给终端用户,因此,要截取 TGC 难度非常大,从而确保CAS 的安全性。但 CAS 的传输安全性仅仅依赖于 SSL 。
TGC 面临的风险 主要并非传输窃取,而是存活周期内( logout 前)被使用(如:离开了电脑)或窃取。
TGC 有自己的存活周期。可以在 web.xml 中,通过 grantingTimeout 来设置 CAS TGC 存活周期的参数,参数默认是 120 分钟,在合适的范围内设置最小值,太短,会影响 SSO 体验,太长,会增加安全性风险。
2、 ST 安全性
ST ( Service Ticket )是通过 Http 传送的,网络中的其他人可以 Sniffer 到其他人的 Ticket 。CAS 协议从几个方面让 Service Ticket 变得更加安全:
1) ST 只能使用一次: CAS 协议规定,无论 ST 验证是否成功, CAS Server 都会将服务端的缓存中清除该 Ticket ,从而可以确保一个 Service Ticket 不被使用两次;
2) ST 在一段时间内失效:假设用户拿到 ST 之后,他请求服务的过程又被中断了, ST 就被空置了,事实上,此时 TS 仍然有效。 CAS 规定 Service Ticket 只能存活一定的时间,然后 CAS Server 会让它失效。可通过在 web.xml 中配置参数,让 ST 在多少秒内失效。
3) ST 是基于随机数生成的。
二、 Spring Security 简介
2.1 Spring Security 是什么
Spring Security 开始于 2003 年底,那时候叫 “spring 的 Acegi 安全系统 ” 。 Acegi 在 2007年底,正式成为 spring 组合项目,被更名为 “Spring Security” 。
Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案。一般来说, Web应用的安全性包括用户认证( Authentication )和用户授权( Authorization )两个部分。
用户认证指的是验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认证过程。
用户授权指的是验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。
对于上面提到的两种应用情景, Spring Security 框架都有很好的支持。在用户认证方面, Spring Security 框架支持主流的认证方式,包括 HTTP 基本认证、 HTTP 表单验证、 HTTP 摘要认证、 OpenID和 LDAP 等。在用户授权方面, Spring Security 提供了 基于角色 的访问控制和访问控制列表( Access Control List , ACL ),可以对应用中的领域对象进行细粒度的控制。
2.3 如何控制权限
1、 概要
Spring 使用由 Filter 组成的 Chain ,来判断权限。 Spring 预定义了很多 out-of-boxed filter供开发者直接使用。每个 Filter 一般情况下(有些 Filter 是 abstract 的)都和配置文件的一个元素(有的情况下可能是属性)对应。比如: AUTHENTICATION_PROCESSING_FILTER ,对应配置文件里面的: http/form-login 元素。
如果 Spring 提供的 Filter 不能满足系统的权限功能,开发者可以自定义 Filter ,然后把 Filter 放在某个 Filter Chain 的某个位置。可以替换掉原有 Filter Chain 的某个 Filter ,也可以放在某个 Filter 之前或者之后。
总之, Spring Security 采用 Filter Chain 模式判断权限, Spring 提供了一些 Filter ,也支持开发者自定义 Filter 。
2、 控制内容
Spring Security 提供对 URL 、 Bean Method 、 Http Session 、领域对象这四种内容的控制,分别如下:
1) URL
可以分为需要权限判断的 url ,不需要权限判断的 url ,登录表单 url 。需要权限判断的 url ,仅限于做角色判断,就是说判断当前用户是否具有指定的角色。
2) Bean Method
Spring 支持对 Service layer method 做权限判断。也仅限于做角色判断。配置方式有 2 种:
Ø Annotation : 写在 Java 源代码里面( Annotation ),如: @Secured("ROLE_TELLER") (该方法只有具有 TELLER 角色的用户能够访问,否则抛出异常);
Ø 写在配置文件里面,如:
3) Http Session
控制一个用户名是否能重复登录,以及重复登录次数,并非重试密码次数。
4) 领域对象(待完善)
复杂程序常常需要定义访问权限,不是简单的 web 请求或方法调用级别。而是,安全决议,需要包括谁(认证),哪里( MethodInvocation )和什么(一些领域对象)。换而言之,验证决议也需要考虑真实的领域对象实例,方法调用的主体。主要通过 ACL (访问控制列表)实现。
2.4 权限控制的几种配置方法
在 Spring Security 3 的使用中,权限控制有 4 种配置方法:
1、 全部利用配置文件,将用户、权限、资源 (url) 硬编码在 xml 文件中;
2、 用户和权限用数据库存储,而资源 (url) 和权限的对应采用硬编码配置;
3、 细分角色和权限,并将用户、角色、权限和资源均采用数据库存储,并且自定义过滤器,代替原有的 FilterSecurityInterceptor 过滤器,并分别实现 AccessDecisionManager 、 InvocationSecurityMetadataSourceService 和 UserDetailsService ,并在配置文件中进行相应配置;(将主要考虑该方法)
4、 修改 spring security 的源代码,主要是修改 InvocationSecurityMetadataSourceService 和 UserDetailsService 两个类。 前者是将配置文件或数据库中存储的资源 (url) 提取出来加工成为 url 和权限列表的 Map 供 Security 使用,后者提取用户名和权限组成一个完整的 (UserDetails)User 对象,该对象可以提供用户的详细信息供 AuthentationManager 进行认证与授权使用。比较暴力,不推荐。
2.5 主要内置对象或内容
1、 Spring Security 默认的过滤器顺序列表
order |
过滤器名称 |
备注 |
100 |
ChannelProcessingFilter |
|
200 |
ConcurrentSessionFilter |
|
300 |
SecurityContextPersistenceFilter |
|
400 |
LogoutFilter |
|
500 |
X509AuthenticationFilter |
|
600 |
RequestHeaderAuthenticationFilter |
|
700 |
CasAuthenticationFilter |
|
800 |
UsernamePasswordAuthenticationFilter |
|
900 |
OpenIDAuthenticationFilter |
|
1000 |
DefaultLoginPageGeneratingFilter |
|
1100 |
DigestAuthenticationFilter |
|
1200 |
BasicAuthenticationFilter |
|
1300 |
RequestCacheAwareFilter |
|
1400 |
SecurityContextHolderAwareRequestFilter |
|
1500 |
RememberMeAuthenticationFilter |
|
1600 |
AnonymousAuthenticationFilter |
|
1700 |
SessionManagementFilter |
|
1800 |
ExceptionTranslationFilter |
|
1900 |
FilterSecurityInterceptor |
|
2000 |
SwitchUserFilter |
|
2、
2.5 主要应用模式
1、 自定义授权管理
自定义 Filter 以及相关辅助类,实现用户、角色、权限、资源的数据库管理,涉及相关接口或类说明如下:
1) AbstractSecurityInterceptor
具体实现类作为过滤器,该过滤器要插入到授权之前。在执行 doFilter 之前,进行权限的检查,而具体的实现交给 accessDecisionManager 。
2) FilterInvocationSecurityMetadataSource
具体实现类在初始化时,要实现从数据库或其它存储库中加载所有资源与权限(角色),并装配到 MAP
3) UserDetailService
具体实现类从存储库中读取特定用户的各种信息(用户的密码,角色信息,是否锁定,账号是否过期等)。唯一要实现的方法: public UserDetails loadUserByUsername(String username)
4) AccessDecisionManager
匹配权限以决定是否放行。主要实现方法: public void decide (Authentication authentication, Object object,
Collection
//In this method, need to compare authentication with configAttributes.
Ø A object is a URL, a filter was find permission configuration by this URL, and pass to here.
Ø Check authentication has attribute in permission configuration (configAttributes)
Ø If not match corresponding authentication, throw a AccessDeniedException.
2.6 Spring Security 评价
在 Spring Security 世界里,可以区分出哪些资源可以匿名访问,哪些需要角色权限,哪个页面提供登录功能;怎样进行用户身份认证,用户的密码如何加密。哪些资源必须使用 https 协议,资源和访问端口有怎样的对应关系。
下面就优点和缺点对 Spring Security 进行点评。
1、 优点
总体说来 Spring Security 具有以下几个优点:
1) 提供了一套权限框架,这套框架是可行的;
2) 提供了很多用户身份认证功能,可以节约大量开发工作;
3) 提供了角色判断功能,这点既是优点又是缺点;
4) 提供了 form-login 、 remember me 等控制。
其中 2 、 4 两点,对于我们中国开发者,可用性并不大。我们的系统大多采用用户名 / 密码身份认证模式,大多公司都有可复用代码。 form-login 、 remember me 等这些功能,并不是难以开发,而且可用之处也并不多。
2、 缺点
Spring Security 存在以下几个硬伤:
1) 角色被“编码”到配置文件和源文件,这样最终用户就不能创建角色了。但最终用户期望自己来控制角色。因为在项目实施过程中,客户可能并不能确定有哪些角色,以及角色怎么分配给系统用户。角色大多需要等到系统上线后,才能确定。这些编码有:
a) url 的权限控制,
b) java 方法的权限控制, @Secured("IS_AUTHENTICATED_ANONYMOUSLY") ;
c) java 方法的权限控制,
2) RBCA 这种被广泛运用的模型,没有在 Spring Security 体现出来;
3) Spring Security 没有提供好的细粒度(数据级)权限方案,提供的缺省实现维护工作量大,在大数据量情况下,几乎不可用;
4) Spring Security 对于用户、角色、权限之间的关系,没有提供任何一种维护界面。不过从Spring Security 角度看,确实没有必要有界面。角色创建、角色和权限直接的关系,都被“编码”到配置文件和源文件了;
5) Spring Security 学习难度大,配置文件还是很多。
三、 CAS Server 与 LDAP 整合
CAS Server 与 LDAP 整合,主要是使用 LDAP 来进行统一认证。
3.1 环境需求
1 、 CAS 服务包: cas-server-3.3.5-release
2 、 LDAP : apacheds-1.5.7-setup ( LDAP )
3 、 Tomcat : tomcat-6.0.18
4 、 JDK : jdk1.5+
3.1 搭建 CAS Server
1、 生成服务器证书(以下方式包含了双向认证)
部署 CAS3 服务器端所需得 ssl 环境生成 , 预备生成文件 : cacerts server.keystore client.keystore , server.cer client.cer
1 )生成服务器端库文件 ( 生成的时候, 第一项 name 一定要为完整计算机名称或域名 )
keytool -genkey -alias tomcat-server -keyalg RSA -keypass changeit -storepass changeit -keystore server.keystore
2 )导出服务器端证书
keytool -export -alias tomcat-server -storepass changeit -file server.cer -keystore server.keystore
3 )生成客户端库文件
keytool -genkey -alias tomcat-client -keyalg RSA -keypass changeit -storepass changeit -keystore client.keystore
4 )导出客户端证书
keytool -export -alias tomcat-client -storepass changeit -file client.cer -keystore client.keystore
5 )导入服务器端证书
keytool -import -trustcacerts -alias server -file server.cer -keystore cacerts -storepass changeit
6 )导入客户端证书
keytool -import -trustcacerts -alias client -file client.cer -keystore cacerts -storepass changeit
将如上生成的 cacerts , server.keystore , client.keystore , server.cer , client.cer 文件 分别拷贝到 cas 服务器以及应用客户端( server.cer ) TOMCAT_HOME 主目录以及JAVA_HOEM/jre/lib/security 文件下 ( 每个子系统的 tomcat 也要拷贝进去 ) 。
2、 部署 CAS Server
1) 将下载后的 cas-server-3.3.5-release.zip 解压后,进入 cas-server-3.3.5\modules 目录,将 cas-server-webapp-3.3.5.war 重命名为: cas.war 并将它拷贝到%TOMCAT_HOME%/webapps/ 下;
2) 启动 tomcat 后,停止 tomcat, 然后看到有一个 cas 目录,有的话把 cas.war删除掉;
3) 修改 TOMCAT 配置文件: tomcat/conf/server.xml
放开注释改成如下内容 :
maxThreads="150" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" keystoreFile="E:\servers\tomcat-6.0.18-casServer\server.keystore" keystorePass="changeit" /> 注意:证书的路径和密码需做对应修改。 4) 启动 TOMCAT 在 IE 地址栏输入: https://localhost(IP):8443/cas ,如显示如下图,表明成功: 5) 3.2 LDAP 服务安装 见附件“ LDAP 服务安装说明 .doc ”。 3.3 配置 CAS Server 与 LDAP 1 、 修改 webapps\cas\WEB-INF\deployerConfigContext.xml 文件 具体文件配置:注释用” ” 2 、在刚才注释的那个位置下面输入: 该段内容主要是加入 LDAP 的过滤条件及访问目录。 3 、在配置文件的倒数第 2 行,加入如下代码配置 ( 主要为需要访问的 LDAP 服务地址及用户名、密码 ) : 3.4 验证 CAS Server 与 LDAP 在 IE 地址栏输入 “ https://localhost:8443/cas “ , 并用 LDAP 建立的用户名及密码登录,正常情况如下图所示: 四、 CAS Client 与 Spring Security 整合 4.1 环境需求 1 、 CAS Client : cas-client-core-3.2.1.jar 放入 Web 应用的 lib 中 2 、 Spring Security : spring-security-cas-client-3.0.2.RELEASE.jar ,在基于 spring security 项目中加入 cas 相应的依赖 jar 包 4.2 搭建 CAS Client (即 Spring Security 应用) 1、 导入服务端生成证书 复制 cas 服务端生成证书 server.cer 到客户端( TOMCAT_Home 下),并将证书导入 JDK 中 , keytool -import -trustcacerts -alias casserver -file server.cer -keystore D:\Java\jre1.6.0_02\lib\security\cacerts -storepass changeit 注此处的 jre 必须为 JDK 路径下的 jre 。 2、 配置 CAS Client 应用的 web.xml 增加如下:
< context-param > < param-name > contextConfigLocation param-name > < param-value > classpath:applicationContext.xml,classpath:applicationContext-security.xml param-value > context-param > < filter > < filter-name > encodingFilter filter-name > < filter-class > org.springframework.web.filter.CharacterEncodingFilter filter-class > < init-param > < param-name > encoding param-name > < param-value > UTF-8 param-value > init-param > filter > < filter-mapping > < filter-name > encodingFilter filter-name > < url-pattern > /* url-pattern > filter-mapping > < filter > < filter-name > springSecurityFilterChain filter-name > < filter-class > org.springframework.web.filter.DelegatingFilterProxy filter-class > filter > < filter-mapping > < filter-name > springSecurityFilterChain filter-name > < url-pattern > /* url-pattern > filter-mapping > < listener > < listener-class > org.springframework.web.context.ContextLoaderListener listener-class > listener > 3、 配置 applicationContext-security-ns.xml xml version = "1.0" encoding = "UTF-8" ?> < beans:beans xmlns = "http://www.springframework.org/schema/security" xmlns:beans = "http://www.springframework.org/schema/beans" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation = "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd" > < http auto-config = "false" entry-point-ref = "casAuthenticationEntryPoint"access-denied-page = "/common/403.jsp" > < intercept-url pattern = "/jsp/forbidden/**" access = "ROLE_USER" > intercept-url > < intercept-url pattern = "/**" filters = "none" > intercept-url > < custom-filter position = "CAS_FILTER" ref = "casAuthenticationFilter" /> http > < global-method-security secured-annotations = "enabled" jsr250-annotations ="enabled" > < protect-pointcut expression = "execution(* com.coin.acs.demo.bo.*Service.*(..))" access = "ROLE_USER" /> < protect-pointcut expression = "execution(* com.coin.acs.demo.bo.*AOP.update*(..))" access = "ROLE_USER" /> global-method-security > < beans:bean id = "casAuthenticationEntryPoint" class = "org.springframework.security.cas.web.CasAuthenticationEntryPoint"> < beans:property name = "loginUrl" value = "https://localhost:8443/cas/login" /> < beans:property name = "serviceProperties" ref = "serviceProperties" /> beans:bean > < beans:bean id = "serviceProperties" class = "org.springframework.security.cas.ServiceProperties" > < beans:property name = "service" value = "http://localhost:9001/client/j_spring_cas_security_check" /> < beans:property name = "sendRenew" value = "false" /> beans:bean > < authentication-manager alias = "authenticationManager" > < authentication-provider ref = "casAuthenticationProvider" /> authentication-manager > < beans:bean id = "casAuthenticationFilter" class = "org.springframework.security.cas.web.CasAuthenticationFilter" > < beans:property name = "authenticationManager" ref = "authenticationManager" /> beans:bean > < beans:bean id = "casAuthenticationProvider" class = "org.springframework.security.cas.authentication.CasAuthenticationProvider" > < beans:property name = "authenticationUserDetailsService" ref = "authenticationUserDetailsService" /> < beans:property name = "serviceProperties" ref = "serviceProperties" /> < beans:property name = "ticketValidator" > < beans:bean class = "org.jasig.cas.client.validation.Cas20ServiceTicketValidator" > < beans:constructor-arg index = "0" value = "https://localhost:8443/cas" /> beans:bean > beans:property > < beans:property name = "key" value = "cas" /> beans:bean > < beans:bean id = "authenticationUserDetailsService" class = "org.springframework.security.cas.userdetails.GrantedAuthorityFromAssertionAttributesUserDetailsService" > < beans:constructor-arg > < beans:array > < beans:value > authorities beans:value > beans:array > beans:constructor-arg > beans:bean > beans:beans > 配置完毕。 五、 参考资料 1、 Spring Security-3.0.1 中文官方文档 2、 http://metadmin.iteye.com/blog/364132 “ Spring Security 优劣之我见” 3、 http://dead-knight.iteye.com/blog/1520080