天天看點

自定義cas用戶端核心過濾器AuthenticationFilter

自定義cas用戶端核心過濾器AuthenticationFilter

         關于cas用戶端的基本配置這裡就不多說了,不清楚的可以參考上一篇博文:配置簡單cas用戶端。這裡是關于cas用戶端實作動态配置認證需要開發說明。

         往往業務系統中有些子產品或功能是可以不需要登入就可以通路的,但是添加了cas用戶端之後,通過cas用戶端filter中的url-pattern來設定需要過濾的url,有時根本無法滿足實際業務的需求,這裡筆者就通過對cas用戶端中源碼的閱讀,和對認證流程的了解,對cas用戶端做了些改動,來實作動态配置cas用戶端認證範圍。

         下面是cas認證的核心配置,其中AuthenticationFilter過濾器為cas用戶端核心過濾,下面的url-pattern是配置需要過濾的url,如果我們能編寫該過濾器,我們就可以實作動态配置cas用戶端的過濾url了。

<!-- cas統一認證  -->
 <filter>
  	<filter-name>CASFilter</filter-name>
  	<filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
  	<init-param>
  		<param-name>casServerLoginUrl</param-name>
  		<param-value>http://localhost:8080/casServer3/login</param-value>
  	</init-param>
  	<init-param>
  		<param-name>serverName</param-name>
  		<param-value>http://localhost:8080</param-value>
  	</init-param>
  </filter>
  <filter-mapping>
  	<filter-name>CASFilter</filter-name>
  	<url-pattern>/*</url-pattern>
  </filter-mapping>  
  <filter>
  	<filter-name>CAS Validation Filter</filter-name>
	<filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class>
  	<init-param>
  		<param-name>casServerUrlPrefix</param-name>
  		<param-value>http://localhost:8080/casServer3</param-value>
  	</init-param>
  	<init-param>
  		<param-name>serverName</param-name>
  		<param-value>http://localhost:8080</param-value>
  	</init-param>
  </filter>
  <filter-mapping>
  	<filter-name>CAS Validation Filter</filter-name>
  	<url-pattern>/*</url-pattern>
  </filter-mapping>
           

思路:将配置中指向的核心過濾器,指向自己定義的過濾器,将源碼中核心過濾器AuthenticationFilter的代碼複制拷貝到該自定義過濾器中,然後在該過濾器中添加自己的過濾規則。

步驟:

1.配置并啟動cas服務端,具體配置可以參考博文:搭建簡單的cas認證服務

2.建立一個web項目,然後添加cas用戶端配置,具體配置可以參考博文:配置簡單cas用戶端

3.導入cas用戶端核心jar的源碼到該web項目中,源碼在cas用戶端下載下傳zip包中就有,一般為cas-client-core檔案夾

4.在項目的src中建立類AuthenticationFilter,繼承org.jasig.cas.client.util.AbstractCasFilter,打開web.xml檔案,找到找到cas核心過濾器的配置項CASFilter,Ctrl+左鍵,點選進入org.jasig.cas.client.authentication.AuthenticationFilter類中,複制類裡面的全部代碼到自定義的AuthenticationFilter類中。修改web.xml中cas核心過濾器配置項CASFilter中的配置,将filter-class指向剛才自定義的AuthenticationFilter類,同時在該過濾器中添加<init-param>配置。如下

<filter>
  	<filter-name>CASFilter</filter-name>
  	<filter-class>com.supre.filter.AuthenticationFilter</filter-class>
  	<init-param>
  		<param-name>casServerLoginUrl</param-name>
  		<param-value>http://localhost:8080/casServer3/login</param-value>
  	</init-param>
  	<init-param>
  		<param-name>serverName</param-name>
  		<param-value>http://localhost:8080</param-value>
  	</init-param>
    <init-param>
  		<param-name>excludePaths</param-name>
  		<param-value>.*[/,\\]rest[/,\\].*</param-value>
  	</init-param>
  </filter>
  <filter-mapping>
  	<filter-name>CASFilter</filter-name>
  	<url-pattern>/*</url-pattern>
  </filter-mapping> 
           

說明:

1其中param-name為參數名,這個在過濾器初始化中需要根據該名字來取param-value中的值

2其中param-value的值可以根據需要在filter中制定自己的規則,筆者這裡是正規表達式

5.在自定義的AuthenticationFilter中添加自己的代碼,來實作認證範圍的控制,代碼如下:

package com.supre.filter;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.jasig.cas.client.authentication.DefaultGatewayResolverImpl;
import org.jasig.cas.client.authentication.GatewayResolver;
import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.validation.Assertion;
/**
 * 為了友善控制filter,自定義了統一認證過濾器AuthenticationFilter
 * @author Administrator
 *
 */
public class AuthenticationFilter extends AbstractCasFilter{
	 /**
     * The URL to the CAS Server login.
     */
    private String casServerLoginUrl;

    /**
     * Whether to send the renew request or not.
     */
    private boolean renew = false;

    /**
     * Whether to send the gateway request or not.
     */
    private boolean gateway = false;
    /**
     * 添加屬性,這裡用來存放不過濾位址正規表達式,可以根據自己需求定制---1
     */
    private String excludePaths;
    
    private GatewayResolver gatewayStorage = new DefaultGatewayResolverImpl();

    protected void initInternal(final FilterConfig filterConfig) throws ServletException {
        if (!isIgnoreInitConfiguration()) {
            super.initInternal(filterConfig);
            setCasServerLoginUrl(getPropertyFromInitParams(filterConfig, "casServerLoginUrl", null));
            log.trace("Loaded CasServerLoginUrl parameter: " + this.casServerLoginUrl);
            setRenew(parseBoolean(getPropertyFromInitParams(filterConfig, "renew", "false")));
            log.trace("Loaded renew parameter: " + this.renew);
            setGateway(parseBoolean(getPropertyFromInitParams(filterConfig, "gateway", "false")));
            log.trace("Loaded gateway parameter: " + this.gateway);

            final String gatewayStorageClass = getPropertyFromInitParams(filterConfig, "gatewayStorageClass", null);

            if (gatewayStorageClass != null) {
                try {
                    this.gatewayStorage = (GatewayResolver) Class.forName(gatewayStorageClass).newInstance();
                } catch (final Exception e) {
                    log.error(e,e);
                    throw new ServletException(e);
                }
            }
            //自定義添加代碼,用來讀取web配置檔案中excludes屬性值 ---2
            excludePaths = getPropertyFromInitParams(filterConfig, "excludePaths", null);//filterConfig.getInitParameter("excludePaths");
            excludePaths = excludePaths.trim();
        }
    }

    public void init() {
        super.init();
        CommonUtils.assertNotNull(this.casServerLoginUrl, "casServerLoginUrl cannot be null.");
    }
// url判斷邏輯,這裡大家可以根據自己需要來制訂規則
    private boolean isExclude(String uri){
    	boolean isInWhiteList = false;
    	if(excludePaths!=null&& uri!=null){
    		isInWhiteList = uri.matches(excludePaths);
    	}
        return isInWhiteList;
    }
   
    
    public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
        final HttpServletRequest request = (HttpServletRequest) servletRequest;
        final HttpServletResponse response = (HttpServletResponse) servletResponse;
        final HttpSession session = request.getSession(false);
        final Assertion assertion = session != null ? (Assertion) session.getAttribute(CONST_CAS_ASSERTION) : null;
       // 該判斷是自定義的對符合條件的url進行通過處理 ---3
        if(isExclude(request.getRequestURI())){
        	filterChain.doFilter(request, response);
            return;
        }
        
        if (assertion != null) {
            filterChain.doFilter(request, response);
            return;
        }

        final String serviceUrl = constructServiceUrl(request, response);
        final String ticket = CommonUtils.safeGetParameter(request,getArtifactParameterName());
        final boolean wasGatewayed = this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);

        if (CommonUtils.isNotBlank(ticket) || wasGatewayed) {
            filterChain.doFilter(request, response);
            return;
        }

        final String modifiedServiceUrl;

        log.debug("no ticket and no assertion found");
        if (this.gateway) {
            log.debug("setting gateway attribute in session");
            modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl);
        } else {
            modifiedServiceUrl = serviceUrl;
        }

        if (log.isDebugEnabled()) {
            log.debug("Constructed service url: " + modifiedServiceUrl);
        }

        final String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway);

        if (log.isDebugEnabled()) {
            log.debug("redirecting to \"" + urlToRedirectTo + "\"");
        }

        response.sendRedirect(urlToRedirectTo);
    }

    public final void setRenew(final boolean renew) {
        this.renew = renew;
    }

    public final void setGateway(final boolean gateway) {
        this.gateway = gateway;
    }

    public final void setCasServerLoginUrl(final String casServerLoginUrl) {
        this.casServerLoginUrl = casServerLoginUrl;
    }
    
    public final void setGatewayStorage(final GatewayResolver gatewayStorage) {
    	this.gatewayStorage = gatewayStorage;
    }
    
}
           

說明:上面的例子筆者是想在web中配置不需要認證的url,通過正規表達式來判斷,這裡相關的規則可以根據自己需要來編寫。

6.到這裡就基本完成了,根據自己定義的規則來做測試,大家可以在項目中建立多個jsp或html檔案,放在不同目錄下(部分設計為通過,部分設計為不通過),然後在浏覽器中直接通路這些檔案,看是否被攔截而跳到認證見面,通過根據自己定義的規則判斷修改是否成功。

繼續閱讀