2013年1月22日 星期二

Integrate CAS with Spring Security

Spring Security和CAS的整合關係有outgoing和incoming兩個方向:
  1. Outgoing:是指將未經過身分驗證的使用者導到CAS的登入畫面進行身分驗證。
  2. Incoming:是指在CAS已經完成身分認證,然後透過CAS的token機制進行SSO,讓使用者不需要再輸入一次帳號密碼即可進入系統。
一般的系統若是沒有自己的登入入口,就兩者都需要設定,但如果系統本身已經有登入入口,那麼只需要設定第二個SSO的設定就可以了。

以下是在Spring Security設定可以從CAS進行SSO到我們的系統的設定方式,也就是incoming的方向,其中一些handler暫時不多做說明。
Spring XML
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:security="http://www.springframework.org/schema/security"
    xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <security:http pattern="/login.action" security="none" />
    <security:http pattern="/struts/**" security="none" />
    <security:http pattern="/css/**" security="none" />
    <security:http pattern="/images/**" security="none" />
    <security:http pattern="/js/**" security="none" />
    <security:http pattern="/styles/**" security="none" />
    <security:http pattern="/template/**" security="none" />
    <security:http auto-config="true" authentication-manager-ref="authenticationManager" use-expressions="true">
        <security:form-login authentication-success-handler-ref="cookieTokenAwareAuthenticationSuccessHandler"
            login-page="/login.action" authentication-failure-url="/login.action?login_error=1" />
        <security:logout invalidate-session="true" success-handler-ref="cookieTokenClearingLogoutSuccessHandler"
            logout-url="/spring_security_logout" />
        <security:custom-filter position="PRE_AUTH_FILTER" ref="cookieTokenAuthenticationFilter" />
        <security:custom-filter position="CAS_FILTER" ref="casFilter" />
        <security:intercept-url pattern="/**" access="authenticated" />
    </security:http>

    <security:authentication-manager alias="authenticationManager">
        <security:authentication-provider ref="preauthAuthProvider" />
        <security:authentication-provider ref="casAuthenticationProvider" />
        <security:authentication-provider ref="ldapAuthenticationProvider" />
        <security:authentication-provider user-service-ref="userDetailService">
            <!-- <security:password-encoder ref="passwordEncoder"/> -->
        </security:authentication-provider>
    </security:authentication-manager>

    <!-- <bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.ShaPasswordEncoder"/> -->

    <bean id="cookieTokenAwareAuthenticationSuccessHandler" class="com.gss.gmo.cao.spring.security.web.authentication.CookieTokenAwareAuthenticationSuccessHandler">
        <property name="defaultTargetUrl" value="/init/menu.jsp" />
        <property name="alwaysUseDefaultTargetUrl" value="true" />
    </bean>

    <bean id="cookieTokenClearingLogoutSuccessHandler" class="com.gss.gmo.cao.spring.security.web.authentication.logout.CookieTokenClearingLogoutSuccessHandler">
        <property name="defaultTargetUrl" value="/login.action" />
        <property name="alwaysUseDefaultTargetUrl" value="true" />
    </bean>

    <bean id="cookieTokenAuthenticationFilter" class="com.gss.gmo.cao.spring.security.web.authentication.preauth.CookieTokenAuthenticationFilter">
        <property name="authenticationManager" ref="authenticationManager" />
        <property name="continueFilterChainOnUnsuccessfulAuthentication" value="true" />
        <property name="checkForPrincipalChanges" value="true" />
    </bean>

    <bean id="preauthAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
        <property name="preAuthenticatedUserDetailsService" ref="authenticationUserDetailsService" />
    </bean>

    <bean id="authenticationUserDetailsService" class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
        <property name="userDetailsService" ref="userDetailService" />
    </bean>

    <bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties">
        <property name="service" value="http://localhost:8080/portal-web/gss/j_spring_cas_security_check" />
    </bean>

    <bean id="casFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter">
        <property name="filterProcessesUrl" value="/gss/j_spring_cas_security_check" />
        <property name="authenticationManager" ref="authenticationManager" />
        <property name="authenticationSuccessHandler" ref="cookieTokenAwareAuthenticationSuccessHandler" />
        <property name="authenticationFailureHandler">
            <bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
                <property name="defaultFailureUrl" value="/login.action?login_error=1" />
            </bean>
        </property>
    </bean>

    <bean id="casAuthenticationProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
        <property name="serviceProperties" ref="serviceProperties" />
        <property name="ticketValidator">
            <bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
                <constructor-arg value="http://teamkube.gss.com.tw/cas" />
            </bean>
        </property>
        <property name="key" value="teamkube-cas" />
        <property name="authenticationUserDetailsService" ref="authenticationUserDetailsService" />
    </bean>

</beans>
依照我們的設定,用URL:
http://teamkube.gss.com.tw/cas/login?service=http%3A%2F%2Flocalhost%3A8080%2Fportal-web%2Fgss%2Fj_spring_cas_security_check
就可以用CAS的畫面登入到我們的系統。