目录
1.1.1 配置AuthenticationEntryPoint
1.1.2 配置CasAuthenticationFilter
众所周知,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")
publicclass CasTestController {
@RequestMapping("/getData")
publicvoid 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所写)
(注:原创文章,转载请注明出处。原文地址:http://elim.iteye.com/blog/2270446)
相关推荐
7. **配置Spring Security**:Spring Security可以与CAS客户端配合,提供更高级的安全控制。例如,可以通过`CasAuthenticationProvider`和`CasProcessingFilterEntryPoint`来处理用户认证和登录失败的场景。 8. **...
根据提供的文件信息,我们可以归纳总结出一系列与Spring Security相关的知识点,包括基础知识、Web安全保护以及高级功能等。下面是这些知识点的详细阐述: ### 基础篇 #### 1. 一个简单的Hello World - **配置...
wdaaaaaaaaaafwawfw
scratch少儿编程逻辑思维游戏源码-十字鸭子.zip
Android移动应用开发_ViewFlow自定义控件CircleFlowIndicator指示器网络图片加载缓存自动循环滚动手势滑动交互_横向循环平滑滚动广告条Banne
前端开发_基于jQuery和EasyUI框架_企业级Web应用UI组件库与后台管理系统模板_提供GPL开源版本和商业授权版本的双重授权模式_适用于快速构建响应式管理后台和复杂数据可
《C++编程实例100篇》是一本深入实践、极具价值的编程教程,它针对C++编程语言提供了丰富的实例,旨在帮助读者更好地理解和掌握C++的各项特性与编程技巧。这本书的经典之处在于它将理论与实践相结合,通过100个精心设计的编程实例,覆盖了C++的各个核心领域,包括基础语法、面向对象编程、模板、异常处理、STL(标准模板库)等。 我们来探讨C++的基础语法。C++是C语言的增强版,它保留了C语言的高效性和灵活性,并引入了类、对象和继承等面向对象编程概念。基础语法包括变量声明、数据类型、运算符、控制结构(如if语句、for循环、while循环)、函数的定义和调用等。在实例中,你可能会遇到如何编写简单的程序,如计算两个数的和,或者实现一个简单的猜数字游戏。 C++的面向对象编程是其一大特色。通过类和对象,你可以构建复杂的软件系统。类是对象的蓝图,它定义了对象的属性和行为。实例化一个类,就是创建一个具体的对象。继承允许你创建新的类,这些类从现有的类派生,共享其属性和方法,同时可以添加新的功能。多态性是面向对象的另一个关键特性,它使得不同类型的对象可以对同一消息作出不同的响应。这些概念在实例中会以各种形式展现,例如设计一个图形界面的类层次,或实现一个简单的模拟游戏。 接下来是模板,C++的模板功能让代码更加通用,可以处理不同类型的数据。模板分为函数模板和类模板,前者可以创建泛型函数,后者可以创建泛型类。通过模板,你可以编写出高效且灵活的代码,比如实现一个通用的排序算法。 异常处理是C++中用于处理程序运行时错误的机制。当程序出现异常情况时,可以抛出一个异常,然后在适当的点捕获并处理这个异常。这使得代码能够优雅地处理错误,而不是让程序崩溃。实例中可能会有涉及文件操作或网络通信时可能出现的异常处理示例。
scratch少儿编程逻辑思维游戏源码-忍者无限跑者.zip
少儿编程scratch项目源代码文件案例素材-战斗机游戏.zip
手工DIY行业_微信小程序云开发_手工坊预约管理系统_基于腾讯云开发技术的手工DIY行业线上线下结合解决方案_包含手工项目展示预约管理签到核销数据统计等完整功能_支持后台时段设置人
内容概要:本文档汇总了FFmpeg工具的多个实用命令示例,涵盖音频和视频处理的各个方面。包括但不限于:音频采样率转换、音频格式转为raw、查询音频文件时长与音量检测、调整音频音量大小、改变音频播放速度、视频逆序播放(针对无音频视频)、精准视频片段截取、音频片段截取、视频与音频合并处理、调整视频宽高比等。每个命令都提供了具体的参数设置方法; 适合人群:音视频编辑爱好者、多媒体处理领域的初学者或有一定经验的技术人员; 使用场景及目标:①在音频处理方面,可以完成从格式转换到音质调整的任务;②在视频处理上,能够实现基本的剪辑操作如切割、翻转、合并等;③为用户提供了一站式的解决方案,帮助用户快速掌握FFmpeg命令行工具的基本用法,提高工作效率; 阅读建议:本指南以命令实例为主,建议读者结合实际需求选择相应的命令进行尝试,在实践中理解各个参数的作用并灵活运用。
一、毕业设计的技术背景和设计依据 计算机硬盘驱动器作为一种存储数据信息的设备,在目前的计算机系统中起着不可替代的作用,读写的快慢对整个计算机系统的性能无疑占有重要地位。硬盘式电子计算机磁记录机构中最为精密的一类微机电系统设备,它对振动的敏感性是由其结构决定的,而其控制尚未很好的解决,针对硬盘展开建模及控制有着重要的实际应用价值。 二、毕业设计的任务 现代硬盘一般有 1 - 5 个盘片,以常见的 3.5 英寸硬盘为例,盘片直径约为 95mm,厚度约为 0.635mm。磁头数量通常与盘片数量相关,每面盘片有一个磁头,比如一个 3 盘片的硬盘就有 6 个磁头。缓存大小可能为 32MB、64MB 或 128MB 等,如某普通硬盘缓存为 64MB,电机功率一般在 2 - 5W,像转速为 7200RPM(转每分钟)的电机功率约 3W。数据写入时,磁头在电机带动下,以平均寻道速度约 10 - 15m/s 定位磁道。以一块顺序写入数据的硬盘为例,当写入连续大文件,如 1GB 的视频文件时,在 SATA3.0 接口下,理论传输带宽为 6Gb/s(约 750MB/s),实际可达 500 - 600MB/s。读取时,磁头感应盘片磁场变化,如读取小文件(1MB 大小),若磁头寻道距离平均为 1000 个磁道,每次寻道时间约 8 - 12ms,读取速度会因寻道而下降到 10 - 20MB/s。 本设计选取硬盘驱动器作为研究对象,假设电机扭矩为 0.3N・m,盘片转动惯量为 0.003kg・m²,轴承摩擦力矩为 0.03N・m。在稳定旋转阶段,转速波动范围设为额定转速的 ±0.5%,即 ±36RPM。首先通过对该系统分析建立了其物理模型,然后针对以音圈电机作为伺服机构的硬盘磁头定位伺服系统的性能要求,对音圈电机的控制设计采用三种控制器方案,即极点配置法、PID控制方法、线性二次型调节器方法,分别比较分
scratch少儿编程逻辑思维游戏源码-猫猫冲刺.zip
大数据处理_全文搜索引擎_ElasticSearch中文发行版_基于最新稳定版ElasticSearch预集成中文分词插件及常用配置的开箱即用解决方案_专为中文开发者优化的免配置高
离散数学_逻辑演算系统_真值表生成器_主范式计算器_最简范式求解器_王浩算法实现_等值演算工具_命题逻辑分析平台_基于Web的交互式逻辑运算系统_用于离散数学教学与研究的综合性逻辑
商业智能_SpringBoot_MQ_AIGC_React_智能数据分析_可视化图表_自动分析_数据集处理_用户诉求解析_数据降本增效_基于人工智能的商业智能分析平台_通过自然语言
少儿编程scratch项目源代码文件案例素材-爪猫小车.zip
少儿编程scratch项目源代码文件案例素材-拽猫挥剑.zip
kuishou68_neteaseMusic_32152_1745866029873
少儿编程scratch项目源代码文件案例素材-种族叛徒.zip