CAS(9)-通過Proxy訪問其它Cas應用

考慮這樣一種場景:有兩個應用App1和App2,它們都是受Cas Server保護的,即請求它們時都需要通過Cas Server的認證。現需要在App1中通過Http請求訪問App2,顯然該請求將會被App2配置的Cas的AuthenticationFilter攔截並轉向Cas Server,Cas Server將引導用戶進行登錄認證,這樣我們也就不能真正的訪問到App2了。針對這種應用場景,Cas也提供了對應的支持。

1.1     原理

       Cas Proxy可以讓我們輕鬆的通過App1訪問App2時通過Cas Server的認證,從而訪問到App2。其主要原理是這樣的,App1先通過Cas Server的認證,然後向Cas Server申請一個針對於App2的proxy ticket,之後在訪問App2時把申請到的針對於App2的proxy ticket以參數ticket傳遞過去。App2的AuthenticationFilter將攔截到該請求,發現該請求攜帶了ticket參數後將放行交由後續的Ticket Validation Filter處理。Ticket Validation Filter將會傳遞該ticket到Cas Server進行認證,顯然該ticket是由Cas Server針對於App2發行的,App2在申請校驗時是可以校驗通過的,這樣我們就可以正常的訪問到App2了。針對Cas Proxy的原理,官網有一張圖很能說明問題,如下所示。



 

 

1.2     配置

       Cas Proxy實現的核心是Cas20ProxyReceivingTicketValidationFilter,該Filter是Ticket Validation Filter的一種。使用Cas Proxy時我們需要使用Cas20ProxyReceivingTicketValidationFilter作爲我們的Ticket Validation Filter,而且對於代理端而言該Filter需要放置在AuthenticationFilter之前。對於上述應用場景而言,App1就是我們的代理端,而App2就是我們的被代理端。Cas20ProxyReceivingTicketValidationFilter在代理端與被代理端的配置是不一樣的。我們先來看一下在代理端的配置。

1.2.1代理端

       既然Cas20ProxyReceivingTicketValidationFilter是一個Ticket Validation Filter,所以之前我們介紹的Ticket Validation Filter需要配置的參數,在這裏也需要配置,Ticket Validation Filter可以配置的參數這裏也可以配置。所不同的是對於代理端的Cas20ProxyReceivingTicketValidationFilter必須指定另外的兩個參數,proxyCallbackUrl和proxyReceptorUrl。

 

l  proxyCallbackUrl:用於指定一個回調地址,在代理端通過Cas Server校驗ticket成功後,Cas Server將回調該地址以傳遞pgtId和pgtIou,Cas20ProxyReceivingTicketValidationFilter在接收到對應的響應後會將它們保存在內部持有的ProxyGrantingTicketStorage中。之後在對傳遞過來的ticket進行validate的時候又會根據pgtIou從ProxyGrantingTicketStorage中獲取對應的pgtId,用以保存在AttributePrincipal中,而AttributePrincipal又會保存在Assertion中。proxyCallbackUrl因爲是指定Cas Server回調的地址,所以其必須是一個可以供外部訪問的絕對地址。此外,因爲Cas Server默認只回調使用安全通道協議https進行通信的地址,所以我們的proxyCallbackUrl需要是一個使用https協議訪問的地址。

l  proxyReceptorUrl:該地址是proxyCallbackUrl相對於代理端的一個地址,Cas20ProxyReceivingTicketValidationFilter將根據該地址來決定請求是否來自Cas Server的回調。

 

       下面是一個Cas20ProxyReceivingTicketValidationFilter在代理端配置的示例,需要注意的是該Filter需要配置在AuthenticationFilter之前,所以完整配置如下:

 

   <context-param>

      <param-name>serverName</param-name>

      <param-value>https://elim:8043</param-value>

   </context-param>

 

   <filter>

      <filter-name>proxyValidationFilter</filter-name>

   <filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class>

      <init-param>

         <param-name>casServerUrlPrefix</param-name>

         <param-value>https://elim:8443/cas</param-value>

      </init-param>

      <init-param>

         <param-name>proxyCallbackUrl</param-name>

         <param-value>https://elim:8043/app1/proxyCallback</param-value>

      </init-param>

      <init-param>

         <param-name>proxyReceptorUrl</param-name>

         <param-value>/proxyCallback</param-value>

      </init-param>

   </filter>

   <filter-mapping>

      <filter-name>proxyValidationFilter</filter-name>

      <url-pattern>/*</url-pattern>

   </filter-mapping> 

 

   <filter>

      <filter-name>casAuthenticationFilter</filter-name>

   <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>

      <init-param>

         <param-name>casServerLoginUrl</param-name>

         <param-value>https://elim:8443/cas/login</param-value>

      </init-param>

   </filter>

   <filter-mapping>

      <filter-name>casAuthenticationFilter</filter-name>

      <url-pattern>/*</url-pattern>

   </filter-mapping>

 

   <filter>

      <filter-name>casHttpServletRequestWrapperFilter</filter-name>

   <filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class>

   </filter>

   <filter-mapping>

      <filter-name>casHttpServletRequestWrapperFilter</filter-name>

      <url-pattern>/*</url-pattern>

   </filter-mapping>

 

   <filter>

      <filter-name>casAssertionThreadLocalFilter</filter-name>

     <filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class>

   </filter>

   <filter-mapping>

      <filter-name>casAssertionThreadLocalFilter</filter-name>

      <url-pattern>/*</url-pattern>

   </filter-mapping>

 

1.2.2被代理端

       在被代理端Cas20ProxyReceivingTicketValidationFilter是扮演Ticket Validation Filter的角色,它可以驗證正常通過Cas Server登錄認證成功後返回的ticket,也可以認證來自其它代理端傳遞過來的proxy ticket,當然,最終的認證都是通過Cas Server來完成的。既然Cas20ProxyReceivingTicketValidationFilter在被代理端是作爲Ticket Validation Filter來使用的,所以Ticket Validation Filter可以有的參數其都可以配置。在被代理端需要配置一個參數用以表示接受來自哪些應用的代理,這個參數可以是acceptAnyProxy,也可以是allowedProxyChains。acceptAnyProxy表示接受所有的,其對應的參數值是true或者false;而allowedProxyChains則用以指定具體接受哪些應用的代理,多個應用就寫多行,allowedProxyChains的值對應的是代理端提供給Cas Server的回調地址,如果使用前文示例的代理端配置,我們就可以指定被代理端的allowedProxyChains爲“https://elim:8043/app1/proxyCallback”,這樣當app1作爲代理端來訪問該被代理端時就能通過驗證,得到正確的響應。下面是一個被代理端配置Cas20ProxyReceivingTicketValidationFilter的完整配置示例。

   <context-param>

      <param-name>serverName</param-name>

      <param-value>http://elim:8081</param-value>

   </context-param>

 

   <filter>

      <filter-name>casSingleSignOutFilter</filter-name>

      <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>

   </filter>

   <filter-mapping>

      <filter-name>casSingleSignOutFilter</filter-name>

      <url-pattern>/*</url-pattern>

   </filter-mapping>

 

   <filter>

      <filter-name>casAuthenticationFilter</filter-name>

   <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>

      <init-param>

         <param-name>casServerLoginUrl</param-name>

         <param-value>https:// elim:8443/cas/login</param-value>

      </init-param>

   </filter>

   <filter-mapping>

      <filter-name>casAuthenticationFilter</filter-name>

      <url-pattern>/*</url-pattern>

   </filter-mapping>

 

   <filter>

      <filter-name>proxyValidationFilter</filter-name>

   <filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class>

      <init-param>

         <param-name>casServerUrlPrefix</param-name>

         <param-value>https://elim:8443/cas</param-value>

      </init-param>

      <init-param>

         <param-name>acceptAnyProxy</param-name>

         <param-value>true</param-value>

      </init-param>

   </filter>

   <filter-mapping>

      <filter-name>proxyValidationFilter</filter-name>

      <url-pattern>/*</url-pattern>

   </filter-mapping>

 

   <filter>

      <filter-name>casHttpServletRequestWrapperFilter</filter-name>

   <filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class>

   </filter>

   <filter-mapping>

      <filter-name>casHttpServletRequestWrapperFilter</filter-name>

      <url-pattern>/*</url-pattern>

   </filter-mapping>

 

   <filter>

      <filter-name>casAssertionThreadLocalFilter</filter-name>

     <filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class>

   </filter>

   <filter-mapping>

      <filter-name>casAssertionThreadLocalFilter</filter-name>

      <url-pattern>/*</url-pattern>

   </filter-mapping>

 

1.3     請求示例

       配置好以後接下來將展示一個app1作爲代理端訪問app2的應用示例。該示例的重點在於app1的請求發起,對於需要請求的app2端的內容我們假設就是一個簡單的jsp文件,其簡單的輸出一些文本。對於代理端而言,其請求的發起通常需要經過如下步驟:

       1、獲取到當前的AttributePrincipal對象,如果當前可以獲取到request對象並且使用了HttpServletRequestWrapperFilter,我們則可以直接從request中獲取。

   AttributePrincipal principal = (AttributePrincipal) req.getUserPrincipal();

 

       當然,如果使用了AssertionThreadLocalFilter,我們也可以從AssertionHolder中獲取Assertion,進而獲取到對應的AttributePrincipal對象。

   AttributePrincipal principal = AssertionHolder.getAssertion().getPrincipal();

 

       2、通過AttributePrincipal獲取針對於被代理端對應的proxy ticket,該操作將促使AttributePrincipal向Cas Server發起請求,從而獲取到對應的proxy ticket。針對同一URL每次從Cas Server請求獲取到的proxy ticket都是不一樣的。以下是一個獲取針對於“http://elim:8081/app2/getData.jsp”的proxy ticket的示例:

   String proxyTicket = principal.getProxyTicketFor("http://elim:8081/app2/getData.jsp");

 

       3、在請求被代理端時將獲取到的proxy ticket以參數ticket一起傳遞過去,如:

   URL url = new URL("http://elim:8081/app2/getData.jsp?ticket=" + proxyTicket);

 

       完整的示例代碼如下所示:

@WebServlet(name="casProxyTest", urlPatterns="/cas/proxy/test")

public class CasProxyTestServlet extends HttpServlet {

 

   protected void doGet(HttpServletRequest req, HttpServletResponse resp)

         throws ServletException, IOException {

      doPost(req, resp);

   }

 

   protected void doPost(HttpServletRequest req, HttpServletResponse resp)

         throws ServletException, IOException {

      //1、獲取到AttributePrincipal對象

      AttributePrincipal principal = AssertionHolder.getAssertion().getPrincipal();

      //2、獲取對應的proxy ticket

      String proxyTicket = principal.getProxyTicketFor("http://elim:8081/app/getData.jsp");

      //3、請求被代理應用時將獲取到的proxy ticket以參數ticket進行傳遞

      URL url = new URL("http://elim:8081/app/getData.jsp?ticket=" + URLEncoder.encode(proxyTicket, "UTF-8"));

      HttpURLConnection conn = (HttpURLConnection)url.openConnection();

      BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));

      StringBuffer content = new StringBuffer();

      String line = null;

      while ((line=br.readLine()) != null) {

         content.append(line).append("<br/>");

      }

      resp.getWriter().write(content.toString());

   }

}

 

(注:本文是基於Cas Server3.5.2和Cas Client3.1.11所寫,原文地址:http://haohaoxuexi.iteye.com/blog/2145751

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章