Build Your Own Cas Service - Pro

weixin_34321977發表於2016-02-02

示例程式碼: https://github.com/superalsrk/modify-jasig-cas ,以下所有描述都基於版本 3.5.2.1

Generally Design

我們可以把一個war專案作為dependency,然後建立一個web專案webapp,然後只要將建立專案的 web.xml 和 index.jsp 去掉, 整個專案就能跑了。

更重要的是,如果要對war進行擴充套件, 只要講war對應的檔案拷貝一份到webapp,打包的時候便能自動到替換。下面講的 修改XXX檔案, 都是對其拷貝進行修改,特此宣告:

webapp module的pom為pom.xml

Auth Module

自定義Credentials

Credentials是一個使用者憑證, 可以理解為一個簡易的pojo, 只要實現Credentials介面即可,我們的自定義憑證中除了使用者名稱密碼,還加了一個欄位 product : String, 表明要登入的產品型別

在Web Module中,需要進行如下修改

1 . 在登入表單增加product欄位,具體操作詳見下個Section
2 . 在 /WEB-INF/login-webflow.xml 中,修改credentials型別為自定義的Credentials

<var name="credentials" class="com.nbrc.sso.cas.principal.NbrcCredentials"/>

3 . 然後繼續在 login-webflow.xml裡找到 viewLoginForm ,進行資料繫結

<view-state id="viewLoginForm" view="casLoginView" model="credentials">  
       <binder>  
           <binding property="username" />  
           <binding property="password" />  
           <binding property="product"/> <!--增加這一行 -->  
       </binder>  
       ...  
</view-state>  

自定義Handler

自定義Handler只要實現介面 AuthenticationHandler 即可

1 . 如果要在前臺顯示一個 許可權不足 的資訊, 只需在Handler裡throw一個自定義的 AuthenticationException 即可
2 . support 介面用來宣告handler是否支援某種型別的憑證
3 . 修改 /WEB-INF/deployConfigContext.xml ,進行handler的配置

<property name="authenticationHandlers">
            <list>
                <bean
                    class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler"
                    p:httpClient-ref="httpClient" p:requireSecure="false" />

                <bean
                    class="com.miaozhen.dashboard.darkportal.mechanism.DarkportalAuthenticationHandler" />
            </list>
</property>

自定義Resolver

Resolver是一個Credentials 到 Principal的轉換器, 其中Principal其實是javaEE中就已經定義好的

1 . 修改 /WEB-INF/deployConfigContext.xml ,進行Resolver的配置

<property name="credentialsToPrincipalResolvers">
            <list>
                <bean
                    class="com.miaozhen.dashboard.darkportal.mechanism.DarkportalCredentialsToPrincipalResolver">

                </bean>

                <bean
                    class="org.jasig.cas.authentication.principal.HttpBasedServiceCredentialsToPrincipalResolver" />
            </list>
</property>

2 . resolver可以返回一個Principal, 個人覺得比較好用的方式是返回一個 #SimplePrincipal# ,除了使用者的user資訊外,還可以返回一個 AttrMap,不過需要參考下章進行Resolver檢視的修改

Map<String, Object> map = new HashMap<String, Object>();
map.put(ATTR_USERNAME, mzCredentials.getUsername());
map.put(ATTR_PASSWORD, mzCredentials.getPassword());

SimplePrincipal simple = new SimplePrincipal(mzCredentials.getUsername(), map);

Web Module

自定義登陸頁面

正常的做法應該是copy一份defaults資料夾,然後在resources裡copy對應的主題配置檔案,最後在cas.properties裡配置一下主題,不過為了省事直接改defaults裡的檔案就可以了

default/ui/casLoginView.jsp 就是預設的登入介面,可以給form表單增加多餘的欄位。需要注意的是:form表單裡還有一堆cas自帶的input,這個在改頁面的時候不能刪掉。

自定義返回使用者資訊

1 . 在resolver中雖然返回了更多Attr,不過預設的Resolver檢視不支援返回更多屬性,需要對 protocol/2.0/casServiceValidationSuccess.jsp 頁面進行擴充套件.

<%@ page session="false"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>
<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
    <cas:authenticationSuccess>
        <cas:user>${fn:escapeXml(assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.id)}</cas:user>
        <c:if
            test="${fn:length(assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes) > 0}">
            <cas:attributes>
                  <c:forEach var="attr" items="${assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes}">
                    <cas:${fn:escapeXml(attr.key)}>${fn:escapeXml(attr.value)}</cas:${fn:escapeXml(attr.key)}>
                </c:forEach>
            </cas:attributes>
        </c:if>
        <c:if test="${not empty pgtIou}">
            <cas:proxyGrantingTicket>${pgtIou}</cas:proxyGrantingTicket>
        </c:if>
        <c:if test="${fn:length(assertion.chainedAuthentications) > 1}">
            <cas:proxies>
                <c:forEach var="proxy" items="${assertion.chainedAuthentications}"
                    varStatus="loopStatus" begin="0"
                    end="${fn:length(assertion.chainedAuthentications)-2}" step="1">
                    <cas:proxy>${fn:escapeXml(proxy.principal.id)}</cas:proxy>
                </c:forEach>
            </cas:proxies>
        </c:if>
    </cas:authenticationSuccess>
</cas:serviceResponse>

2 . 在client端,使用如下程式碼就可以獲取多餘屬性

AttributePrincipal attribute = (AttributePrincipal) request.getUserPrincipal();
AttributePrincipal.getName()  就是 Resolver中返回的SimplePrincipal名字
AttributePrincipal.getAttributes() 就是Resolver中返回的SinmplePrincipal的attributes

3 . 注意把deployerConfigContext.xml中 serviceRegistryDao全部刪掉(cas),參考資料


CAS退出功能

預設的JASIG退出成功後會跳到一個 推出成功頁面, 但我們想要的效果是退出CAS,並且退出已經登入的應用, 那麼可以進行如下的配置:

  1. 如果只是退出應用,那麼在此訪問頁面的時候,cas-client又會向cas-server端進行請求驗證,然後自動登入,所以同時退出cas和應用即可
  2. 修改 cas-servlet.xml , 在 logoutController 的bean中增加屬性 p:followServiceRedirects="true"
  3. 假如應用已經有一個退出controller,此contoller用來清空session,那麼連結 http://cas.example.org/logout?service=http://localhost:8080/logout 便可以正常退出

THE END

相關文章