CAS 统一认证集成

1. 前言

在前面的小节中,我们介绍了两种统一身份授权规范 OAuth2.0 和 SAML2.0,本节我们介绍另一种统一认证解决方案:「CAS」服务。

CAS 是由 JA-SIG 提供的企业级单点认证解决方案,推出以来在企业 SSO 场景中被广泛使用。Spring Security 完整的支持了 CAS,为多应用单点认证提供了快速集成方案。

更多关于 CAS 的介绍可以从此获得:JA-SIG/CAS

2. CAS 工作原理

CAS 是一套开源的企业级 SSO 项目,是 B / S 服务架构,支持多种通讯协议。CAS 的主要成员有 CAS 服务端和 CAS 客户端。

图片描述

2.1 CAS 服务端

CAS 服务端是一套基于 Spring 框架构建的应用系统,是实现 SSO 的核心构成。其软件层面可分为三个层次:

  • Web 服务(Spring MVC、Spring Webflow)
  • 票据
  • 认证系统

2.2 CAS 客户端

CAS 客户端是指可以和 CAS 服务端进行安全通讯的业务系统。CAS 通讯支持以下协议:

  • CAS(版本1,2,3)
  • SAML 1.1
  • OpenID
  • OAuth(1.0,2.0)

3. Spring Security 集成 CAS

3.1 交互流程

在Spring Security 集成 CAS 认证的过程中,共有三个核心组成部分:浏览器、CAS 认证中心和基于 Spring Security 构建的资源服务。

浏览器、认证中心、资源服务之间的交互流程如下:

  1. 当用户访问开放资源时,无需经过 CAS 认证中心或者资源服务器上的安全过滤器;
  2. 当用户访问私密资源时,资源服务器 ExceptionTranslationFilter 将会接收到 AccessDeniedException 或是 AuthenticationException,要求用户进行认证;
  • 资源服务器在过滤器 ExceptionTranslationFilter 中查找 CAS 认证相关配置 CasAuthenticationEntryPoint,如有则认定使用了 CAS 认证;
  • 资源服务器通过读取配置CasAuthenticationEntryPoint, 将用户浏览器地址跳转到认证中心,并指定返回地址;
  • 认证中心查看 Cookies 中是否保存了用户信息,如有直接认证,如果没有,要求用户填入其用户名密码;
  • 认证中心判断登录是否成功,浏览器将跳回原服务地址,并携带票据参数;
  • 回到资源服务后,CasAuthenticationFilter 会持续监听 /login/cas 地址的请求,过滤器会生成 UsernamePasswordAuthenticationToken 用来保存票据信息,并将认证状态置为已通过;
  • 资源服务的认证管理器 AuthenticationManager 传递票据到认证中心验证用户登录信息;
  • 认证中心认证完成后,应用服务会检查 CAS 返回的 XML 内容,判断是允许访问、拒绝访问或者是其他策略;
  • 通过之后,资源服务的认证管理器会继续获取用户的权限信息;
  • 如果获取成功,认证中心将生成认证凭证 Token 并保存到安全上下文;
  • 用户浏览器回到最初访问地址,并且顺利获得资源。

3.2 配置 CAS 客户端

从前面的流程中我们看到,与 CAS 认证中心集成过程中,Spring Security 用来构建资源服务,也就是 CAS 的认证客户端。此处我们来看如何通过 Spring Security 配置 CAS 认证客户端。

3.2.1 服务票据认证

集成的第一步是配置资源服务获取到 Token 之后,如何到 CAS 认证中心去验证。多数情况下,这一步是必须的。我们需要在 Spring 上下文中创建 ServiceProperties Bean 对象。

该对象必须配置 service 属性,其值为一个 URL 地址,用来被过滤器 CasAuthenticationFilter 侦听。配置代码如下:

<bean id="serviceProperties"
    class="org.springframework.security.cas.ServiceProperties">
<property name="service"
    value="https://localhost:8443/cas-sample/login/cas"/>
<property name="sendRenew" value="false"/>
</bean>

接下来配置 CAS 认证相关过滤器信息,这些配置基本固定。

<security:http entry-point-ref="casEntryPoint">
...
<security:custom-filter position="CAS_FILTER" ref="casFilter" />
</security:http>

<bean id="casFilter"
    class="org.springframework.security.cas.web.CasAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
</bean>

<bean id="casEntryPoint"
    class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
<property name="loginUrl" value="https://localhost:9443/cas/login"/>
<property name="serviceProperties" ref="serviceProperties"/>
</bean>

完成这步后,我们继续配置 CAS 认证管理器,添加测试用的用户信息。

<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="casAuthenticationProvider" />
</security:authentication-manager>

<bean id="casAuthenticationProvider"
    class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
<property name="authenticationUserDetailsService">
    <bean class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
    <constructor-arg ref="userService" />
    </bean>
</property>
<property name="serviceProperties" ref="serviceProperties" />
<property name="ticketValidator">
    <bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
    <constructor-arg index="0" value="https://localhost:9443/cas" />
    </bean>
</property>
<property name="key" value="an_id_for_this_auth_provider_only"/>
</bean>

<security:user-service id="userService">
<security:user name="joe" password="{noop}joe" authorities="ROLE_USER" />
</security:user-service>

注意,CAS 体系下的认证是交由 CAS 认证中心完成的,这一步中配置的密码并不会用于认证,此处的配置是为了决定认证后该用户所具有的权限。

至此,CAS 认证客户端的配置完成。

3.2.2 单点注销

CAS 认证协议中,包含了明确的单点注销流程,我们可以便捷的集成到我们的认证客户端之中,配置信息如下:

<security:http entry-point-ref="casEntryPoint">
...
<security:logout logout-success-url="/cas-logout.jsp"/>
<security:custom-filter ref="requestSingleLogoutFilter" before="LOGOUT_FILTER"/>
<security:custom-filter ref="singleLogoutFilter" before="CAS_FILTER"/>
</security:http>

<!-- 该过滤器用于接收来自 CAS 认证中心的登出请求。(由其他应用发起登出) -->
<bean id="singleLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter"/>

<!-- 该过滤器用于向 CAS 认证中心发送登出请求。(由本应用发起登出) -->
<bean id="requestSingleLogoutFilter"
    class="org.springframework.security.web.authentication.logout.LogoutFilter">
<constructor-arg value="https://localhost:9443/cas/logout"/>
<constructor-arg>
    <bean class=
        "org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>
</constructor-arg>
<property name="filterProcessesUrl" value="/logout/cas"/>
</bean>

登出操作由某一 CAS 客户端发起,CAS 认证中心接收到之后,会向所有该用户已登录应用发起登出通知,最后实现全部应用的登出效果。

4. 小结

本节介绍了 CAS 认证体系的工作流程,Spring Security 可方便的实现 CAS 认证客户端功能,CAS 认证体系下支持单点登出操作,需要每一个加入体系的应用配置用于处理单点登出的过滤器。

下节我们将介绍 Java 早期的安全框架 JAAS,已经如何用 Spring Security 集成 JAAS 认证。