CAS (5) —— Nginx代理模式下瀏覽器訪問CAS伺服器配置詳解
tomcat版本: tomcat-8.0.29
jdk版本: jdk1.8.0_65
nginx版本: nginx-1.9.8
cas版本: cas4.1.2
cas-client-3.4.1
參考來源:
https://github.com/Jasig/java-cas-client
以下的示例採用我部落格的另外兩篇文章中搭建好的測試環境舉例
CAS (1) —— Mac下配置CAS到Tomcat(服務端)
CAS (2) —— Mac下配置CAS到Tomcat(客戶端)
CAS (3) —— Mac下配置CAS客戶端經代理訪問Tomcat CAS
Mac為nginx安裝nginx-sticky-module
【高可用HA】Nginx (1) —— Mac下配置Nginx Http負載均衡(Load Balancer)之101例項
Nginx (2) —— Mac下配置Apache Httpd的Https/SSL (待出)
目標架構
此代理非彼代理
在CAS官方網站上給出了一個“Proxy Web Flow Diagram”:
順序圖:(來源於http://jasig.github.io/cas/4.0.x/protocol/CAS-Protocol.html)
這個方案主要適用一種場景:
有兩個應用App1和App2,它們都是受Cas Server保護的,即請求它們時都需要通過Cas Server的認證。現需要在App1中通過Http請求訪問App2,顯然該請求將會被App2配置的Cas的AuthenticationFilter攔截並轉向Cas Server,Cas Server將引導使用者進行登入認證,這樣我們也就不能真正的訪問到App2了。針對這種應用場景,Cas也提供了對應的支援。通過Proxy訪問其它Cas應用
無論是用中文關鍵字在“度娘”,還是用英文關鍵字再“谷哥”上搜尋,多數文章都是描述上面這樣一個場景。
而我這裡介紹的“代理”,並非是上述場景——依靠代理去驗證ticket,“代理”在此的角色是:
- 只做分發反向代理(未來的負載均衡器)
* 注意:所以說“此代理非彼代理”
準備
要搭建上面這個環境會相對複雜,我們需要參照之前的文章準備以下必備的元件或環境:
2個Tomcat伺服器作為客戶端應用程式伺服器(即cas的客戶端)
app1.hoau.com:8081/8413(http/https) app2.hoau.com:8082/8423(http/https)
1個配置好SSL的Nginx伺服器作為中間層代理轉發伺服器(後可擴充套件為LoadBalancer)
proxy.sso.hoau.com:85/443(http/https)
另一個1個帶有SSL的Tomcat伺服器作為CAS伺服器
sso.hoau.com:8083/8433(http/https)
關鍵配置
代理伺服器(Nginx x 1)
nginx.confhttp
server:
server { listen 85; server_name proxy.sso.hoau.com; location / { #index index.html index.htm; #設定主機頭和客戶端真實地址,以便伺服器獲取客戶端真實IP proxy_set_header Host $host; proxy_set_header Referer $http_referer; proxy_set_header Cookie $http_cookie; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_redirect off; #禁用快取 #proxy_buffering off; proxy_connect_timeout 3; proxy_send_timeout 30; proxy_read_timeout 30; proxy_pass http://cas_server_http; }
upstream:
upstream cas_server_http { #根據ip計算將請求分配各那個後端tomcat,許多人誤認為可以解決session問題,其實並不能。 #同一機器在多網情況下,路由切換,ip可能不同 #ip_hash; #sticky; #Richard: http server localhost:8083 weight=1 srun_id=c; #server localhost:8084 weight=1 srun_id=c; jvm_route $cookie_JSESSIONID|sessionid reverse; }
*注意:
(1)以上的“jvm_route $cookie_JSESSIONID|sessionid reverse;”是關鍵配置,因為CAS是依賴於Session和Cookie進行身份驗證的。
(2)srun_id=c,其中“c”需要與CAS伺服器Tomcat server.xml檔案裡的jvmRoute配置“
<Engine name="Catalina" defaultHost="localhost" jvmRoute="c">”
https
server:
server { listen 443; server_name proxy.sso.hoau.com; ssl on; ssl_certificate /Users/Richard/Documents/Dev/servers/cluster/nginx/keys/server.crt; ssl_certificate_key /Users/Richard/Documents/Dev/servers/cluster/nginx/keys/server.key; ssl_session_timeout 5m; ssl_protocols SSLv3 TLSv1; ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP; ssl_prefer_server_ciphers on; location / { proxy_redirect off; proxy_set_header Host $host; proxy_set_header Referer $http_referer; proxy_set_header Cookie $http_cookie; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-FORWARDED-HOST $server_addr; proxy_set_header X-FORWARDED-PORT $server_port; proxy_connect_timeout 3; proxy_send_timeout 30; proxy_read_timeout 30; proxy_pass https://cas_server_ssl; } }
*注意:以上的ssl為關鍵配置“ssl_certificate”和“ssl_certificate_key”需要指向正確的證照和金鑰。
upstream:
upstream cas_server_ssl { #Richard: https todo server sso.hoau.com:8433 weight=1 srun_id=c; #server sso.hoau.com:8443 weight=1 srun_id=c; jvm_route $cookie_JSESSIONID|sessionid reverse; }
*注意:以上http和https可以只配一項,或兩者兼存皆可,埠不要衝突。
CAS客戶端應用伺服器(Tomcat x 2)
以下客戶端的藍本可以在github上收到(關鍵字:“cas-sample-java-webapp”),我這裡只貼出自己的關鍵點和修改後的結果。
CAS客戶端的應用伺服器有兩臺,如果不使用Spring Security的整合,比較關鍵配置就只有pom.xml(編譯)和web.xml(部署):
兩個環境編譯類似,pom.xml(貼全了,有無冗餘請自行解決):
*注意:以下Spring Security相關依賴為非必須
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>iamlabs.unicon.net</groupId> <artifactId>cas-sample-java-webapp</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <name>CAS Example Java Web App</name> <description>A sample web application that exercises the CAS protocol features via the Java CAS Client.</description> <build> <finalName>cas-sample-java-webapp</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.5.1</version> <configuration> <source>1.7</source> <target>1.7</target> </configuration> </plugin> </plugins> </build> <properties> <spring.version>3.2.4.RELEASE</spring.version> <casclient.version>3.4.1</casclient.version> </properties> <dependencies> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.1</version> </dependency> <!-- Logging --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.13</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.7.13</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.13</version> </dependency> <dependency> <groupId>org.opensaml</groupId> <artifactId>opensaml1</artifactId> <version>1.1</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.jasig.cas.client</groupId> <artifactId>cas-client-core</artifactId> <version>${casclient.version}</version> <exclusions> <exclusion> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.jasig.cas.client</groupId> <artifactId>cas-client-integration-tomcat-common</artifactId> <version>${casclient.version}</version> </dependency> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.6</version> </dependency> <dependency> <groupId>org.apache.santuario</groupId> <artifactId>xmlsec</artifactId> <version>1.4.3</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>${spring.version}</version> </dependency> </dependencies> </project>
-
web.xml:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <context-param> <param-name>log4jConfigLocation</param-name> <param-value>/WEB-INF/log4j.properties</param-value> </context-param> <listener> <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class> </listener> <!-- <listener> <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class> </listener> <filter> <filter-name>CAS Single Sign Out Filter</filter-name> <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class> <init-param> <param-name>casServerUrlPrefix</param-name> <param-value>https://proxy.sso.hoau.com:443</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Single Sign Out Filter</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> <!-- <filter-class>org.jasig.cas.client.validation.Saml11TicketValidationFilter</filter-class> --> <!-- <filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class> --> <init-param> <param-name>casServerUrlPrefix</param-name> <!-- <param-value>https://sso.hoau.com:8433/cas</param-value> <param-value>https://proxy.sso.hoau.com:443/cas</param-value> --> <param-value>https://proxy.sso.hoau.com:443/cas</param-value> </init-param> <!----> <init-param> <param-name>serverName</param-name> <param-value>https://app1.hoau.com:8413</param-value> </init-param> <init-param> <param-name>redirectAfterValidation</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>useSession</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>acceptAnyProxy</param-name> <param-value>true</param-value> </init-param> <!-- <init-param> <param-name>ticketValidatorClass</param-name> <param-value>org.jasig.cas.client.validation.Cas20ProxyTicketValidator</param-value> </init-param> --> <!-- http://haohaoxuexi.iteye.com/blog/2145751 <init-param> <param-name>proxyReceptorUrl</param-name> <param-value>/proxyCallback</param-value> </init-param> <init-param> <param-name>proxyCallbackUrl</param-name> <param-value>https://app1.hoau.com:8413/cas1/proxyCallback</param-value> </init-param> --> </filter> <filter> <filter-name>CAS Authentication Filter</filter-name> <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class> <!-- <filter-class>org.jasig.cas.client.authentication.Saml11AuthenticationFilter</filter-class> --> <!-- <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class> --> <init-param> <param-name>casServerLoginUrl</param-name> <!-- <param-value>https://sso.hoau.com:8433/cas/login</param-value> --> <param-value>https://proxy.sso.hoau.com:443/cas/login</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>https://app1.hoau.com:8413</param-value> </init-param> </filter> <filter> <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name> <filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class> </filter> <filter-mapping> <filter-name>CAS Validation Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>CAS Authentication Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- <servlet> <servlet-name>ProxyValidate</servlet-name> <servlet-class> edu.yale.its.tp.cas.servlet.ProxyValidate</servlet-class> </servlet> <servlet> <servlet-name>ProxyTicketReceptor</servlet-name> <servlet-class>edu.yale.its.tp.cas.proxy.ProxyTicketReceptor</servlet-class> </servlet > <servlet-mapping> <servlet-name>ProxyTicketReceptor</servlet-name> <url-pattern>/CasProxyServlet </url-pattern> </servlet-mapping > --> <welcome-file-list> <welcome-file> index.jsp </welcome-file> </welcome-file-list> </web-app>
*注意:
- “CAS Validation Filter”需要放在“CAS Authentication Filter”之前
此代理非彼代理
網上一些文章說的需要配置諸如:- “SingleSignOutHttpSessionListener”
- “SingleSignOutFilter”
- “ticketValidatorClass”
- “ProxyValidate”
- “ProxyTicketReceptor”
均不需要
如果誤配了SingleSignOutFilter,會出現異常
Caused by: java.io.IOException: Server returned HTTP response code: 500 for URL: https://proxy.sso.hoau.com:443/cas/proxyValidate?ticket=ST-31-TM9EbFoQbasNdXh11HaJ-cas01.sso.hoau.com&service=https%3A%2F%2Fapp2.hoau.com%3A8423%2Fcas2%3Bjsessionid%3D0CEB865B53E64FF31BF02A496DF73860.tomcat2
at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1840) at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1441) at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254) at org.jasig.cas.client.util.CommonUtils.getResponseFromServer(CommonUtils.java:429) ... 24 more
如果為了在https下錄製自動化測試指令碼,會修改classpath下/Library/Java/JavaVirtualMachines/jdk1.8.0_65.jdk/Contents/Home/jre/lib/security的java.security檔案,那麼也可能出現類似的錯誤。
-
web.xml配置同上
*注意:修改埠
CAS伺服器(Tomcat x 1)
服務端https://sso.hoau.com:8433
*注意:
系列文章CAS (1) —— Mac下配置CAS到Tomcat(服務端)中介紹服務端配置也無需任何修改。
以下配置:
- “Proxy (PGT acquisition)”
- “Modern proxy-service validation”
均不需要
測試
*1. 訪問“https://app1.hoau.com:8413/cas1”
會重定向到“https://proxy.sso.hoau.com/cas/login?service=https%3A%2F%2Fapp1.hoau.com%3A8413%2Fcas1”
*2. 然後輸入使用者明密碼(test01/psw01)
如果驗證成功,則會將瀏覽器重定向到app1的登陸成功頁面。
*3. 再次訪問“https://app1.hoau.com:8413/cas1”
可以直接進入登陸成功頁,而無需輸入使用者名稱密碼。
*4. 訪問另一應用
同樣可以通過test01使用者直接進入登陸成功頁,而無需輸入使用者名稱密碼。
代理下的網路順序分析
參照另一文章:(待出)
原始碼
還在準備,如果需要的童鞋請留言。