SSO(单点登录)要求同一个用户在使用不同的应用时,只需要登录一次即可。关于SSO系统的详细介绍此处不涉及。Spring Security提供了良好的接口和CAS进行整合。CAS是一个SSO系统,分为Client和Server,Server来完成用户的认证,Client放在应用端来和Server进行交互获取用户的认证信息。
本文主要用来指导在Spring Security框架下配置CAS完成SSO功能。达到如下目的:
- 对未授权的WEB请求的访问都会重定向到CAS Server上进行验证
- 验证通过的用户可以访问WEB资源和提交WEB请求
- A应用登录后,相同的用户用相同的浏览器登录B应用(A和B都是通过CAS Server进行授权),不需要再进行验证。
- 可以对单应用进行登出,也可以登出用户
- 提供CSRF保护
关于CAS Server的安装和配置,本文档不做说明,可以参考《CAS Server部署指南》文档。
配置
为了使用CAS的功能,需要安装security支持CAS的jar包,修改pom.xml如下:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-cas</artifactId>
<version>4.0.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.jasig.cas.client</groupId>
<artifactId>cas-client-core</artifactId>
<version>3.3.3</version>
<scope>compile</scope>
</dependency>
Spring-security.xml的配置如下:
<!-- CAS Configuration-->
<bean id="serviceProperties"
class="org.springframework.security.cas.ServiceProperties">
<property name="service"
value="http://localhost:8080/login/cas"/>
<property name="sendRenew" value="false"/>
</bean>
<!-- CAS Filter-->
<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://ssomanage.test.com:8443/cas" />
</bean>
</property>
<property name="key" value="casAuthProviderKey"/>
</bean>
<security:user-service id="userService">
<security:user name="xiaof" password="post-it" authorities="ROLE_ADMIN,ROLE_USER" />
<security:user name="test1" password="" authorities="ROLE_USER" />
</security:user-service>
<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://ssomanage.test.com:8443/cas/login"/>
<property name="serviceProperties" ref="serviceProperties"/>
</bean>
<!-- CSRF Configuration-->
<bean id="csrfTokenFilter" class="com.test.cloud.security.CsrfTokenFilter"/>
<!-- CAS Logout Configuration-->
<!-- This filter handles a Single Logout Request from the CAS Server -->
<bean id="singleLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter"/>
<!-- This filter redirects to the CAS Server to signal Single Logout should be performed -->
<bean id="requestSingleLogoutFilter"
class="org.springframework.security.web.authentication.logout.LogoutFilter">
<constructor-arg value="https://ssomanage.test.com:8443/cas/logout"/>
<constructor-arg>
<bean class= "org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>
</constructor-arg>
<property name="filterProcessesUrl" value="/logout/cas"/>
</bean>
<security:debug/>
<!-- General Configuration-->
<security:http
auto-config="false"
entry-point-ref="casEntryPoint">
<security:intercept-url pattern="/" access="permitAll" />
<security:intercept-url pattern="/login/cas" access="permitAll" />
<!--<security:intercept-url pattern="/index.jsp" access="permitAll" />-->
<security:intercept-url pattern="/view/app/pages/**" access= "hasRole('ROLE_ADMIN')" />
<security:intercept-url pattern="/**" access= "hasRole('ROLE_USER')" />
<security:custom-filter ref="requestSingleLogoutFilter" before="LOGOUT_FILTER"/>
<security:custom-filter ref="singleLogoutFilter" before="CAS_FILTER"/>
<security:custom-filter ref="casFilter" position="CAS_FILTER" />
<!--<security:csrf disabled="true"/>-->
<security:custom-filter ref="csrfTokenFilter" after="CSRF_FILTER"/>
</security:http>
这里不再对配置进行逐条说明,CAS对应的配置不需要修改前后台代码,CSRF配置修改前后台代码参考《Spring Security实践(二):简单认证的实现》。在配置过程中需要注意以下几点:
- serviceProperties中value的配置是web服务器的主机+/login/cas,该URL是service的值,会依据该值在CAS Server上生成ticket, 然后用来到WEB服务器上验证ticket。
- casEntryPoint中的https://ssomanage.test.com:8443/cas/login是CAS Server用来输入用户凭证的入口,其中主机和端口配置成提供CAS Server服务的主机和端口即可。
- 注意,因为WEB Server要和CAS Server建立HTTPS连接,所以必须将CAS Server的自签名证书或者一个可以用来验证CAS Server证书的CA证书导入WEB Server中,实验环境用的是Tomcat作为WEB Server,具体导入证书的步骤,参考《CAS单点登录数据库认证V1.0》文档。
- requestSingleLogoutFilter用来指定登出操作的过滤器,其中https://ssomanage.test.com:8443/cas/logout是用来登出用户的,执行该操作之后,用户从CAS Server上登出,/logout/cas是用来登出应用的,在WEB Server上执行该操作之后,只会登出当前的WEB Server。
- userService中配置的用户信息,主要用来做授权,绑定用户的角色。其中的用户密码不会用来验证用户。
演示
这里通过一个实际的例子来演示SSO登录的过程,并对其中的通信细节做简要说明。
操作示例
- 登录WEB Server
- 跳转到CAS Server进行认证
- 认证成功后跳转到WEB Server
通信细节
- 从WEB Server跳转到CAS Server
- 携带用户名和密码到CAS上进行认证:
- 认证通过之后跳转到WEB Server上获取认证信息:
- 在WEB Server上认证通过之后,跳转到WEB资源页面:
- 多应用登录的场景,此时A应用已经登录,再登录B:
- 登出应用:
- 登出CAS Server:
小结
本文说明了在Spring Security中应用CAS如何进行配置,并对操作和通信细节进行了演示。多个应用进行单点登录在实际环境中也经过测试,和单个应用登录并没有不同,在认证的时候携带了一个指定的ticket.