cas4.2.7實現單點登陸

準備前參考: html

 cas server下載地址
java

 cas client 下載地址mysql

   安全cookie setSecure詳解git

   Spring經過構造方法注入的四種方式github

 cas 學習博文web

自定義登陸頁和登陸認證

  cas server端的login-webflow詳細流程redis

  CAS服務端自定義數據庫認證用戶算法

 

準備工做

  1. cas server下載以後解壓,在項目根目錄下執行 mvn clean 和 mvn install命令(若是使用給定的命令報錯)。spring

  2. 根目錄下會生成target目錄,target中會有一個cas.war的文件,將這個文件經過 mvn install 發佈到maven的本地倉庫中。sql

  

 

mvn install:install-file -DgroupId=com.hjz -DartifactId=cas -Dversion=1.0.0 -Dfile=cas.war -Dpackaging=war -DgeneratePom=true

 

  3.項目中的依賴

<dependency>
  <groupId>com.hjz</groupId>
  <artifactId>cas</artifactId>
  <version>1.0.0</version>
  <type>war</type>
</dependency>

客戶端cas ticket驗證走查

  1.若是請求中攜帶了參數ticket則將會由TicketValidationFilter來對攜帶的ticket進行校驗。TicketValidationFilter只是對驗證ticket的這一類Filter的統稱,其並不對應Cas Client中的一個具體類型。Cas Client中有多種驗證ticket的Filter,都繼承自AbstractTicketValidationFilter,它們的驗證邏輯都是一致的,都有AbstractTicketValidationFilter實現,所不一樣的是使用的TicketValidator不同。筆者這裏將以Cas20TicketValidationFilter爲例。

<context-param>
      <param-name>serverName</param-name>
      <param-value>http://127.0.0.1:8089</param-value>
</context-param>

<filter>
   <filter-name>casTicketValidationFilter</filter-name>
<filter-class>org.jasig.cas.client.validation.Cas10TicketValidationFilter</filter-class>
   <init-param>
      <param-name>casServerUrlPrefix</param-name>
      <param-value>http://127.0.0.1:8080/cas</param-value>
   </init-param>
</filter>
<filter-mapping>
   <filter-name>casTicketValidationFilter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

  一個是用來指定Cas Server登陸地址的casServerLoginUrl,另外一個是用來指定認證成功後須要跳轉地址的serverNameservice。service和serverName只須要指定一個就能夠了。當二者都指定了,參數service將具備更高的優先級,即將以service指定的參數值爲準。service和serverName的區別在於service指定的是一個肯定的URL,認證成功後就會確切的跳轉到service指定的URL;而serverName則是用來指定主機名,其格式爲{protocol}:{hostName}:{port},如:https://localhost:8443,當指定的是serverName時,AuthenticationFilter將會把它附加上當前請求的URI,以及對應的查詢參數來構造一個肯定的URL,如指定serverName爲「http://localhost」,而當前請求的URI爲「/app」,查詢參數爲「a=b&b=c」,則對應認證成功後的跳轉地址將爲「http://localhost/app?a=b&b=c」。

       必須指定的參數:

   casServerUrlPrefix:用來指定Cas Server對應URL地址的前綴,如上面示例的「https://elim:8443/cas」。

   serverName或service:語義跟前面介紹的一致。

 

       可選參數:

   redirectAfterValidation :表示是否驗證經過後從新跳轉到該URL,可是不帶參數ticket,默認爲true。

   useSession :在驗證ticket成功後會生成一個Assertion對象,若是useSession爲true,則會將該對象存放到Session中。若是爲false,則要求每次請求都須要攜帶ticket進行驗證,顯然useSession爲false跟redirectAfterValidation爲true是衝突的。默認爲true。

  exceptionOnValidationFailure :表示ticket驗證失敗後是否須要拋出異常,默認爲true。

   renew:當值爲true時將發送「renew=true」到Cas Server,默認爲false。

  

  2.經過模仿 Cas10TicketValidationFilter 的邏輯,自定義 ticket validation filter。

package com.xxx.web.cas;

import com.xxx.sdk.RedisUtils;
import com.xxx.web.cas.logout.entity.TenantUser;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import javax.net.ssl.HostnameVerifier;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.lang.StringUtils;
import org.jasig.cas.client.authentication.AttributePrincipal;
import org.jasig.cas.client.proxy.AbstractEncryptedProxyGrantingTicketStorageImpl;
import org.jasig.cas.client.proxy.Cas20ProxyRetriever;
import org.jasig.cas.client.proxy.CleanUpTimerTask;
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl;
import org.jasig.cas.client.ssl.HttpURLConnectionFactory;
import org.jasig.cas.client.ssl.HttpsURLConnectionFactory;
import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.util.ReflectUtils;
import org.jasig.cas.client.validation.Assertion;
import org.jasig.cas.client.validation.Cas20ProxyTicketValidator;
import org.jasig.cas.client.validation.Cas20ServiceTicketValidator;
import org.jasig.cas.client.validation.TicketValidationException;
import org.jasig.cas.client.validation.TicketValidator;
import org.slf4j.Logger;
import uap.web.esapi.EncryptException;
import uap.web.utils.PropertyUtil;
import uap.web.utils.TokenGenerator;

public class TenantProxyReceivingTicketValidationFilter extends AbstractCasFilter {
   private static final String[] RESERVED_INIT_PARAMS = { "proxyGrantingTicketStorageClass", "proxyReceptorUrl",
         "acceptAnyProxy", "allowedProxyChains", "casServerUrlPrefix", "proxyCallbackUrl", "renew",
         "exceptionOnValidationFailure", "redirectAfterValidation", "useSession", "serverName", "service",
         "artifactParameterName", "serviceParameterName", "encodeServiceUrl", "millisBetweenCleanUps",
         "hostnameVerifier", "encoding", "config", "ticketValidatorClass" };
   private static final int DEFAULT_MILLIS_BETWEEN_CLEANUPS = 60000;
   private String proxyReceptorUrl;
   private Timer timer;
   private TimerTask timerTask;
   private int millisBetweenCleanUps;
   private static final String TENANT_ASSERTION = "tenant_assertion";
   private TicketValidator ticketValidator;
   private boolean redirectAfterValidation;
   private boolean exceptionOnValidationFailure;
   private boolean useSession;
   private ProxyGrantingTicketStorage proxyGrantingTicketStorage;

   public TenantProxyReceivingTicketValidationFilter() {
      this.redirectAfterValidation = true;

      this.exceptionOnValidationFailure = false;

      this.useSession = true;

      this.proxyGrantingTicketStorage = new ProxyGrantingTicketStorageImpl();
   }

   protected void initInternal(FilterConfig filterConfig) throws ServletException {
      setProxyReceptorUrl(getPropertyFromInitParams(filterConfig, "proxyReceptorUrl", null));

      String proxyGrantingTicketStorageClass = getPropertyFromInitParams(filterConfig,
            "proxyGrantingTicketStorageClass", null);

      if (proxyGrantingTicketStorageClass != null) {
         this.proxyGrantingTicketStorage = ((ProxyGrantingTicketStorage) ReflectUtils
               .newInstance(proxyGrantingTicketStorageClass, new Object[0]));

         if (this.proxyGrantingTicketStorage instanceof AbstractEncryptedProxyGrantingTicketStorageImpl) {
            AbstractEncryptedProxyGrantingTicketStorageImpl p = (AbstractEncryptedProxyGrantingTicketStorageImpl) this.proxyGrantingTicketStorage;
            String cipherAlgorithm = getPropertyFromInitParams(filterConfig, "cipherAlgorithm", "DESede");

            String secretKey = getPropertyFromInitParams(filterConfig, "secretKey", null);

            p.setCipherAlgorithm(cipherAlgorithm);
            try {
               if (secretKey != null)
                  p.setSecretKey(secretKey);
            } catch (Exception e) {
               throw new RuntimeException(e);
            }
         }
      }

      this.logger.trace("Setting proxyReceptorUrl parameter: {}", this.proxyReceptorUrl);
      this.millisBetweenCleanUps = Integer
            .parseInt(getPropertyFromInitParams(filterConfig, "millisBetweenCleanUps", Integer.toString(60000)));

      setExceptionOnValidationFailure(
            parseBoolean(getPropertyFromInitParams(filterConfig, "exceptionOnValidationFailure", "false")));

      this.logger.trace("Setting exceptionOnValidationFailure parameter: {}",
            Boolean.valueOf(this.exceptionOnValidationFailure));
      setRedirectAfterValidation(
            parseBoolean(getPropertyFromInitParams(filterConfig, "redirectAfterValidation", "true")));

      this.logger.trace("Setting redirectAfterValidation parameter: {}",
            Boolean.valueOf(this.redirectAfterValidation));
      setUseSession(parseBoolean(getPropertyFromInitParams(filterConfig, "useSession", "true")));
      this.logger.trace("Setting useSession parameter: {}", Boolean.valueOf(this.useSession));

      if ((!(this.useSession)) && (this.redirectAfterValidation)) {
         this.logger.warn(
               "redirectAfterValidation parameter may not be true when useSession parameter is false. Resetting it to false in order to prevent infinite redirects.");

         setRedirectAfterValidation(false);
      }

      setTicketValidator(getTicketValidator(filterConfig));

      super.initInternal(filterConfig);

      String servername = PropertyUtil.getPropertyByKey("servername");
      if (StringUtils.isNotBlank(servername))
         setServerName(servername);
      else
         setServerName(getPropertyFromInitParams(filterConfig, "serverName", null));
   }

   public void init() {
      super.init();
      CommonUtils.assertNotNull(this.proxyGrantingTicketStorage, "proxyGrantingTicketStorage cannot be null.");
      CommonUtils.assertNotNull(this.ticketValidator, "ticketValidator cannot be null.");
      if (this.timer == null) {
         this.timer = new Timer(true);
      }

      if (this.timerTask == null) {
         this.timerTask = new CleanUpTimerTask(this.proxyGrantingTicketStorage);
      }
      this.timer.schedule(this.timerTask, this.millisBetweenCleanUps, this.millisBetweenCleanUps);
   }

   private <T> T createNewTicketValidator(String ticketValidatorClass, String casServerUrlPrefix, Class<T> clazz) {
      if (CommonUtils.isBlank(ticketValidatorClass)) {
         return ReflectUtils.newInstance(clazz, new Object[] { casServerUrlPrefix });
      }

      return ReflectUtils.newInstance(ticketValidatorClass, new Object[] { casServerUrlPrefix });
   }

   protected final TicketValidator getTicketValidator(FilterConfig filterConfig) {
      String allowAnyProxy = getPropertyFromInitParams(filterConfig, "acceptAnyProxy", null);
      String allowedProxyChains = getPropertyFromInitParams(filterConfig, "allowedProxyChains", null);

      String casServerUrlPrefix = getCasUrl(filterConfig);

      String ticketValidatorClass = getPropertyFromInitParams(filterConfig, "ticketValidatorClass", null);
      Cas20ServiceTicketValidator validator;
      Cas20ServiceTicketValidator validator;
      if ((CommonUtils.isNotBlank(allowAnyProxy)) || (CommonUtils.isNotBlank(allowedProxyChains))) {
         Cas20ProxyTicketValidator v = (Cas20ProxyTicketValidator) createNewTicketValidator(ticketValidatorClass,
               casServerUrlPrefix, Cas20ProxyTicketValidator.class);

         v.setAcceptAnyProxy(parseBoolean(allowAnyProxy));
         v.setAllowedProxyChains(CommonUtils.createProxyList(allowedProxyChains));
         validator = v;
      } else {
         validator = (Cas20ServiceTicketValidator) createNewTicketValidator(ticketValidatorClass, casServerUrlPrefix,
               Cas20ServiceTicketValidator.class);
      }

      validator.setProxyCallbackUrl(getPropertyFromInitParams(filterConfig, "proxyCallbackUrl", null));
      validator.setProxyGrantingTicketStorage(this.proxyGrantingTicketStorage);

      HttpURLConnectionFactory factory = new HttpsURLConnectionFactory(getHostnameVerifier(filterConfig),
            getSSLConfig(filterConfig));

      validator.setURLConnectionFactory(factory);

      validator.setProxyRetriever(new Cas20ProxyRetriever(casServerUrlPrefix,
            getPropertyFromInitParams(filterConfig, "encoding", null), factory));

      validator.setRenew(parseBoolean(getPropertyFromInitParams(filterConfig, "renew", "false")));
      validator.setEncoding(getPropertyFromInitParams(filterConfig, "encoding", null));

      Map additionalParameters = new HashMap();
      List params = Arrays.asList(RESERVED_INIT_PARAMS);

      for (Enumeration e = filterConfig.getInitParameterNames(); e.hasMoreElements();) {
         String s = (String) e.nextElement();

         if (!(params.contains(s))) {
            additionalParameters.put(s, filterConfig.getInitParameter(s));
         }
      }

      validator.setCustomParameters(additionalParameters);
      return validator;
   }

   public void destroy() {
      super.destroy();
      this.timer.cancel();
   }

   protected final boolean preFilter(ServletRequest servletRequest, ServletResponse servletResponse,
         FilterChain filterChain) throws IOException, ServletException {
      HttpServletRequest request = (HttpServletRequest) servletRequest;
      HttpServletResponse response = (HttpServletResponse) servletResponse;
      String requestUri = request.getRequestURI();

      if ((CommonUtils.isEmpty(this.proxyReceptorUrl)) || (!(requestUri.endsWith(this.proxyReceptorUrl)))) {
         return true;
      }
      try {
         CommonUtils.readAndRespondToProxyReceptorRequest(request, response, this.proxyGrantingTicketStorage);
      } catch (RuntimeException e) {
         this.logger.error(e.getMessage(), e);
         throw e;
      }

      return false;
   }

   public final void setProxyReceptorUrl(String proxyReceptorUrl) {
      this.proxyReceptorUrl = proxyReceptorUrl;
   }

   public void setProxyGrantingTicketStorage(ProxyGrantingTicketStorage storage) {
      this.proxyGrantingTicketStorage = storage;
   }

   public void setTimer(Timer timer) {
      this.timer = timer;
   }

   public void setTimerTask(TimerTask timerTask) {
      this.timerTask = timerTask;
   }

   public void setMillisBetweenCleanUps(int millisBetweenCleanUps) {
      this.millisBetweenCleanUps = millisBetweenCleanUps;
   }

   protected Properties getSSLConfig(FilterConfig filterConfig) {
      Properties properties = new Properties();
      String fileName = getPropertyFromInitParams(filterConfig, "sslConfigFile", null);

      if (fileName != null) {
         FileInputStream fis = null;
         try {
            fis = new FileInputStream(fileName);
            properties.load(fis);
            this.logger.trace("Loaded {} entries from {}", Integer.valueOf(properties.size()), fileName);
         } catch (IOException ioe) {
            this.logger.error(ioe.getMessage(), ioe);
         } finally {
            CommonUtils.closeQuietly(fis);
         }
      }
      return properties;
   }

   protected HostnameVerifier getHostnameVerifier(FilterConfig filterConfig) {
      String className = getPropertyFromInitParams(filterConfig, "hostnameVerifier", null);
      this.logger.trace("Using hostnameVerifier parameter: {}", className);
      String config = getPropertyFromInitParams(filterConfig, "hostnameVerifierConfig", null);
      this.logger.trace("Using hostnameVerifierConfig parameter: {}", config);
      if (className != null) {
         if (config != null) {
            return ((HostnameVerifier) ReflectUtils.newInstance(className, new Object[] { config }));
         }
         return ((HostnameVerifier) ReflectUtils.newInstance(className, new Object[0]));
      }

      return null;
   }

   protected void onSuccessfulValidation(HttpServletRequest request, HttpServletResponse response,
         Assertion assertion) {
   }

   protected void onFailedValidation(HttpServletRequest request, HttpServletResponse response) {
   }

   public final void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
         throws IOException, ServletException {
      if (!(preFilter(servletRequest, servletResponse, filterChain))) {
         return;
      }

      HttpServletRequest request = (HttpServletRequest) servletRequest;
      HttpServletResponse response = (HttpServletResponse) servletResponse;
      String ticket = retrieveTicketFromRequest(request);

      if (CommonUtils.isNotBlank(ticket)) {
         this.logger.debug("Attempting to validate ticket: {}", ticket);
         try {
            Assertion assertion = this.ticketValidator.validate(ticket, constructServiceUrl(request, response));

            this.logger.debug("Successfully authenticated user: {}", assertion.getPrincipal().getName());

            request.setAttribute("_const_cas_assertion_", assertion);

            if (this.useSession) {
               request.getSession().setAttribute("_const_cas_assertion_", assertion);
            }

            createToken(ticket, assertion, response);
            saveAssertion(ticket, assertion);
            onSuccessfulValidation(request, response, assertion);

            if (this.redirectAfterValidation) {
               this.logger.debug("Redirecting after successful ticket validation.");
               response.sendRedirect(constructServiceUrl(request, response));
               return;
            }
         } catch (TicketValidationException e) {
            this.logger.debug(e.getMessage(), e);

            onFailedValidation(request, response);

            if (this.exceptionOnValidationFailure) {
               throw new ServletException(e);
            }

            response.sendError(403, e.getMessage());

            return;
         }
      }

      filterChain.doFilter(request, response);
   }

   public final void setTicketValidator(TicketValidator ticketValidator) {
      this.ticketValidator = ticketValidator;
   }

   public final void setRedirectAfterValidation(boolean redirectAfterValidation) {
      this.redirectAfterValidation = redirectAfterValidation;
   }

   public final void setExceptionOnValidationFailure(boolean exceptionOnValidationFailure) {
      this.exceptionOnValidationFailure = exceptionOnValidationFailure;
   }

   public final void setUseSession(boolean useSession) {
      this.useSession = useSession;
   }

   private void createToken(String ticket, Assertion assertion, HttpServletResponse response) {
      AttributePrincipal principal = assertion.getPrincipal();
      String userID = "";
      if (principal != null) {
         Map params = principal.getAttributes();
         Object userId = params.get("userId");
         if (userId != null) {
            userID = userId.toString();
         }
      }

      if (StringUtils.isBlank(userID))
         return;
      String saveUserid = ticket + "__" + userID;
      TenantUser existUser = RedisUtils.getUserCache("user.info.login.tenant:" + saveUserid);
      HashMap cookiesMap;
      Iterator iterator;
      if (existUser == null) {
         TenantUser user = new TenantUser();
         user.setUserId(saveUserid);
         long ts = System.currentTimeMillis();
         user.setLoginTs(ts);
         String cookieValue = "";
         try {
            cookieValue = TokenGenerator.genToken(saveUserid, ts, RedisUtils.findSeed());
         } catch (EncryptException e) {
            this.logger.error("Fail to generate cookie!", e);
         }

         HashMap cookiesMap = new HashMap();
         cookiesMap.put("tenant_username", saveUserid);
         cookiesMap.put("tenant_token", cookieValue);

         for (Iterator iterator = cookiesMap.keySet().iterator(); iterator.hasNext();) {
            String key = (String) iterator.next();
            Cookie cookie = new Cookie(key, URLEncoder.encode((String) cookiesMap.get(key)));
            cookie.setPath("/");

            cookie.setMaxAge(-1);
            cookie.setHttpOnly(true);
            response.addCookie(cookie);
         }
         try {
            RedisUtils.cacheUser(saveUserid, user);
         } catch (Exception e) {
            this.logger.error("登錄信息寫入到redis緩存中失敗", e);
         }
      } else {
         long ts = existUser.getLoginTs();
         String cookieValue = "";
         try {
            cookieValue = TokenGenerator.genToken(saveUserid, ts, RedisUtils.findSeed());
         } catch (EncryptException e) {
            this.logger.error("Fail to generate cookie!", e);
         }

         cookiesMap = new HashMap();
         cookiesMap.put("tenant_username", saveUserid);
         cookiesMap.put("tenant_token", cookieValue);

         for (iterator = cookiesMap.keySet().iterator(); iterator.hasNext();) {
            String key = (String) iterator.next();
            Cookie cookie = new Cookie(key, URLEncoder.encode((String) cookiesMap.get(key)));
            cookie.setPath("/");

            cookie.setMaxAge(-1);
            cookie.setHttpOnly(true);
            response.addCookie(cookie);
         }
      }
   }

   private void saveAssertion(String ticket, Assertion assertion) {
      if (assertion == null)
         return;
      AttributePrincipal principal = assertion.getPrincipal();
      String userID = "";
      if (principal != null) {
         Map params = principal.getAttributes();
         Object userId = params.get("userId");
         if (userId != null) {
            userID = userId.toString();
         }
      }
      if ((userID != null) && (!(userID.equalsIgnoreCase("")))) {
         String saveUserid = ticket + "__" + userID;
         RedisUtils.putSessionCacheAttribute("tenant_assertion", saveUserid, assertion);
      }
   }

   private String getCasUrl(FilterConfig filterConfig) {
      String casUrl = PropertyUtil.getPropertyByKey("cas.url");
      if (StringUtils.isBlank(casUrl)) {
         casUrl = getPropertyFromInitParams(filterConfig, "casServerUrlPrefix", null);
      }
      return casUrl;
   }
}
View Code

  這樣,servername 和 casServerUrlPrefix 獲取的邏輯能夠自定義了,例如能夠放到 .properties 中。

 

  3.經過casServerUrlPrefix 和 Cas10TicketValidator 或(Cas20ProxyTicketValidator)的 getUrlSuffix方法(返回值分別是「validate」和「serviceValidate」)能夠肯定出 具體的校驗ticket的接口。經過查看 cas server的web.xml,能夠在servlet-mapping中看到getUrlSuffix返回值對應的servlet是org.springframework.web.servlet.DispatcherServlet,參數contextConfigLocation 對應的值是'/WEB-INF/cas-servlet.xml, /WEB-INF/cas-servlet-*.xml',裏面定義了springmvc初始的handlerMapping和handlerAdapter,以下。

  <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

  <bean
      id="handlerMappingC"
      class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"
      p:alwaysUseFullPath="true">
    <property name="mappings">
      <util:properties>
        <prop key="/serviceValidate">serviceValidateController</prop>
        <prop key="/proxyValidate">proxyValidateController</prop>

        <prop key="/p3/serviceValidate">v3ServiceValidateController</prop>
        <prop key="/p3/proxyValidate">v3ProxyValidateController</prop>
        <prop key="/validate">legacyValidateController</prop>
        <prop key="/proxy">proxyController</prop>
        <prop key="/authorizationFailure.html">passThroughController</prop>
      </util:properties>
    </property>
  </bean>

  找到 '/serviceValidate' 對應的bean -> serviceValidateController

   <bean id="abstractValidateController" class="org.jasig.cas.web.ServiceValidateController" abstract="true"
        p:centralAuthenticationService-ref="centralAuthenticationService"
        p:proxyHandler-ref="proxy20Handler"
        p:argumentExtractor-ref="casArgumentExtractor"
        p:servicesManager-ref="servicesManager" />

  <bean id="serviceValidateController" parent="abstractValidateController"
        p:validationSpecificationClass="org.jasig.cas.validation.Cas20WithoutProxyingValidationSpecification"/>

  因而能夠確認 最終調用 org.jasig.cas.web.ServiceValidateController 進行ticket驗證。究竟是調用哪一個方法呢? 這裏先看一下 springmvc中 DispatchServlet的doDispatch方法,以下。

  protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {  
        HttpServletRequest processedRequest = request;  
        HandlerExecutionChain mappedHandler = null;  
        boolean multipartRequestParsed = false;  
  
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);  
  
        try {  
            ModelAndView mv = null;  
            Exception dispatchException = null;  
  
            try {  
                processedRequest = checkMultipart(request);  
                multipartRequestParsed = (processedRequest != request);  
                      //這個是重點,第一步由HandlerMapping找到對應的handler  
                // Determine handler for the current request.  
                mappedHandler = getHandler(processedRequest);  
                if (mappedHandler == null || mappedHandler.getHandler() == null) {  
                    noHandlerFound(processedRequest, response);  
                    return;  
                }  
  
                // Determine handler adapter for the current request.  
                       //這是第二步,找到合適的HandlerAdapter,而後由它來調度執行handler的方法  
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());  
  
                // Process last-modified header, if supported by the handler.  
                String method = request.getMethod();  
                boolean isGet = "GET".equals(method);  
                if (isGet || "HEAD".equals(method)) {  
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());  
                    if (logger.isDebugEnabled()) {  
                        logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);  
                    }  
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {  
                        return;  
                    }  
                }  
  
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {  
                    return;  
                }  
  
                try {  
                    // Actually invoke the handler.  
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());  
                }  
                finally {  
                    if (asyncManager.isConcurrentHandlingStarted()) {  
                        return;  
                    }  
                }  
  
                applyDefaultViewName(request, mv);  
                mappedHandler.applyPostHandle(processedRequest, response, mv);  
            }  
            catch (Exception ex) {  
                dispatchException = ex;  
            }  
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);  
        }  
        catch (Exception ex) {  
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);  
        }  
        catch (Error err) {  
            triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);  
        }  
        finally {  
            if (asyncManager.isConcurrentHandlingStarted()) {  
                // Instead of postHandle and afterCompletion  
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);  
                return;  
            }  
            // Clean up any resources used by a multipart request.  
            if (multipartRequestParsed) {  
                cleanupMultipart(processedRequest);  
            }  
        }  
    }  
View Code

  接下來看一下  org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter 的handle方法,以下。

package org.springframework.web.servlet.mvc;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.ModelAndView;

public class SimpleControllerHandlerAdapter implements HandlerAdapter {
    public boolean supports(Object handler) {
        return handler instanceof Controller;
    }

    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        return ((Controller) handler).handleRequest(request, response);
    }

    public long getLastModified(HttpServletRequest request, Object handler) {
        if (handler instanceof LastModified) {
            return ((LastModified) handler).getLastModified(request);
        }
        return -1L;
    }
}
View Code

  可見,handlerAdapter的handle方法調用了某個具體handler的handleRequest方法,查看如上handler配置中 org.jasig.cas.web.ServiceValidateController 的handleRequest方法,以下。

package org.springframework.web.servlet.mvc;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.support.WebContentGenerator;
import org.springframework.web.util.WebUtils;

public abstract class AbstractController extends WebContentGenerator implements Controller {
    private boolean synchronizeOnSession;

    public AbstractController() {
        this.synchronizeOnSession = false;
    }

    public final void setSynchronizeOnSession(boolean synchronizeOnSession) {
        this.synchronizeOnSession = synchronizeOnSession;
    }

    public final boolean isSynchronizeOnSession() {
        return this.synchronizeOnSession;
    }

    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        checkAndPrepare(request, response, this instanceof LastModified);

        if (this.synchronizeOnSession) {
            HttpSession session = request.getSession(false);
            if (session != null) {
                Object mutex = WebUtils.getSessionMutex(session);
                synchronized (mutex) {
                    return handleRequestInternal(request, response);
                }
            }
        }

        return handleRequestInternal(request, response);
    }

    protected abstract ModelAndView handleRequestInternal(HttpServletRequest paramHttpServletRequest,
            HttpServletResponse paramHttpServletResponse) throws Exception;
}
View Code

  父類handleRequest方法調用了handleRequestInternal方法,子類ServiceValidateController 自身重寫了 handleRequestInternal方法。

/*
 * Licensed to Apereo under one or more contributor license
 * agreements. See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership.
 * Apereo licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file
 * except in compliance with the License.  You may obtain a
 * copy of the License at the following location:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.jasig.cas.web;

import org.jasig.cas.CasProtocolConstants;
import org.jasig.cas.CentralAuthenticationService;
import org.jasig.cas.authentication.AuthenticationException;
import org.jasig.cas.authentication.Credential;
import org.jasig.cas.authentication.HttpBasedServiceCredential;
import org.jasig.cas.authentication.principal.Service;
import org.jasig.cas.authentication.principal.WebApplicationService;
import org.jasig.cas.services.RegisteredService;
import org.jasig.cas.services.ServicesManager;
import org.jasig.cas.services.UnauthorizedProxyingException;
import org.jasig.cas.services.UnauthorizedServiceException;
import org.jasig.cas.ticket.TicketException;
import org.jasig.cas.ticket.TicketGrantingTicket;
import org.jasig.cas.ticket.TicketValidationException;
import org.jasig.cas.ticket.proxy.ProxyHandler;
import org.jasig.cas.validation.Assertion;
import org.jasig.cas.validation.Cas20ProtocolValidationSpecification;
import org.jasig.cas.validation.ValidationSpecification;
import org.jasig.cas.web.support.ArgumentExtractor;
import org.jasig.cas.web.view.CasViewConstants;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import java.net.URL;
import java.util.Collections;
import java.util.Map;

/**
 * Process the /validate , /serviceValidate , and /proxyValidate URL requests.
 * <p>
 * Obtain the Service Ticket and Service information and present them to the CAS
 * validation services. Receive back an Assertion containing the user Principal
 * and (possibly) a chain of Proxy Principals. Store the Assertion in the Model
 * and chain to a View to generate the appropriate response (CAS 1, CAS 2 XML,
 * SAML, ...).
 *
 * @author Scott Battaglia
 * @author Misagh Moayyed
 * @since 3.0.0
 */
public class ServiceValidateController extends DelegateController {
    /** View if Service Ticket Validation Fails. */
    public static final String DEFAULT_SERVICE_FAILURE_VIEW_NAME = "cas2ServiceFailureView";

    /** View if Service Ticket Validation Succeeds. */
    public static final String DEFAULT_SERVICE_SUCCESS_VIEW_NAME = "cas2ServiceSuccessView";

    /** Implementation of Service Manager. */
    @NotNull
    private ServicesManager servicesManager;
    
    /** The CORE which we will delegate all requests to. */
    @NotNull
    private CentralAuthenticationService centralAuthenticationService;

    /** The validation protocol we want to use. */
    @NotNull
    private Class<?> validationSpecificationClass = Cas20ProtocolValidationSpecification.class;

    /** The proxy handler we want to use with the controller. */
    @NotNull
    private ProxyHandler proxyHandler;

    /** The view to redirect to on a successful validation. */
    @NotNull
    private String successView = DEFAULT_SERVICE_SUCCESS_VIEW_NAME;

    /** The view to redirect to on a validation failure. */
    @NotNull
    private String failureView = DEFAULT_SERVICE_FAILURE_VIEW_NAME;

    /** Extracts parameters from Request object. */
    @NotNull
    private ArgumentExtractor argumentExtractor;

    /**
     * Overrideable method to determine which credentials to use to grant a
     * proxy granting ticket. Default is to use the pgtUrl.
     *
     * @param service the webapp service requesting proxy
     * @param request the HttpServletRequest object.
     * @return the credentials or null if there was an error or no credentials
     * provided.
     */
    protected Credential getServiceCredentialsFromRequest(final WebApplicationService service, final HttpServletRequest request) {
        final String pgtUrl = request.getParameter(CasProtocolConstants.PARAMETER_PROXY_CALLBACK_URL);
        if (StringUtils.hasText(pgtUrl)) {
            try {
                final RegisteredService registeredService = this.servicesManager.findServiceBy(service);
                verifyRegisteredServiceProperties(registeredService, service);
                return new HttpBasedServiceCredential(new URL(pgtUrl), registeredService);
            } catch (final Exception e) {
                logger.error("Error constructing pgtUrl", e);
            }
        }

        return null;
    }

    /**
     * Inits the binder with the required fields. <code>renew</code> is required.
     *
     * @param request the request
     * @param binder the binder
     */
    protected void initBinder(final HttpServletRequest request, final ServletRequestDataBinder binder) {
        binder.setRequiredFields("renew");
    }

    @Override
    protected final ModelAndView handleRequestInternal(final HttpServletRequest request, final HttpServletResponse response)
            throws Exception {
        final WebApplicationService service = this.argumentExtractor.extractService(request);
        final String serviceTicketId = service != null ? service.getArtifactId() : null;

        if (service == null || serviceTicketId == null) {
            logger.debug("Could not identify service and/or service ticket for service: [{}]", service);
            return generateErrorView(CasProtocolConstants.ERROR_CODE_INVALID_REQUEST,
                    CasProtocolConstants.ERROR_CODE_INVALID_REQUEST, null);
        }

        try {
            final Credential serviceCredential = getServiceCredentialsFromRequest(service, request);
            TicketGrantingTicket proxyGrantingTicketId = null;
            
            if (serviceCredential != null) {
                try {
                    proxyGrantingTicketId = this.centralAuthenticationService.delegateTicketGrantingTicket(serviceTicketId,
                                serviceCredential);
                    logger.debug("Generated PGT [{}] off of service ticket [{}] and credential [{}]",
                            proxyGrantingTicketId.getId(), serviceTicketId, serviceCredential);
                } catch (final AuthenticationException e) {
                    logger.info("Failed to authenticate service credential {}", serviceCredential);
                } catch (final TicketException e) {
                    logger.error("Failed to create proxy granting ticket for {}", serviceCredential, e);
                }
                
                if (proxyGrantingTicketId == null) {
                    return generateErrorView(CasProtocolConstants.ERROR_CODE_INVALID_PROXY_CALLBACK,
                            CasProtocolConstants.ERROR_CODE_INVALID_PROXY_CALLBACK,
                            new Object[] {serviceCredential.getId()});
                }
            }

            final Assertion assertion = this.centralAuthenticationService.validateServiceTicket(serviceTicketId, service);

            final ValidationSpecification validationSpecification = this.getCommandClass();
            final ServletRequestDataBinder binder = new ServletRequestDataBinder(validationSpecification, "validationSpecification");
            initBinder(request, binder);
            binder.bind(request);

            if (!validationSpecification.isSatisfiedBy(assertion)) {
                logger.debug("Service ticket [{}] does not satisfy validation specification.", serviceTicketId);
                return generateErrorView(CasProtocolConstants.ERROR_CODE_INVALID_TICKET,
                        CasProtocolConstants.ERROR_CODE_INVALID_TICKET, null);
            }

            String proxyIou = null;
            if (serviceCredential != null && this.proxyHandler.canHandle(serviceCredential)) {
                proxyIou = this.proxyHandler.handle(serviceCredential, proxyGrantingTicketId);
                if (StringUtils.isEmpty(proxyIou)) {
                    return generateErrorView(CasProtocolConstants.ERROR_CODE_INVALID_PROXY_CALLBACK,
                            CasProtocolConstants.ERROR_CODE_INVALID_PROXY_CALLBACK,
                            new Object[] {serviceCredential.getId()});
                }
            }

            onSuccessfulValidation(serviceTicketId, assertion);
            logger.debug("Successfully validated service ticket {} for service [{}]", serviceTicketId, service.getId());
            return generateSuccessView(assertion, proxyIou, service, proxyGrantingTicketId);
        } catch (final TicketValidationException e) {
            final String code = e.getCode();
            return generateErrorView(code, code,
                    new Object[] {serviceTicketId, e.getOriginalService().getId(), service.getId()});
        } catch (final TicketException te) {
            return generateErrorView(te.getCode(), te.getCode(),
                new Object[] {serviceTicketId});
        } catch (final UnauthorizedProxyingException e) {
            return generateErrorView(e.getMessage(), e.getMessage(), new Object[] {service.getId()});
        } catch (final UnauthorizedServiceException e) {
            return generateErrorView(e.getMessage(), e.getMessage(), null);
        }
    }

    /**
     * Triggered on successful validation events. Extensions are to
     * use this as hook to plug in behvior.
     *
     * @param serviceTicketId the service ticket id
     * @param assertion the assertion
     */
    protected void onSuccessfulValidation(final String serviceTicketId, final Assertion assertion) {
        // template method with nothing to do.
    }

    /**
     * Generate error view, set to {@link #setFailureView(String)}.
     *
     * @param code the code
     * @param description the description
     * @param args the args
     * @return the model and view
     */
    private ModelAndView generateErrorView(final String code, final String description, final Object[] args) {
        final ModelAndView modelAndView = new ModelAndView(this.failureView);
        final String convertedDescription = getMessageSourceAccessor().getMessage(description, args, description);
        modelAndView.addObject("code", code);
        modelAndView.addObject("description", convertedDescription);

        return modelAndView;
    }

    /**
     * Generate the success view. The result will contain the assertion and the proxy iou.
     *
     * @param assertion the assertion
     * @param proxyIou the proxy iou
     * @param service the validated service
     * @param proxyGrantingTicket the proxy granting ticket
     * @return the model and view, pointed to the view name set by
     */
    private ModelAndView generateSuccessView(final Assertion assertion, final String proxyIou,
                                             final WebApplicationService service,
                                             final TicketGrantingTicket proxyGrantingTicket) {

        final ModelAndView success = new ModelAndView(this.successView);
        success.addObject(CasViewConstants.MODEL_ATTRIBUTE_NAME_ASSERTION, assertion);
        success.addObject(CasViewConstants.MODEL_ATTRIBUTE_NAME_SERVICE, service);
        success.addObject(CasViewConstants.MODEL_ATTRIBUTE_NAME_PROXY_GRANTING_TICKET_IOU, proxyIou);
        if (proxyGrantingTicket != null) {
            success.addObject(CasViewConstants.MODEL_ATTRIBUTE_NAME_PROXY_GRANTING_TICKET, proxyGrantingTicket.getId());
        }
        final Map<String, ?> augmentedModelObjects = augmentSuccessViewModelObjects(assertion);
        if (augmentedModelObjects != null) {
            success.addAllObjects(augmentedModelObjects);
        }
        return success;
    }

    /**
     * Augment success view model objects. Provides
     * a way for extension of this controller to dynamically
     * populate the model object with attributes
     * that describe a custom nature of the validation protocol.
     *
     * @param assertion the assertion
     * @return map of objects each keyed to a name
     */
    protected Map<String, ?> augmentSuccessViewModelObjects(final Assertion assertion) {
        return Collections.emptyMap();  
    }
    
    /**
     * Gets the command class based on {@link #setValidationSpecificationClass(Class)}.
     *
     * @return the command class
     */
    private ValidationSpecification getCommandClass() {
        try {
            return (ValidationSpecification) this.validationSpecificationClass.newInstance();
        } catch (final Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean canHandle(final HttpServletRequest request, final HttpServletResponse response) {
        return true;
    }

    /**
     * @param centralAuthenticationService The centralAuthenticationService to
     * set.
     */
    public final void setCentralAuthenticationService(final CentralAuthenticationService centralAuthenticationService) {
        this.centralAuthenticationService = centralAuthenticationService;
    }

    
    public final void setArgumentExtractor(final ArgumentExtractor argumentExtractor) {
        this.argumentExtractor = argumentExtractor;
    }

    /**
     * @param validationSpecificationClass The authenticationSpecificationClass
     * to set.
     */
    public final void setValidationSpecificationClass(final Class<?> validationSpecificationClass) {
        this.validationSpecificationClass = validationSpecificationClass;
    }

    /**
     * @param failureView The failureView to set.
     */
    public final void setFailureView(final String failureView) {
        this.failureView = failureView;
    }

    /**
     * @param successView The successView to set.
     */
    public final void setSuccessView(final String successView) {
        this.successView = successView;
    }

    /**
     * @param proxyHandler The proxyHandler to set.
     */
    public final void setProxyHandler(final ProxyHandler proxyHandler) {
        this.proxyHandler = proxyHandler;
    }

    /**
     * Sets the services manager.
     *
     * @param servicesManager the new services manager
     */
    public final void setServicesManager(final ServicesManager servicesManager) {
        this.servicesManager = servicesManager;
    }

    /**
     * Ensure that the service is found and enabled in the service registry.
     * @param registeredService the located entry in the registry
     * @param service authenticating service
     * @throws UnauthorizedServiceException
     */
    private void verifyRegisteredServiceProperties(final RegisteredService registeredService, final Service service) {
        if (registeredService == null) {
            final String msg = String.format("ServiceManagement: Unauthorized Service Access. "
                    + "Service [%s] is not found in service registry.", service.getId());
            logger.warn(msg);
            throw new UnauthorizedServiceException(UnauthorizedServiceException.CODE_UNAUTHZ_SERVICE, msg);
        }
        if (!registeredService.getAccessStrategy().isServiceAccessAllowed()) {
            final String msg = String.format("ServiceManagement: Unauthorized Service Access. "
                    + "Service [%s] is not enabled in service registry.", service.getId());
            
            logger.warn(msg);
            throw new UnauthorizedServiceException(UnauthorizedServiceException.CODE_UNAUTHZ_SERVICE, msg);
        }
    }
}
View Code

  handleRequestInternal方法中調用了一個argumentExtractor,在上面的配置中能夠看到,可肯定這是個 spring bean。這個bean在 WEB-INF/spring-configuration/argumentExtractorsConfiguration.xml中,內容以下。

<?xml version="1.0" encoding="UTF-8"?>
<!--

    Licensed to Apereo under one or more contributor license
    agreements. See the NOTICE file distributed with this work
    for additional information regarding copyright ownership.
    Apereo licenses this file to you under the Apache License,
    Version 2.0 (the "License"); you may not use this file
    except in compliance with the License.  You may obtain a
    copy of the License at the following location:

      http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing,
    software distributed under the License is distributed on an
    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, either express or implied.  See the License for the
    specific language governing permissions and limitations
    under the License.

-->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
    <description>
        Argument Extractors are what are used to translate HTTP requests into requests of the appropriate protocol (i.e.
        CAS, SAML, SAML2,
        OpenId, etc.). By default, only CAS is enabled.
    </description>

    <!--
    <bean id="samlArgumentExtractor" class="org.jasig.cas.support.saml.web.support.SamlArgumentExtractor" />
    -->

    <bean
            id="casArgumentExtractor"
            class="org.jasig.cas.web.support.CasArgumentExtractor"/>

    <util:list id="argumentExtractors">
        <!-- <ref bean="samlArgumentExtractor" /> -->
        <ref bean="casArgumentExtractor"/>
    </util:list>
</beans>
View Code

  查看org.jasig.cas.web.support.CasArgumentExtractor內容以下。

/*** Eclipse Class Decompiler plugin, copyright (c) 2016 Chen Chao (cnfree2000@hotmail.com) ***/
package org.jasig.cas.web.support;

import javax.servlet.http.HttpServletRequest;
import org.jasig.cas.authentication.principal.SimpleWebApplicationServiceImpl;
import org.jasig.cas.authentication.principal.WebApplicationService;

public final class CasArgumentExtractor extends AbstractArgumentExtractor {
    public WebApplicationService extractServiceInternal(HttpServletRequest request) {
        return SimpleWebApplicationServiceImpl.createServiceFrom(request);
    }
}
View Code

  最後查看org.jasig.cas.authentication.principal.SimpleWebApplicationServiceImpl內容以下。

/*** Eclipse Class Decompiler plugin, copyright (c) 2016 Chen Chao (cnfree2000@hotmail.com) ***/
package org.jasig.cas.authentication.principal;

import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.util.StringUtils;

public final class SimpleWebApplicationServiceImpl extends AbstractWebApplicationService {
    private static final long serialVersionUID = 8334068957483758042L;
    private static final String CONST_PARAM_SERVICE = "service";
    private static final String CONST_PARAM_TARGET_SERVICE = "targetService";
    private static final String CONST_PARAM_TICKET = "ticket";
    private static final String CONST_PARAM_METHOD = "method";
    private final Response.ResponseType responseType;

    public SimpleWebApplicationServiceImpl(String id) {
        this(id, id, null, null);
    }

    private SimpleWebApplicationServiceImpl(String id, String originalUrl, String artifactId,
            Response.ResponseType responseType) {
        super(id, originalUrl, artifactId);
        this.responseType = responseType;
    }

    public static SimpleWebApplicationServiceImpl createServiceFrom(HttpServletRequest request) {
        String targetService = request.getParameter("targetService");
        String service = request.getParameter("service");
        String serviceAttribute = (String) request.getAttribute("service");
        String method = request.getParameter("method");
        String serviceToUse;
        String serviceToUse;
        if (StringUtils.hasText(targetService)) {
            serviceToUse = targetService;
        } else {
            String serviceToUse;
            if (StringUtils.hasText(service))
                serviceToUse = service;
            else {
                serviceToUse = serviceAttribute;
            }
        }
        if (!(StringUtils.hasText(serviceToUse))) {
            return null;
        }

        String id = cleanupUrl(serviceToUse);
        String artifactId = request.getParameter("ticket");

        return new SimpleWebApplicationServiceImpl(id, serviceToUse, artifactId,
                ("POST".equals(method)) ? Response.ResponseType.POST : Response.ResponseType.REDIRECT);
    }

    public Response getResponse(String ticketId) {
        Map parameters = new HashMap();

        if (StringUtils.hasText(ticketId)) {
            parameters.put("ticket", ticketId);
        }

        if (Response.ResponseType.POST == this.responseType) {
            return DefaultResponse.getPostResponse(getOriginalUrl(), parameters);
        }
        return DefaultResponse.getRedirectResponse(getOriginalUrl(), parameters);
    }
}
View Code

  在SimpleWebApplicationServiceImpl createServiceFrom方法中能夠看到,ticket從request中取到。也就是對應 org.jasig.cas.web.ServiceValidateController 的handleRequestInternal 以下獲取方式。

final WebApplicationService service = this.argumentExtractor.extractService(request);
final String serviceTicketId = service != null ? service.getArtifactId() : null;

 

經過配置deployerConfigContext.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:context="http://www.springframework.org/schema/context"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:sec="http://www.springframework.org/schema/security"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

    <!-- 數據源配置, 使用druid鏈接池 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
          init-method="init" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driver}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <!-- 配置初始化大小、最小、最大 -->
        <property name="initialSize" value="${druid.initialSize}"/>
        <property name="minIdle" value="${druid.minIdle}"/>
        <property name="maxActive" value="${druid.maxActive}"/>

        <!-- 配置獲取鏈接等待超時的時間 -->
        <property name="maxWait" value="${druid.maxWait}"/>
        <!-- 配置間隔多久才進行一次檢測,檢測須要關閉的空閒鏈接,單位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="${druid.timeBetweenEvictionRunsMillis}" />

        <!-- 配置一個鏈接在池中最小生存的時間,單位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="${druid.minEvictableIdleTimeMillis}" />

        <property name="validationQuery" value="${druid.validationQuery}" />
        <property name="testWhileIdle" value="${druid.testWhileIdle}" />
        <property name="testOnBorrow" value="${druid.testOnBorrow}" />
        <property name="testOnReturn" value="${druid.testOnReturn}" />

        <!-- 打開PSCache,而且指定每一個鏈接上PSCache的大小  若是用Oracle,則把poolPreparedStatements配置爲true,mysql能夠配置爲false。-->
        <property name="poolPreparedStatements" value="${druid.poolPreparedStatements}" />
        <property name="maxPoolPreparedStatementPerConnectionSize" value="${druid.maxPoolPreparedStatementPerConnectionSize}" />
    </bean>

    <bean id="usernamePasswordAuthenticationHandler" class="com.hjzgg.auth.UsernamePasswordAuthenticationHandler"/>

    <util:map id="authenticationHandlersResolvers">
        <entry key-ref="proxyAuthenticationHandler" value-ref="proxyPrincipalResolver" />
        <entry key-ref="primaryAuthenticationHandler" value-ref="primaryPrincipalResolver" />
    </util:map>

    <util:list id="authenticationMetadataPopulators">
        <ref bean="successfulHandlerMetaDataPopulator" />
        <ref bean="rememberMeAuthenticationMetaDataPopulator" />
    </util:list>

    <bean id="attributeRepository" class="org.jasig.services.persondir.support.NamedStubPersonAttributeDao"
          p:backingMap-ref="attrRepoBackingMap" />

    <!--first 默認方式-->
    <!--<alias name="acceptUsersAuthenticationHandler" alias="primaryAuthenticationHandler" />-->

    <!--second 使用 MD5對密碼進行加密-->
    <!--<bean id="MD5PasswordEncoder" class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder" autowire="byName">
        <constructor-arg  value="MD5"/>
    </bean >
    <bean id="queryDatabaseAuthenticationHandler" name="primaryAuthenticationHandler" class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler">
        <property name="passwordEncoder" ref="MD5PasswordEncoder"/>
    </bean>-->

    <!--third 不使用加密算法-->
    <!--<alias name="queryDatabaseAuthenticationHandler" alias="primaryAuthenticationHandler" />-->

    <!--forth 經過繼承AbstractUsernamePasswordAuthenticationHandler,並實現authenticateUsernamePasswordInternal-->
    <alias name="usernamePasswordAuthenticationHandler" alias="primaryAuthenticationHandler" />
    <alias name="personDirectoryPrincipalResolver" alias="primaryPrincipalResolver" />

    <alias name="dataSource" alias="queryDatabaseDataSource" />

    <util:map id="attrRepoBackingMap">
        <entry key="uid" value="uid" />
        <entry key="eduPersonAffiliation" value="eduPersonAffiliation" />
        <entry key="groupMembership" value="groupMembership" />
        <entry>
            <key><value>memberOf</value></key>
            <list>
                <value>faculty</value>
                <value>staff</value>
                <value>org</value>
            </list>
        </entry>
    </util:map>

    <alias name="serviceThemeResolver" alias="themeResolver" />

    <alias name="jsonServiceRegistryDao" alias="serviceRegistryDao" />

    <alias name="defaultTicketRegistry" alias="ticketRegistry" />
    
    <alias name="ticketGrantingTicketExpirationPolicy" alias="grantingTicketExpirationPolicy" />
    <alias name="multiTimeUseOrTimeoutExpirationPolicy" alias="serviceTicketExpirationPolicy" />

    <alias name="anyAuthenticationPolicy" alias="authenticationPolicy" />
    <alias name="acceptAnyAuthenticationPolicyFactory" alias="authenticationPolicyFactory" />

    <bean id="auditTrailManager"
          class="org.jasig.inspektr.audit.support.Slf4jLoggingAuditTrailManager"
          p:entrySeparator="${cas.audit.singleline.separator:|}"
          p:useSingleLine="${cas.audit.singleline:false}"/>

    <alias name="neverThrottle" alias="authenticationThrottle" />

    <util:list id="monitorsList">
        <ref bean="memoryMonitor" />
        <ref bean="sessionMonitor" />
    </util:list>

    <alias name="defaultPrincipalFactory" alias="principalFactory" />
    <alias name="defaultAuthenticationTransactionManager" alias="authenticationTransactionManager" />
    <alias name="defaultPrincipalElectionStrategy" alias="principalElectionStrategy" />
    <alias name="tgcCipherExecutor" alias="defaultCookieCipherExecutor" />
</beans>

 

相關參考

CAS Properties

Database Authentication

CAS4.2.x

報異常:Application Not Authorized to Use CAS

       cas4.2.7 取消https  

Cas https 證書生成

keytool -genkey -keyalg RSA -alias cas.server.com -dname "cn=cas.server.com" -keystore /usr/data/tomcat.keystore
 
sudo keytool -export -alias cas.server.com -keystore /usr/data/tomcat.keystore  -file /usr/data/tomcat.cer
 
cd  /Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/jre/lib/security
 
sudo keytool -import -keystore cacerts -file /usr/data/tomcat.cer -alias cas.server.com -storepass changeit
 
 
sudo keytool -delete -trustcacerts -alias cas.server.com -keystore "/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/jre/lib/security/cacerts" -storepass changeit
 
keytool -list -keystore /Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/jre/lib/security/cacerts -storepass changeit 

 

完整項目地址:https://github.com/hjzgg/cas4.2.7-authentication

cas4.2.7 template項目地址:https://github.com/hjzgg/cas-overlay-template-4.2.7

相關文章
相關標籤/搜索