内容简介:Spring Security(20)——整合Cas
整合 Cas
目录
1.1 配置登录认证
1.1.1 配置 AuthenticationEntryPoint
1.1.2 配置 CasAuthenticationFilter
1.1.3 配置 AuthenticationManager
1.2 单点登出
1.3 使用代理
1.3.3 既为代理端又为被代理端
众所周知, Cas 是对单点登录的一种实现。本文假设读者已经了解了 Cas 的原理及其使用,这些内容在本文将不会讨论。 Cas 有 Server 端和 Client 端, Client 端通常对应着我们自己的应用, Spring Security 整合 Cas 指的就是在 Spring Security 应用中整合 Cas Client ,已达到使用 Cas Server 实现单点登录和登出的效果。本文旨在描述如何在 Spring Security 应用中使用 Cas 的单点登录。
首先需要将 Spring Security 对 Cas 支持的 jar 包加入到应用的类路径中。如果我们的应用使用 Maven 构造的,则可以在应用的 pom.xml 文件中加上如下依赖。
< dependency >
< groupId > org.springframework.security </ groupId >
< artifactId > spring-security- cas </ artifactId >
< version > ${spring.security.version} </ version >
</ dependency >
1.1 配置登录认证
加入了 spring-security-cas-xxx.jar 到 Spring Security 应用的 classpath 后,我们便可以开始配置我们的 Spring Security 应用使用 Cas 进行单点登录了。
1.1.1配置AuthenticationEntryPoint
首先需要做的是将应用的登录认证入口改为使用 CasAuthenticationEntryPoint 。所以首先我们需要配置一个 CasAuthenticationEntryPoint 对应的 bean ,然后指定需要进行登录认证时使用该 AuthenticationEntryPoint 。配置 CasAuthenticationEntryPoint 时需要指定一个 ServiceProperties ,该对象主要用来描述 service ( Cas 概念)相关的属性,主要是指定在 Cas Server 认证成功后将要跳转的地址。
<!-- 指定登录入口为 casEntryPoint -->
< security:http entry-point-ref = "casEntryPoint" >
...
</ security:http >
<!-- 认证的入口 -->
< bean id = "casEntryPoint"
class = "org.springframework.security.cas.web.CasAuthenticationEntryPoint" >
<!-- Cas Server 的登录地址, elim 是我的计算机名 -->
< property name = "loginUrl" value = "https://elim:8443/cas/login" />
<!-- service 相关的属性 -->
< property name = "serviceProperties" ref = "serviceProperties" />
</ bean >
<!-- 指定 service 相关信息 -->
< bean id = "serviceProperties" class = "org.springframework.security.cas.ServiceProperties" >
<!-- Cas Server 认证成功后的跳转地址,这里要跳转到我们的 Spring Security 应用,之后会由 CasAuthenticationFilter 处理,默认处理地址为 /j_spring_cas_security_check -->
< property name = "service"
value = "http://elim:8080/app/j_spring_cas_security_check" />
</ bean >
1.1.2配置CasAuthenticationFilter
之后我们需要配置一个 CasAuthenticationFilter ,并将其放置在 Filter 链表中 CAS_FILTER 的位置,以处理 Cas Server 认证成功后的页面跳转,用以在 Spring Security 中进行认证。该 Filter 会将 Cas Server 传递过来的 ticket ( Cas 概念)封装成一个 Authentication (对应 UsernamePasswordAuthenticationToken ,其中 ticket 作为该 Authentication 的 password ),然后传递给 AuthenticationManager 进行认证。
< security:http entry-point-ref = "casEntryPoint" >
...
< security:custom-filter ref = "casFilter" position = "CAS_FILTER" />
...
</ security:http >
< bean id = "casFilter"
class = "org.springframework.security.cas.web.CasAuthenticationFilter" >
< property name = "authenticationManager" ref = "authenticationManager" />
<!-- 指定处理地址,不指定时默认将会是 “/j_spring_cas_security_check” -->
< property name = "filterProcessesUrl" value = "/j_spring_cas_security_check" />
</ bean >
1.1.3配置AuthenticationManager
CasAuthenticationFilter 会将封装好的包含 Cas Server 传递过来的 ticket 的 Authentication 对象传递给 AuthenticationManager 进行认证。我们知道默认的 AuthenticationManager 实现类为 ProviderManager ,而 ProviderManager 中真正进行认证的是 AuthenticationProvider 。所以接下来我们要在 AuthenticationManager 中配置一个能够处理 CasAuthenticationFilter 传递过来的 Authentication 对象的 AuthenticationProvider 实现, CasAuthenticationProvider 。 CasAuthenticationProvider 首先会利用 TicketValidator ( Cas 概念)对 Authentication 中包含的 ticket 信息进行认证。认证通过后将利用持有的 AuthenticationUserDetailsService 根据认证通过后回传的 Assertion 对象中拥有的 username 加载用户对应的 UserDetails ,即主要是加载用户的相关权限信息 GrantedAuthority 。然后构造一个 CasAuthenticationToken 进行返回。之后的逻辑就是正常的 Spring Security 的逻辑了。
< security:authentication-manager alias = "authenticationManager" >
< security:authentication-provider ref = "casAuthenticationProvider" />
</ security:authentication-manager >
< bean id = "casAuthenticationProvider"
class = "org.springframework.security.cas.authentication.CasAuthenticationProvider" >
<!-- 通过 username 来加载 UserDetails -->
< property name = "authenticationUserDetailsService" >
< bean class = "org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper" >
<!-- 真正加载 UserDetails 的 UserDetailsService 实现 -->
< constructor-arg ref = "userDetailsService" />
</ bean >
</ property >
< property name = "serviceProperties" ref = "serviceProperties" />
<!-- 配置 TicketValidator 在登录认证成功后验证 ticket -->
< property name = "ticketValidator" >
< bean class = "org.jasig.cas.client.validation.Cas20ServiceTicketValidator" >
<!-- Cas Server 访问地址的前缀 ,即根路径 -->
< constructor-arg index = "0" value = "https:// elim:8443/cas" />
</ bean >
</ property >
< property name = "key" value = "key4CasAuthenticationProvider" />
</ bean >
< bean id = "userDetailsService"
class = "org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl" >
< property name = "dataSource" ref = "dataSource" />
</ bean >
经过以上三步配置以后,我们的 Spring Security 应用就已经跟 Cas 整合好了,可以在需要登录的时候通过 Cas Server 进行单点登录了。
1.2 单点登出
Spring Security 应用整合 Cas Client 配置单点登出功能实际和单独使用 Cas Client 配置单点登出功能一样,其根本都是通过配置一个 SingleSignOutFilter 响应 Cas Server 单点登出时的回调,配置一个 SingleSignOutHttpSessionListener 用于在 Session 过期时删除 SingleSignOutFilter 存放的对应信息。 SingleSignOutFilter 需要配置在 Cas 的 AuthenticationFilter 之前,对于 Spring Security 应用而言,该 Filter 通常是配置在 Spring Security 的配置文件中,而且是配置在 CAS_FILTER 之前。所以我们可以在 Spring Security 的配置文件中进行如下配置。
< security:http entry-point-ref = "casEntryPoint" >
<!-- SingleSignOutFilter 放在 CAS_FILTER 之前 -->
< security:custom-filter ref = "casLogoutFilter" before = "CAS_FILTER" />
< security:custom-filter ref = "casFilter" position = "CAS_FILTER" />
...
</ security:http >
< bean id = "casLogoutFilter" class = "org.jasig.cas.client.session.SingleSignOutFilter" />
然后跟单独使用 Cas Client 一样,在 web.xml 文件中配置一个 SingleSignOutHttpSessionListener 。
< listener >
< listener-class > org.jasig.cas.client.session.SingleSignOutHttpSessionListener </ listener-class >
</ listener >
经过以上配置在访问 Cas Server 的 logout 地址(如: https:elim:8443/cas/logout )进行登出时, Cas Server 登出后将回调其中注册的每一个 Service ( Cas 概念,即 client 应用),此时在 client 应用中配置好的 SingleSignOutFilter 将处理对应 Client 应用的登出操作。
虽然以上配置可以满足我们在 Spring Security 应用中的单点登出要求,但 Cas 官方文档和 Spring Security 官方文档都推荐我们在 Cas Client 应用进行登出操作时,不是直接访问 Cas Server 的 logout ,而是先登出本应用,然后告诉用户其当前登出的只是本应用,再提供一个对应 Cas Server 的链接,使其可以进行真正的单点登出。对此, Spring Security 官方文档中给我们提供例子是提供两个 LogoutFilter ,一个是登出当前 Spring Security 应用,一个是登出 Cas Server 的。
< security:http entry-point-ref = "casEntryPoint" >
<!-- 请求登出 Cas Server 的过滤器,放在 Spring Security 的登出过滤器之前 -->
< security:custom-filter ref = "requestCasLogoutFilter" before = "LOGOUT_FILTER" />
<!-- SingleSignOutFilter 放在 CAS_FILTER 之前 -->
< security:custom-filter ref = "casLogoutFilter" before = "CAS_FILTER" />
< security:custom-filter ref = "casFilter" position = "CAS_FILTER" />
...
</ security:http >
< bean id = "requestCasLogoutFilter" class = "org.springframework.security.web.authentication.logout.LogoutFilter" >
<!-- 指定登出成功后需要跳转的地址,这里指向 Cas Server 的登出 URL ,以实现单点登出 -->
< constructor-arg value = "https://elim:8443/cas/logout" />
< constructor-arg >
< bean class = "org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" />
</ constructor-arg >
<!-- 该 Filter 需要处理的地址, 默认是 Spring Security 的默认登出地址 “/j_spring_security_logout”-->
< property name = "filterProcessesUrl" value = "/j_spring_cas_security_logout" />
</ bean >
此外, Spring Security 推荐我们在使用 Cas Server 的单点登出时一起使用 CharacterEncodingFilter ,以避免 SingleSignOutFilter 在获取参数时出现编码问题。
1.3 使用代理
关于 Cas 应用使用代理的基本原理、概念等内容的介绍不在本文讨论范围之内,如需了解请读者参考其它资料,或者 参考我的另一篇博文 。本文旨在描述 Spring Security 应用在整合 Cas 后如何通过 Cas Proxy 访问另一个受 Cas 包含的应用。
使用 Cas Proxy 时有两个主体,代理端和被代理端。而且我们知道代理端和被代理端针对 Cas20ProxyReceivingTicketValidationFilter 的配置是不一样的,虽然整合 Cas 的 Spring Security 应用不再使用 Cas20ProxyReceivingTicketValidationFilter 了,但其底层的核心机制是一样的。所以 Cas 整合 Spring Security 后的应用在作为代理端和被代理端时的配置也是不一样的。接下来将分开讲解 Spring Security 应用作为代理端和被代理端整合 Cas 后的配置。
1.3.1代理端
首先需要为 CasAuthenticationFilter 多指定两个参数, proxyReceptorUrl 和 proxyGrantingTicketStorage 。 proxyReceptorUrl 用以指定 Cas Server 在回调代理端传递 pgtId 和 pgtIou 时回调地址相对于代理端的路径,如“ /proxyCallback ”, CasAuthenticationFilter 会根据 proxyReceptorUrl 来确定一个请求是否来自 Cas Server 针对 proxy 的回调。如果是则需要接收 Cas Server 传递过来的 pgtId 和 pgtIou ,并将它们保存在持有的 ProxyGrantingTicketStorage 中。 CasAuthenticationProvider 之后会从 ProxyGrantingTicketStorage 中获取对应的 pgtId ,即 proxy granting ticket ,并将其保存在 AttributePrincipal 中,而 AttributePrincipal 又会保存到对应的 Assertion 中。
<!-- 配置 ProxyGrantingTicketStorage ,用以保存 pgtId 和 pgtIou -->
< bean id = "proxyGrantingTicketStorage" class = "org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl" />
< bean id = "casFilter"
class = "org.springframework.security.cas.web.CasAuthenticationFilter" >
< property name = "authenticationManager" ref = "authenticationManager" />
<!-- 指定处理地址,不指定时默认将会是 “/j_spring_cas_security_check” -->
< property name = "filterProcessesUrl" value = "/j_spring_cas_security_check" />
< property name = "proxyGrantingTicketStorage" ref = "proxyGrantingTicketStorage" />
< property name = "proxyReceptorUrl" value = "/proxyCallback" />
</ bean >
其次是需要将 CasAuthenticationProvider 持有的 TicketValidator 由 Cas20ServiceTicketValidator 改成 Cas20ProxyTicketValidator 。其需要配置一个 ProxyGrantingTicketStorage 用来获取 proxy granting ticket ,即我们熟知的 pgtId 。在单独使用 Cas Proxy 时, Cas20ProxyReceivingTicketValidationFilter 内部默认持有一个 ProxyGrantingTicketStorage 实现,其使用的 Cas20ProxyTicketValidator 也将使用该 ProxyGrantingTicketStorage 。整合 Spring Security 之后, Spring Security 不使用 Cas20ProxyReceivingTicketValidationFilter ,而直接由 CasAuthenticationFilter 获取 proxy granting ticket ,由 CasAuthenticationProvider 对 ticket 进行校验。 Cas20ProxyTicketValidator 内部没默认的 ProxyGrantingTicketStorage ,所以在配置 Cas20ProxyTicketValidator 时我们需要给其指定一个 ProxyGrantingTicketStorage 实现。此外还需要为 Cas20ProxyTicketValidator 指定一个 proxyCallbackUrl 用以指定在 Cas20ProxyTicketValidator 通过 Cas Server 校验 service ticket 成功后将回调哪个地址以传递 pgtId 和 pgtIou 。 proxyCallbackUrl 默认情况下必须使用 https 协议,而应用的其它请求可以用非 https 协议。其它的配置和 Cas20ServiceTicketValidator 一样, Cas20ProxyTicketValidator 的父类其实就是 Cas20ServiceTicketValidator 。
< bean id = "casAuthenticationProvider"
class = "org.springframework.security.cas.authentication.CasAuthenticationProvider" >
<!-- 通过 username 来加载 UserDetails -->
< property name = "authenticationUserDetailsService" >
< bean class = "org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper" >
<!-- 真正加载 UserDetails 的 UserDetailsService 实现 -->
< constructor-arg ref = "userDetailsService" />
</ bean >
</ property >
< property name = "serviceProperties" ref = "serviceProperties" />
<!-- 配置 TicketValidator 在登录认证成功后验证 ticket -->
< property name = "ticketValidator" >
< bean class = "org.jasig.cas.client.validation.Cas20ProxyTicketValidator" >
<!-- Cas Server 访问地址的前缀 ,即根路径 -->
< constructor-arg index = "0" value = "https://elim:8443/cas" />
<!-- 指定 Cas Server 回调传递 pgtId 和 pgtIou 的地址,该地址必须使用 https 协议 -->
< property name = "proxyCallbackUrl" value = "https://elim:8043/app/proxyCallback" />
< property name = "proxyGrantingTicketStorage" ref = "proxyGrantingTicketStorage" />
</ bean >
</ property >
< property name = "key" value = "key4CasAuthenticationProvider" />
</ bean >
经过以上步骤后我们整合 Cas 后的 Spring Security 应用就可以作为代理端使用 Cas proxy 访问其它被 Cas 保护的应用了,当然前提是其它被代理端能够接受我们应用的代理,了解 Cas Proxy 的人应该都知道这一点,在接下来的 Spring Security 应用整合 Cas 作为被代理端中也会讲到这部分内容。这里我们假设现在有一个应用 app2 能够接受我们应用的代理访问,那么在基于上述配置的应用中我们可以通过如下代码访问 app2 。
@Controller
@RequestMapping ( "/cas/test" )
public class CasTestController {
@RequestMapping ( "/getData" )
public void getDataFromApp(PrintWriter writer) throws Exception {
//1 、从 SecurityContextHolder 获取到当前的 Authentication 对象,其是一个 CasAuthenticationToken
CasAuthenticationToken cat = (CasAuthenticationToken)SecurityContextHolder. getContext ().getAuthentication();
//2 、获取到 AttributePrincipal 对象
AttributePrincipal principal = cat.getAssertion().getPrincipal();
//3 、获取对应的 proxy ticket
String proxyTicket = principal.getProxyTicketFor( "http://elim:8081/app2/getData.jsp" );
//4 、请求被代理应用时将获取到的 proxy ticket 以参数 ticket 进行传递
URL url = new URL( "http://elim:8081/app2/getData.jsp?ticket=" + URLEncoder. encode (proxyTicket, "UTF-8" ));
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
BufferedReader br = new BufferedReader( new InputStreamReader(conn.getInputStream(), "UTF-8" ));
StringBuffer content = new StringBuffer();
String line = null ;
while ((line=br.readLine()) != null ) {
content.append(line).append( "<br/>" );
}
writer.write(content.toString());
}
}
需要注意的是通过 AttributePrincipal 的 getProxyTicketFor() 方法获取 proxy ticket 时,每调用一次都会获取一个全新的 proxy ticket 。用户可以根据自己的需要将获取到的 proxy ticket 按照指定的 URL 缓存起来,以避免每次都去针对同一个 URL 获取一个全新的 proxy ticket 。此外,如果在被代理端认证时根据 proxy ticket 缓存了 Authentication 的话也需要我们在代理端保证针对同一 URL 传递过去的 proxy ticket 是一样的,否则被代理端针对 proxy ticket 缓存 Authentication 的功能就没用了。
1.3.2被代理端
Spring Security 应用整合 Cas 使用 Cas Proxy 作为被代理端时主要需要进行三点修改。
第一点是通过 ServiceProperties 指定 CasAuthenticationFilter 的 authenticateAllArtifacts 为 true ,这样 CasAuthenticationFilter 将会尝试对所有 ticket 进行认证,而不是只认证来自 filterProccessUrl 指定地址的请求。这样代理端在请求被代理端的资源时将 proxy ticket 以参数 ticket 进行传递时, CasAuthenticationFilter 才会让 CasAuthenticationProvider 对 proxy ticket 进行校验,这样我们的请求才有可能被 CasAuthenticationProvider 认证成功并请求到真正的资源。
第二点是指定 CasAuthenticationFilter 使用的 AuthenticationDetailsSource 为 ServiceAuthenticationDetailsSource 。 CasAuthenticationFilter 默认使用的是 WebAuthenticationDetailsSource 。 ServiceAuthenticationDetailsSource 将构建一个 ServiceAuthenticationDetails 对象作为当前 Authentication 的 details 对象。 ServiceAuthenticationDetailsSource 构建的 ServiceAuthenticationDetails 对象会将当前请求的地址构建为一个 serviceUrl ,通过其 getServiceUrl() 方法可以获取到该 serviceUrl 地址。之后该 Authentication 对象传递到 CasAuthenticationProvider 进行认证时就可以从 Authentication 的 details 中获取到对应的 serviceUrl ,并在通过 Cas Server 对代理端以参数 ticket 传递过来的 proxy ticket 进行验证时连同对应的 serviceUrl 一起传递过去。因为之前代理端申请 proxy ticket 时就是通过该 serviceUrl 进行申请的, Cas Server 需要对于它们的配对来验证对应的 proxy ticket 是否有效。
第三点是将 CasAuthenticationProvider 的 TicketValidator 由 Cas20ServiceTicketValidator 改为 Cas20ProxyTicketValidator ,因为 Cas Proxy 被代理端需要调用 Cas Server 的 proxyValidator 对代理端传递过来的 proxy ticket 进行验证。此外需要通过 acceptAnyProxy 或 allowedProxyChains 指定将接受哪些代理。 acceptAnyProxy 用以指定是否接受所有的代理,可选值为 true 或 false 。 allowedProxyChains 则用以指定具体接受哪些代理,其对应的值是代理端在获取 pgtId 时提供给 Cas Server 的回调地址,如我们需要接受前面示例中代理端的代理,则我们的 allowedProxyChains 的值应该是“ https://elim:8043/app/proxyCallback ”。如果需要接受多个代理端的代理,则在指定 allowedProxyChains 时多个代理端回调地址应各占一行。
针对以上三点,我们的 Spring Security 应用整合 Cas 作为 Cas Proxy 的被代理端时需要对我们的配置进行如下改造。
<!-- 指定 service 相关信息 -->
< bean id = "serviceProperties" class = "org.springframework.security.cas.ServiceProperties" >
<!-- Cas Server 认证成功后的跳转地址,这里要跳转到我们的 Spring Security 应用,之后会由 CasAuthenticationFilter 处理,默认处理地址为 /j_spring_cas_security_check -->
< property name = "service"
value = "http://elim:8083/app2/j_spring_cas_security_check" />
<!-- 通过 ServiceProperties 指定 CasAuthenticationFilter 的 authenticateAllArtifacts 为 true -->
< property name = "authenticateAllArtifacts" value = "true" />
</ bean >
< bean id = "casFilter"
class = "org.springframework.security.cas.web.CasAuthenticationFilter" >
< property name = "authenticationManager" ref = "authenticationManager" />
<!-- 指定处理地址,不指定时默认将会是 “/j_spring_cas_security_check” -->
< property name = "filterProcessesUrl" value = "/j_spring_cas_security_check" />
<!-- 通过 ServiceProperties 指定 CasAuthenticationFilter 的 authenticateAllArtifacts 为 true -->
< property name = "serviceProperties" ref = "serviceProperties" />
<!-- 指定使用的 AuthenticationDetailsSource 为 ServiceAuthenticationDetailsSource -->
< property name = "authenticationDetailsSource" >
< bean class = "org.springframework.security.cas.web.authentication.ServiceAuthenticationDetailsSource" />
</ property >
</ bean >
< bean id = "casAuthenticationProvider"
class = "org.springframework.security.cas.authentication.CasAuthenticationProvider" >
<!-- 通过 username 来加载 UserDetails -->
< property name = "authenticationUserDetailsService" >
< bean class = "org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper" >
<!-- 真正加载 UserDetails 的 UserDetailsService 实现 -->
< constructor-arg ref = "userDetailsService" />
</ bean >
</ property >
< property name = "serviceProperties" ref = "serviceProperties" />
<!-- 配置 TicketValidator 在登录认证成功后验证 ticket -->
< property name = "ticketValidator" >
< bean class = "org.jasig.cas.client.validation.Cas20ProxyTicketValidator" >
<!-- Cas Server 访问地址的前缀 ,即根路径 -->
< constructor-arg index = "0" value = "https://elim:8443/cas" />
< property name = "allowedProxyChains" >
< value > https://elim:8043/app/proxyCallback </ value >
</ property >
</ bean >
</ property >
< property name = "key" value = "key4CasAuthenticationProvider" />
</ bean >
此外,对于被代理端而言,代理端在对其进行访问时都被认为是无状态的。对于无状态的认证 CasAuthenticationProvider 将在认证成功后将对应的 Authentication 对象以 proxy tickit 为 key 存放到所持有的 StatelessTicketCache 中,然后在下次代理端访问时将优先根据代理端传递过来的 proxy ticket 从 StatelessTicketCache 中获取 Authentication 对象,如果存在则不再进行认证,否则将继续进行认证。 CasAuthenticationProvider 默认持有的 StatelessTicketCache 为 NullStatelessTicketCache ,其所有的实现都是空的。所以默认情况下,被代理端在被代理端访问时将每次都对代理端进行认证。如果用户不希望在被代理端每次都对代理端的请求进行认证,则可以为被代理端的 CasAuthenticationProvider 指定一个 StatelessTicketCache 。用户可以实现自己的 StatelessTicketCache ,并指定 CasAuthenticationProvider 使用的 StatelessTicketCache 为该 StatelessTicketCache 。不过也可以使用 Spring Security 为我们提供的 EhCacheBasedTicketCache 。 EhCacheBasedTicketCache 是基于 Ehcache 实现的一个 StatelessTicketCache 。以下是一个为 CasAuthenticationProvider 配置 EhCacheBasedTicketCache 的示例。
< bean id = "casAuthenticationProvider"
class = "org.springframework.security.cas.authentication.CasAuthenticationProvider" >
……
< property name = "statelessTicketCache" >
< bean class = "org.springframework.security.cas.authentication.EhCacheBasedTicketCache" >
<!-- Ehcache 对象 -->
< property name = "cache" ref = "proxyTicketCache" />
</ bean >
</ property >
……
</ bean >
<!-- 定义一个 Ehcache -->
< bean id = "proxyTicketCache" class = "org.springframework.cache.ehcache.EhCacheFactoryBean" >
< property name = "cacheName" value = "proxyTicketCache" />
< property name = "timeToLive" value = "600" />
</ bean >
需要注意的是在代理端通过 AttributePrincipal 的 getProxyTicketFor() 方法获取到的 proxy ticket 每次都是不一样的,所以在被代理端通过 StatelessTicketCache 根据 proxy ticket 缓存认证对象 Authentication 时只有在同一 proxy ticket 能够请求多次的情况下才会有用,这也就要求我们在代理端同样能将 proxy ticket 缓存起来,以在请求同一地址时能使用相同的 proxy ticket 。
1.3.3既为代理端又为被代理端
Cas Proxy 的代理端和被代理端是相互独立的,所以一个 Cas 应用既可以作为代理端去访问其它 Cas 应用,也可以作为被代理端被其它应用访问。当 Spring Security 应用整合 Cas 后既想作为 Cas Proxy 的代理端访问其它 Cas 应用,也想作为被代理端被其它 Cas 应用访问时只需要将上述作为代理端的配置和作为被代理端的配置整到一起就行了。以下是一段示例代码。
<!-- 指定 service 相关信息 -->
< bean id = "serviceProperties" class = "org.springframework.security.cas.ServiceProperties" >
<!-- Cas Server 认证成功后的跳转地址,这里要跳转到我们的 Spring Security 应用,之后会由 CasAuthenticationFilter 处理,默认处理地址为 /j_spring_cas_security_check -->
< property name = "service"
value = "http://elim:8080/app /j_spring_cas_security_check" />
< property name = "authenticateAllArtifacts" value = "true" />
</ bean >
<!-- 配置 ProxyGrantingTicketStorage ,用以保存 pgtId 和 pgtIou -->
< bean id = "proxyGrantingTicketStorage" class = "org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl" />
< bean id = "casFilter"
class = "org.springframework.security.cas.web.CasAuthenticationFilter" >
< property name = "authenticationManager" ref = "authenticationManager" />
<!-- 指定处理地址,不指定时默认将会是 “/j_spring_cas_security_check” -->
< property name = "filterProcessesUrl" value = "/j_spring_cas_security_check" />
< property name = "proxyGrantingTicketStorage" ref = "proxyGrantingTicketStorage" />
< property name = "proxyReceptorUrl" value = "/proxyCallback" />
<!-- 通过 ServiceProperties 指定 CasAuthenticationFilter 的 authenticateAllArtifacts 为 true -->
< property name = "serviceProperties" ref = "serviceProperties" />
<!-- 指定使用的 AuthenticationDetailsSource 为 ServiceAuthenticationDetailsSource -->
< property name = "authenticationDetailsSource" >
< bean class = "org.springframework.security.cas.web.authentication.ServiceAuthenticationDetailsSource" />
</ property >
</ bean >
< bean id = "casAuthenticationProvider"
class = "org.springframework.security.cas.authentication.CasAuthenticationProvider" >
<!-- 通过 username 来加载 UserDetails -->
< property name = "authenticationUserDetailsService" >
< bean class = "org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper" >
<!-- 真正加载 UserDetails 的 UserDetailsService 实现 -->
< constructor-arg ref = "userDetailsService" />
</ bean >
</ property >
< property name = "serviceProperties" ref = "serviceProperties" />
<!-- 配置 TicketValidator 在登录认证成功后验证 ticket -->
< property name = "ticketValidator" >
< bean class = "org.jasig.cas.client.validation.Cas20ProxyTicketValidator" >
<!-- Cas Server 访问地址的前缀 ,即根路径 -->
< constructor-arg index = "0" value = "https://elim:8443/cas" />
<!-- 指定 Cas Server 回调传递 pgtId 和 pgtIou 的地址,该地址必须使用 https 协议 -->
< property name = "proxyCallbackUrl" value = "https://elim:8043/app/proxyCallback" />
< property name = "proxyGrantingTicketStorage" ref = "proxyGrantingTicketStorage" />
<!-- 作为被代理端时配置接收任何代理 -->
< property name = "acceptAnyProxy" value = "true" />
</ bean >
</ property >
< property name = "key" value = "key4CasAuthenticationProvider" />
</ bean >
关于 Cas 的更多内容可以参考 Cas 的官方文档,或者参考我的其它关于 Cas 的博客。
(注:本文是基于 Spring Security3.1.6 、 Cas Server3.5.2 所写)
以上所述就是小编给大家介绍的《Spring Security(20)——整合Cas》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- SpringBoot整合MybatisPlus的简单教程(简单整合)
- springmvc教程--整合mybatis开发(spring+springMVC+mybatis整合开发)
- springboot整合springsecurity从Hello World到源码解析(五):springsecurity+jwt整合restful服务
- SSM整合搭建(二)
- SSM整合
- Storm 整合 Hbase
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
PHP经典实例(第3版)
David Sklar、Adam Trachtenberg / 苏金国、丁小峰 / 中国电力出版社 / 2015-7 / 128.00
想要掌握PHP编程技术?或者想要学习如何完成一个特定的任务?那么一定要先看看《PHP经典实例(第3版)》。本书介绍了专门为PHP 5.4和5.5修订的350个经典技巧,并提供了丰富的示例代码。特别是对生成动态Web内容的解决方案做了全面更新,从使用基本数据类型到查询数据库,从调用RESTful API到测试和保护网站安全都有涵盖。 各个技巧都提供了示例代码,可以免费使用,另外还讨论了如何解决......一起来看看 《PHP经典实例(第3版)》 这本书的介绍吧!