這篇文章對(duì)CAS單點(diǎn)登錄具體實(shí)現(xiàn)的一些步驟就行講述,至于CAS單點(diǎn)登錄的實(shí)現(xiàn)原理分析,請(qǐng)參看下面這篇文章:
CAS單點(diǎn)登錄原理分析(一) https://blog.csdn.net/qq_41258204/article/details/84036875
CAS 包含兩個(gè)部分:?CAS Server?和?CAS Client?。
CAS Server?:其實(shí)就是一個(gè)war包,CAS框架已經(jīng)提供。只需要把部署到web服務(wù)器上即可,主要負(fù)責(zé)對(duì)用戶的認(rèn)證工作。 在文章末尾的示例項(xiàng)目中提供。
CAS Client:就是開發(fā)過程中的web層, 負(fù)責(zé)處理對(duì)客戶端受保護(hù)資源的訪問請(qǐng)求,需要登錄時(shí),重定向到 CAS Server。不需要對(duì)這個(gè)部分進(jìn)行過多編碼,進(jìn)行簡單配置即可。
一,CAS 服務(wù)端部署
本次使用的CAS服務(wù)端版本是cas-server-4.0.0-release,
1.將cas-server-4.0.0-release\cas-server-4.0.0\modules文件夾下cas-server-webapp-4.0.0.war文件放入 tomcat 目錄下的 webapps 下,文件改名為cas.war,為了訪問時(shí)方便。
2.啟動(dòng)tomcat ,tomcat將自動(dòng)解壓 war 包。訪問tomcat下這個(gè)項(xiàng)目,就能看到它的登錄頁面。
其實(shí)訪問的是服務(wù)端的首頁index.jsp,觀察上面的地址欄,發(fā)現(xiàn)是對(duì)請(qǐng)求地址進(jìn)行了重寫,跳轉(zhuǎn)到了登錄頁面。有些小伙伴奇怪這個(gè)是怎么做到的,通過查看服務(wù)端的index.jsp會(huì)發(fā)現(xiàn),這個(gè)一點(diǎn)也不神奇。
注意:CAS Server服務(wù)端的登錄界面是可以進(jìn)行改動(dòng)的,不然項(xiàng)目上線后,用戶的登錄體驗(yàn)忒差了點(diǎn)。這個(gè)不用擔(dān)心!
3.用戶名和密碼配置
在\apache-tomcat-cas\webapps\cas\WEB-INF目錄下的deployerConfigContext.xml配置
也可以連接數(shù)據(jù)庫查詢用戶名和密碼,這里先寫死。
修改配置,重啟tomcat服務(wù)器,輸入用戶名,密碼,看到success頁面
4.服務(wù)端訪問端口修改
不想使用8080 端口訪問 CAS Server服務(wù)端, 可以修改訪問端口
4.1首先修改tomcat的訪問端口
修改\apache-tomcat-cas\conf目錄的server.xml文件

80為http協(xié)議默認(rèn)端口,下次再訪問tomcat就不用加端口號(hào),修改其它端口也可以。
4.2修改 CAS 配置文件
修改 cas 的 WEB-INF/cas.properties
server.name=http://localhost:80
修改cas項(xiàng)目的訪問路徑(可選)
修改apache-tomcat-cas\conf目錄下的server.xml文件, 添加如下配置
<Context path="" docBase="cas" reloadable="true"/>

完成上述修改,重啟tomcat服務(wù),輸入localhost就可以訪問到登錄頁面
6.本地域名解析(可選)
如果覺得輸入localhost覺得別扭,還可以進(jìn)行域名解析配置,不過這個(gè)域名只能在自己電腦上使用。
6.1修改C:\Windows\System32\drivers\etc目錄下的hosts文件
快設(shè)置一個(gè)自己喜歡的域名吧,記得用管理員身份編輯哦!
6.2不過我更習(xí)慣用下面這種方式配置本地域名解析
注意:這款軟件要以管理員身份運(yùn)行!
這款軟件,在文章末的實(shí)例項(xiàng)目中提供。再次訪問CAS Server服務(wù)端,一切正常。
7.去除CAS的 https 認(rèn)證

使用了https協(xié)議的網(wǎng)站
網(wǎng)站的地址欄前面會(huì)有一個(gè)小鎖,使用https也是為了網(wǎng)站更加安全。
CAS 默認(rèn)使用的是 HTTPS 協(xié)議,如果使用 HTTPS 協(xié)議需要 SSL 安全證書(需向特定的機(jī)構(gòu)申請(qǐng)和購買) ,在開發(fā)測(cè)試階段可以先使用http協(xié)議。
7.1修改 cas 的 WEB-INF/deployerConfigContext.xml
找到如下配置

添加一個(gè)屬性設(shè)置p:requireSecure="false"。默認(rèn)為true,需要安全驗(yàn)證,使用的是https協(xié)議。
bean id="proxyAuthenticationHandler"
? ? ? ? ? class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler"
? ? ? ? ? p:httpClient-ref="httpClient"? p:requireSecure="false"/>
7.2修改 cas 的/WEB-INF/spring-configuration/ticketGrantingTicketCookieGenerator.xml
p:cookieSecure="false"
p:cookieMaxAge="3600"
p:cookieName="CASTGC"
p:cookiePath="/cas"
7.3修改 cas 的 WEB-INF/spring-configuration/warnCookieGenerator.xml

找到上述配置,修改如下
p:cookieSecure="false"
p:cookieMaxAge="3600"
p:cookieName="CASPRIVACY"
p:cookiePath="/cas"
修改完配置,重啟tomcat,訪問CAS Server服務(wù),一切正常。關(guān)閉瀏覽器后,再次打開瀏覽器訪問,直接顯示已經(jīng)登錄。說明cookie有效時(shí)間設(shè)置成功,會(huì)話cookie設(shè)置成了持久性cookie。
8.CAS 修改服務(wù)端登錄頁面
8.1將準(zhǔn)備的登陸頁面login.html 拷貝到 cas 的 WEB-INF\view\jsp\default\ui 目錄下
8.2將原來的 casLoginView.jsp 改名,將 login.html 改名為 casLoginView.jsp
8.3將準(zhǔn)備的login.html需要的 css js img 文件夾拷貝到 cas 目錄下
8.4修改準(zhǔn)備的登錄頁面casLoginView.jsp
對(duì)照原來的登錄頁面進(jìn)行修改
打開原來的登錄頁面,里面引入了top.jsp頁面
在cas的WEB-INF\view\jsp\default\ui\includes目錄下找到top.jsp頁面
<%@ page pageEncoding="UTF-8" %>
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
修改完成后,重新訪問CAS Server服務(wù)端,看到頁面修改成功
到這表面看起來都已經(jīng)修改完成,其實(shí)真正的內(nèi)容還沒有修改。form表單,輸入框和登錄按鈕,還需要進(jìn)一步修改。
8.5修改form表單
打開原來的登錄頁面,找到如下部分


<form:form method="post" id="fm1" commandName="${commandName}" htmlEscape="true">
<form:errors path="*" id="msg" cssClass="errors" element="div" htmlEscape="false" />
</form:form>
將上面的form標(biāo)簽復(fù)制到準(zhǔn)備的登錄頁面上,將準(zhǔn)備的登錄頁面的form標(biāo)簽刪除
8.6修改用戶名輸入框
打開原來的登錄頁面,找到如下部分
<form:input cssClass="required" cssErrorClass="error" id="username" size="25" tabindex="1"? accesskey="${userNameAccessKey}" path="username" autocomplete="off" htmlEscape="true" />
將上面的input標(biāo)簽復(fù)制到準(zhǔn)備的登錄頁面上,刪除上面標(biāo)簽中的cssClass,cssErrorClass樣式,換成準(zhǔn)備的登錄頁面上用戶名輸入框的樣式
<form:input class="text" style="color: #FFFFFF !important"? placeholder="請(qǐng)輸入賬戶"? id="username" size="25" tabindex="1"? accesskey="${userNameAccessKey}" path="username" autocomplete="off"? htmlEscape="true" />
8.7修改密碼輸入框
打開原來的登錄頁面,找到如下部分
? <form:password cssClass="required" cssErrorClass="error" id="password" size="25" tabindex="2" path="password"? accesskey="${passwordAccessKey}" htmlEscape="true" autocomplete="off" />
將上面的password標(biāo)簽復(fù)制到準(zhǔn)備的登錄頁面上,刪除上面標(biāo)簽中的cssClass,cssErrorClass樣式,換成準(zhǔn)備的登錄頁面上密碼輸入框的樣式
<form:password? class="text" style="color: #FFFFFF !important; position:absolute; z-index:100;" placeholder="請(qǐng)輸入密碼" id="password" size="25" tabindex="2" path="password"? accesskey="${passwordAccessKey}" htmlEscape="true" autocomplete="off" />
8.8 修改登錄按鈕
打開原來的登錄頁面,找到如下部分
<input type="hidden" name="lt" value="${loginTicket}" />
? ? ? <input type="hidden" name="execution" value="${flowExecutionKey}" />
? ? ? <input type="hidden" name="_eventId" value="submit" />
? ? ? <input class="btn-submit" name="submit" accesskey="l" value="<spring:message code="screen.welcome.button.login" />" tabindex="4" type="submit" />
將上面的內(nèi)容復(fù)制到準(zhǔn)備的登錄頁面上,刪除上面標(biāo)簽中的 class=“btn-submit”,<spring:message code=“screen.welcome.button.login” />,換成準(zhǔn)備的登錄頁面上按鈕的樣式
<a class="act-but submit" href="javascript:document.getElementById('fm1').submit()" style="color: #FFFFFF" name="submit" accesskey="l" value="登錄" tabindex="4"? />登錄</a>
訪問修改好的登錄頁面,當(dāng)輸入用戶名或密碼錯(cuò)誤,給出的提示信息不是很友好
8.9 修改錯(cuò)誤提示
上面的英文錯(cuò)誤提示信息是在cas的 WEB-INF\classes 目錄下的 messages.properties 文件中
authenticationFailure.AccountNotFoundException=Invalid credentials.
authenticationFailure.FailedLoginException=Invalid credentials.
第一個(gè)是用戶名不存在時(shí)的錯(cuò)誤提示
第二個(gè)是密碼錯(cuò)誤的提示
將上面的兩行內(nèi)容復(fù)制到 messages_zh_CN.properties 文件中,這個(gè)文件主要是配置一些中文信息的,這個(gè)里面里面的內(nèi)容是進(jìn)行轉(zhuǎn)碼提示的
這個(gè)跟properties屬性文件很類似,所以可以借助properties文件來完成中文提示內(nèi)容的寫入

將上述內(nèi)容替換掉Invalid credentials英文提示
authenticationFailure.AccountNotFoundException=\u7528\u6237\u540D\u6216\u5BC6\u7801\u9519\u8BEF.
authenticationFailure.FailedLoginException=\u7528\u6237\u540D\u6216\u5BC6\u7801\u9519\u8BEF.
設(shè)置國際化為 zn_CN ,修改cas的WEB-INF目錄下 cas-servlet.xml,優(yōu)先使用配置中文提示信息的messages_zh_CN.properties 文件
搜索en關(guān)鍵字,快速找到這行信息,默認(rèn)使用的是英文國際化

修改為:
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver"
p:defaultLocale="zh_CN" />
修改完配置后,重啟tomcat,再次訪問CAS Server服務(wù)端
建議每修改一步就刷新頁面進(jìn)行查看,防止出錯(cuò)!
9.CAS server 自定義認(rèn)證方式
9.1打開cas服務(wù)端WEB-INF目錄下的deployerConfigContext.xml文件 ,找到如下配置
以上就是cas默認(rèn)的認(rèn)證方式,把用戶名和密碼寫死在配置文件中。下面自定義認(rèn)證方式,通過數(shù)據(jù)庫中的用戶信息,來認(rèn)證登錄的用戶。
9.2自定義認(rèn)證
主要配置:
數(shù)據(jù)源dataSource,從數(shù)據(jù)庫中查詢用戶信息
密碼加密方式passwordEncoder,可選配置,可以自定義加密方式
認(rèn)證方式?dbAuthHandler,主要引用數(shù)據(jù)源,查詢sql和密碼加密方式都可以自定義
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
p:driverClass="com.mysql.jdbc.Driver"
p:jdbcUrl="jdbc:mysql://127.0.0.1:3306/cas?characterEncoding=utf8"
p:user="root"
p:password="root" />
<bean id="passwordEncoder"
class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder"
c:encodingAlgorithm="MD5"
p:characterEncoding="UTF-8" />
<bean id="dbAuthHandler"
class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler"
p:dataSource-ref="dataSource"
p:sql="select password from t_user where username = ?"
p:passwordEncoder-ref="passwordEncoder"/>
把以上三個(gè)配置復(fù)制到deployerConfigContext.xml文件中最后,修改認(rèn)證方式為自定義認(rèn)證方式
<entry key-ref="dbAuthHandler" value-ref="primaryPrincipalResolver" />
9.3導(dǎo)入相關(guān)jar包
由于自定義認(rèn)證方式使用數(shù)據(jù)庫作為數(shù)據(jù)源,需要在cas\WEB-INF\lib 目錄下導(dǎo)入以下jar包

修改完成后,重啟tomcat,使用數(shù)據(jù)庫中的數(shù)據(jù)進(jìn)行測(cè)試

二, CAS 客戶端配置
1.創(chuàng)建Maven工程(war) cas_shoppingclient,引入CAS客戶端相關(guān)依賴,設(shè)置tomcat的訪問端口8081
<dependencies>
? <!-- CAS客戶端 -->
? <dependency>?
? ? <groupId>org.jasig.cas.client</groupId>?
? ? <artifactId>cas-client-core</artifactId>?
? ? <version>3.3.3</version>?
</dependency>?
<!-- servlet -->
? <dependency>
? <groupId>javax.servlet</groupId>
? <artifactId>javax.servlet-api</artifactId>
? <version>3.1.0</version>
? <scope>provided</scope>
? </dependency>
? </dependencies>
? <build>
? <plugins>
? <!-- tomcat插件 -->
? <plugin>
? <groupId>org.apache.tomcat.maven</groupId>
? <artifactId>tomcat7-maven-plugin</artifactId>
? <version>2.2</version>
? <configuration>
? <path>/</path>
? <port>8081</port>
? </configuration>
? </plugin>
? <!-- 設(shè)置jdk版本 -->
? <plugin>
? <groupId>org.apache.maven.plugins</groupId>
? <artifactId>maven-compiler-plugin</artifactId>
? <version>3.5.1</version>
? <configuration>
? <source>1.7</source>
? <target>1.7</target>
? <encoding>UTF-8</encoding>
? </configuration>
? </plugin>
? </plugins>
? </build>
在webapp目錄下創(chuàng)建WEB-INF文件夾,添加web.xml文件
web.xml文件主要配置:
單點(diǎn)登出過濾器SingleSignOutFilter:執(zhí)行用戶退出時(shí)的操作(可選)
認(rèn)證過濾器AuthenticationFilter:負(fù)責(zé)用戶認(rèn)證(必須)
ticket驗(yàn)證過濾器Cas20ProxyReceivingTicketValidationFilter:負(fù)責(zé)檢驗(yàn)ticket(必須)
獲取用戶登錄名過濾器
HttpServletRequestWrapperFilter(可選)和AssertionThreadLocalFilter
(可選)
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
? <listener>
? ? <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
? </listener>
? <!-- 該過濾器用于實(shí)現(xiàn)單點(diǎn)登出功能,可選配置。 -->
? <filter>
? ? <filter-name>CAS Single Sign Out Filter</filter-name>
? ? <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>
? </filter>
? <filter-mapping>
? ? <filter-name>CAS Single Sign Out Filter</filter-name>
? ? <url-pattern>/*</url-pattern>
? </filter-mapping>
? <!-- 該過濾器負(fù)責(zé)用戶的認(rèn)證工作,必須啟用它 -->
? <filter>
? ? <filter-name>CASFilter</filter-name>
? ? <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
? ? <init-param>
? ? ? <param-name>casServerLoginUrl</param-name>
? ? ? <!-- CAS服務(wù)端如果訪問端口配置為80,訪問路徑配置path="" 下面地址可以改成http://cas.xiaogui.com -->
? ? ? <param-value>http://cas.xiaogui.com:80/cas</param-value>
? ? </init-param>
? ? <init-param>
? ? ? <param-name>serverName</param-name>
? ? ? <!-- 客戶端地址,用于認(rèn)證成功后,跳轉(zhuǎn)回客戶端 -->
? ? ? <param-value>http://shopping.xiaogui.com:8081</param-value>
? ? </init-param>
? </filter>
? <filter-mapping>
? ? <filter-name>CASFilter</filter-name>
? ? <url-pattern>/*</url-pattern>
? </filter-mapping>
? <!-- 該過濾器負(fù)責(zé)對(duì) Ticket 的校驗(yàn)工作,必須啟用它 -->
? <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>
? ? ? <!-- CAS服務(wù)端如果訪問端口配置為80,訪問路徑配置path="" 下面地址可以改成http://cas.xiaogui.com -->
? ? ? <param-value>http://cas.xiaogui.com:80/cas</param-value>
? ? </init-param>
? ? <init-param>
? ? ? <param-name>serverName</param-name>
? ? ? <!-- 客戶端地址,用于認(rèn)證成功后,跳轉(zhuǎn)回客戶端 -->
? ? ? <param-value>http://shopping.xiaogui.com:8081</param-value>
? ? </init-param>
? </filter>
? <filter-mapping>
? ? <filter-name>CAS Validation Filter</filter-name>
? ? <url-pattern>/*</url-pattern>
? </filter-mapping>
? <!-- 該過濾器負(fù)責(zé)實(shí)現(xiàn) HttpServletRequest 請(qǐng)求的包裹, 比如允許開發(fā)者通過
HttpServletRequest 的 getRemoteUser()方法獲得 SSO 登錄用戶的登錄名,可選配置。 -->
? <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 HttpServletRequest Wrapper Filter</filter-name>
? ? <url-pattern>/*</url-pattern>
? </filter-mapping>
? <!-- 該過濾器使得開發(fā)者可以通過 org.jasig.cas.client.util.AssertionHolder 來獲取用戶
的登錄名。 比如 AssertionHolder.getAssertion().getPrincipal().getName()。 -->
? <filter>
? ? <filter-name>CAS Assertion Thread Local Filter</filter-name>
? ? <filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class>
? </filter>
? <filter-mapping>
? ? <filter-name>CAS Assertion Thread Local Filter</filter-name>
? ? <url-pattern>/*</url-pattern>
? </filter-mapping>
</web-app>
注意:配置CAS Server服務(wù)端地址和CAS Client客戶端地址一定要對(duì)應(yīng)
3.編寫index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
? ? pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>購物車</title>
</head>
<body>
<h1>歡迎訪問購物車系統(tǒng),當(dāng)前的用戶名:<%=request.getRemoteUser() %></h1>
</body>
</html>
request.getRemoteUser()為獲取遠(yuǎn)程登錄名
4.創(chuàng)建Maven工程(war) cas_payclient客戶端,參照cas_shoppingclient客戶端進(jìn)行配置
5.創(chuàng)建index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
? ? pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>商品結(jié)算</title>
</head>
<body>
<h1>歡迎訪問商品結(jié)算系統(tǒng),當(dāng)前的用戶名:<%=request.getRemoteUser() %></h1>
</body>
</html>
6.單點(diǎn)登錄測(cè)試
啟動(dòng)cas服務(wù)端
啟動(dòng)客戶端cas_shoppingclient和客戶端cas_payclient
訪問cas_shoppingclient客戶端
輸入帳號(hào),密碼登錄
訪問cas_payclient客戶端
直接登錄成功!
7.cas單點(diǎn)退出登錄到指定頁面
地址欄輸入?http://cas.xiaogui.com/cas/logout
即可看到退出后的提示頁面
自定義退出登錄跳轉(zhuǎn)地址
找到如cas服務(wù)端 WEB-INF目錄的配置文件 cas-servlet.xml如下配置

p:followServiceRedirects="${cas.logout.followServiceRedirects:true}
在cas_shoppingclient客戶端index.jsp頁面上添加一個(gè)退出鏈接
<a >退出登錄</a>

分享示例項(xiàng)目在碼云上的地址:https://gitee.com/xiaoguixiaogege/SSO_CAS