心血來潮想給公司搭個單點(diǎn)登錄系統(tǒng)魏烫,畢竟內(nèi)網(wǎng)這么多系統(tǒng)怖亭,每個系統(tǒng)都一套賬號密碼,這樣好low挚瘟。CAS提供了一個統(tǒng)一認(rèn)證服務(wù)叹谁,所有系統(tǒng)直接向CAS去請求登陸認(rèn)證,而用戶只在CAS登陸一次就夠了乘盖,在大一點(diǎn)的公司里基本都有這種服務(wù)焰檩,叫SSO(Single sign-on)。
找了找開源的SSO订框,看到了CAS析苫,感覺挺屌的,一直在更新穿扳,就他了衩侥。然后就開始爬坑了,記錄一下矛物,免得日后忘了又爬一次茫死。
CAS-Overlay-Template
這個項(xiàng)目很大,還需要很多自定義配置履羞,所以不可能做成開箱即用峦萎。CAS-Overlay-Template 就是一個模板項(xiàng)目,clone下來忆首, 修改配置爱榔,然后是直接跑還是打包war或者打包docker都好說。
新版本的 CAS-Overlay-Template 已經(jīng)改為Gradle編譯糙及,我喜歡Gradle详幽。
clone下來后,直接 先運(yùn)行
./build.sh copy
再運(yùn)行
./build.sh run
就跑起來了。
編譯配置
跑起來個屁铐维。
默認(rèn)是CAS 6.0版本着帽,直接用Java11了,要跑6.0版本先去裝個jdk11雳灾,配好環(huán)境。我踩坑的時候6.0還是RC版本冯凹,發(fā)現(xiàn)有bug就還是滾回5.3.5谎亩,配置這些東西在gradle.properties
里
# Versions
cas.version=5.3.5 // 可以改成6.0.0-rc3
springBootVersion=2.1.0.RELEASE
appServer=-tomcat
gradleVersion=4.10.2
tomcatVersion=9
tomcatFullVersion=9.0.12
group=你的組織名
sourceCompatibility=8 // 6.0以上就改成11
targetCompatibility=8 // 6.0以上就改成11
# Location of the downloaded CAS shell JAR
shellDir=build/libs
# use without "-slim" in tag name if you want tools like jstack, adds about 100MB to image size
baseDockerImage=openjdk:8-slim // 6.0以上就改成11
同時,還有增加幾個依賴宇姚,因?yàn)镃AS完全是插件化設(shè)計
compile "org.apereo.cas:cas-server-support-jdbc:${casServerVersion}"
compile "org.apereo.cas:cas-server-support-jdbc-drivers:${casServerVersion}"
compile "org.apereo.cas:cas-server-support-pm:${casServerVersion}"
compile "org.apereo.cas:cas-server-support-pm-jdbc:${casServerVersion}"
然后下1個小時依賴就可以跑起來了匈庭,可喜可賀,可喜可賀浑劳。
配置目錄
這個目錄阱持,里面包含了所有的配置,基本上改這里面的東西就夠了魔熏。
這個配置目錄是如何生效的衷咽?
之所以先運(yùn)行 ./build.sh copy
是因?yàn)?/p>
function copy() {
echo -e "Creating configuration directory under /etc/cas"
mkdir -p /etc/cas/config
echo -e "Copying configuration files from etc/cas to /etc/cas"
cp -rfv ./etc/cas/* /etc/cas
}
他會把配置文件復(fù)制進(jìn)系統(tǒng)配置目錄鸽扁,然后 Springboot 啟動后會去讀這個系統(tǒng)配置目錄。
另外镶骗,注意一下 src/main/jib/docker/entrypoint.sh
里面是類似 Dockerfile
#!/bin/sh
#echo -e "\nChecking java..."
#java -version
#echo -e "\nCreating CAS configuration directories..."
mkdir -p /etc/cas/config
mkdir -p /etc/cas/services
#echo "Listing provided CAS docker artifacts..."
#ls -R docker/cas
#echo -e "\nMoving CAS configuration artifacts..."
mv docker/cas/thekeystore /etc/cas 2>/dev/null
mv docker/cas/config/*.* /etc/cas/config 2>/dev/null
mv docker/cas/services/*.* /etc/cas/services 2>/dev/null
#echo -e "\nListing CAS configuration under /etc/cas..."
#ls -R /etc/cas
echo -e "\nRunning CAS..."
exec java -Xms512m -Xmx2048M -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -jar docker/cas/war/sso.war
打包鏡像時桶现,也會直接把配置目錄復(fù)制進(jìn)鏡像的系統(tǒng)配置目錄
build.sh
常用操作 build.sh 都已經(jīng)寫好了,常用的比如
- ./build.sh copy 復(fù)制配置到系統(tǒng)目錄
- ./build.sh run 運(yùn)行CAS
- ./build.sh docker 打包鏡像
- ./build.sh debug Debug CAS
配置
然后幾乎所有的服務(wù)配置都在 cas/config/cas.properties
里
一點(diǎn)一點(diǎn)的補(bǔ)充吧鼎姊,把example.com
換成自己的域名骡和。
下面是 5.3.5的配置,5.2.x 的配置不一樣相寇,6.0.0也不一樣慰于,文檔沒寫,我也不寫??唤衫。
后文會介紹怎么去源碼看當(dāng)前版本的配置項(xiàng)
服務(wù)地址
cas.server.name=http://sso.example.com
cas.server.prefix=${cas.server.name} //要使用根Path訪問婆赠,比如 http://sso.example.com 就這樣。默認(rèn)值是`${cas.server.name}/cas`
server.context-path= //對战授,就是空页藻,這樣就可以使用根Path訪問了,默認(rèn)值是 /cas
server.port=443 // HTTPS的端口,默認(rèn)8443
cas.server.http.enabled=true // 開啟HTTP植兰,默認(rèn)只有HTTPS份帐,而且我只知道怎么開HTTP,不知道怎么關(guān)HTTPS
cas.server.http.port=80 // 設(shè)置HTTP端口楣导,默認(rèn)8080
上面這幾個配置挺操蛋的废境,我以為 cas.server.prefix
改了就可以 http://sso.example.com
直接訪問了,結(jié)果還是要加上/cas
才能訪問筒繁,這個配置是假的噩凹,只是用于各種重定向url填充用。server.context-path
才是真正的 Path毡咏。
數(shù)據(jù)庫認(rèn)證
cas.authn.accept.users= // 留空才會禁用 Static Authentication
cas.authn.jdbc.query[0].dialect=org.hibernate.dialect.MySQLDialect
cas.authn.jdbc.query[0].driverClass=com.mysql.jdbc.Driver
cas.authn.jdbc.query[0].url=jdbc:mysql://xxxxxxxxxxxxxxxx?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false
cas.authn.jdbc.query[0].user=xxxxxxx
cas.authn.jdbc.query[0].password=xxxxxxxxx
cas.authn.jdbc.query[0].sql=select * from user where name=?
// 這些配置懶得講了驮宴,網(wǎng)上大部分文章講的都沒問題
cas.authn.jdbc.query[0].fieldPassword=password
cas.authn.jdbc.query[0].fieldExpired=expired
cas.authn.jdbc.query[0].fieldDisabled=disabled
cas.authn.jdbc.query[0].passwordEncoder.type=DEFAULT
cas.authn.jdbc.query[0].passwordEncoder.characterEncoding=UTF-8
cas.authn.jdbc.query[0].passwordEncoder.encodingAlgorithm=MD5
配置數(shù)據(jù)庫認(rèn)證很簡單。去給數(shù)據(jù)庫建個表呕缭。填一下字段名和Sql堵泽,就可以了。然后跑一下試試吧
密碼管理-郵件
CAS支持密碼管理功能恢总,直接網(wǎng)頁上點(diǎn)忘記密碼迎罗,就給你郵箱發(fā)重置郵件,配置比較多片仿,坑也很多
# 發(fā)送郵件纹安,我用的騰訊企業(yè)郵
spring.mail.host=smtp.exmail.qq.com
spring.mail.port=465
spring.mail.username=xxxxxx@example.com
spring.mail.password=xxxxxxx
# 這是騰訊企業(yè)郵的配置,其他郵箱自己配,可能有些坑厢岂,但這個不管CAS的事
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.socketFactory.port=465
spring.mail.properties.mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory
spring.mail.properties.mail.smtp.socketFactory.fallback=false
spring.mail.properties.mail.smtp.ssl=true
密碼管理-數(shù)據(jù)庫
改改自己的SQL就好了
cas.authn.pm.enabled=true // 開啟修改密碼功能
cas.authn.pm.autoLogin=true // 修改密碼后自動登錄
cas.authn.pm.jdbc.autocommit=true // 加就是了
cas.authn.pm.jdbc.sqlFindEmail=SELECT email FROM user WHERE name=?
cas.authn.pm.jdbc.sqlChangePassword=UPDATE user SET password=? WHERE name=?
cas.authn.pm.jdbc.url=${cas.authn.jdbc.query[0].url}
cas.authn.pm.jdbc.user=${cas.authn.jdbc.query[0].user}
cas.authn.pm.jdbc.password=${cas.authn.jdbc.query[0].password}
cas.authn.pm.jdbc.dialect=${cas.authn.jdbc.query[0].dialect}
cas.authn.pm.jdbc.driverClass=${cas.authn.jdbc.query[0].driverClass}
# 密碼修改加密規(guī)則光督,這個必須要和原始密碼加密規(guī)則一致
cas.authn.pm.jdbc.passwordEncoder.type=${cas.authn.jdbc.query[0].passwordEncoder.type}
cas.authn.pm.jdbc.passwordEncoder.characterEncoding=${cas.authn.jdbc.query[0].passwordEncoder.characterEncoding}
cas.authn.pm.jdbc.passwordEncoder.encodingAlgorithm=${cas.authn.jdbc.query[0].passwordEncoder.encodingAlgorithm}
cas.authn.pm.jdbc.passwordEncoder.secret=${cas.authn.jdbc.query[0].passwordEncoder.secret}
密碼管理-業(yè)務(wù)配置
cas.authn.pm.reset.mail.from=${spring.mail.username}
cas.authn.pm.reset.mail.subject=[XXX公司 SSO系統(tǒng)] 重置密碼
# 郵件內(nèi)容,必須要有%s咪笑,因?yàn)闀梢粋€連接并且?guī)Я藅oken可帽,否則無法打開鏈接,當(dāng)然這個鏈接也和cas.server.prefix有關(guān)系
cas.authn.pm.reset.mail.text=[季諾科技 SSO]打開以下鏈接重置您的密碼窗怒,10分鐘后失效: %s
cas.authn.pm.reset.expirationMinutes=10 // 郵件有效期
cas.authn.pm.reset.securityQuestionsEnabled=false // 問題驗(yàn)證,我覺得這個操作很蠢蓄拣,就關(guān)了
# 8-32位扬虚,必須包含大小寫字母,數(shù)字球恤,字符辜昵。你可以填其他密碼檢驗(yàn)正則
cas.authn.pm.policyPattern=^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[$@$!%*?&])[A-Za-z\\d$@$!%*?&]{8,32}
神坑
路由轉(zhuǎn)發(fā)環(huán)境下的IP校驗(yàn)
到這里可能本地跑起來沒問題了,修改密碼都正常咽斧。一部署到線上就不能修改密碼堪置,從郵箱驗(yàn)證鏈接點(diǎn)過去,CAS每次都拒絕此次密碼修改請求张惹。為啥舀锨?
翻了大片的源碼,終于找到了原因宛逗。郵箱里收到的鏈接參數(shù)是加了密的坎匿,取消加密的話就很容易看出問題。
http://sso.example.com/login?pswdrst={"jti":"ad06632d-dcee-43e0-bf0f-639150912b96","iss":"http://sso.example.com","aud":"http://sso.example.com","exp":1542541827,"iat":1542541227,"origin":"17*.17*.0.11","client":"1*.1*.97.138","sub":"jude"}
token里包含了請求郵件驗(yàn)證時的客戶端IP雷激,也不知道為啥替蔬,Nginx轉(zhuǎn)發(fā)后,他拿到的IP就老是不對屎暇,點(diǎn)擊鏈接訪問時承桥,IP對不上他就不認(rèn)這次請求。
普通的直接部署的CAS系統(tǒng)根悼,不加這個沒問題凶异。但我們的內(nèi)網(wǎng)環(huán)境是有一臺Nginx路由,所有請求都會經(jīng)過他轉(zhuǎn)發(fā)番挺,就出問題唠帝。
差點(diǎn)去改源碼了,好在無聊翻源碼時找到玄柏,CAS原來留了一手來處理這個問題襟衰。
cas.audit.alternateClientAddrHeaderName=X-Forwarded-For
添加這個配置, CAS就會從Header里讀X-Forwarded-For
來確認(rèn)客戶端IP粪摘,這樣就不存在轉(zhuǎn)發(fā)后IP不對的問題了瀑晒。
6.0版本的密碼修改BUG
我也不清楚6.0版本加了找回用戶名這個積累功能后绍坝,為啥找回密碼頁面他老是發(fā)請求到找回用戶名的接口上去。導(dǎo)致找回密碼和找回用戶名2個功能都不能正常工作苔悦。源碼好多轩褐,對JSP無力,沒細(xì)查玖详,改回5.3.5把介,BUG消失,放棄6.0蟋座。
找配置文檔
我上面的配置有很多拗踢,官網(wǎng)文檔寫的很糟,又少又老還跳來跳去向臀,還每個版本都要改配置項(xiàng)(5.2.x, 5.3.x, 6.0.x都很多不一樣)巢墅。
網(wǎng)上大部分文檔都是5.2版本的祖?zhèn)髋渲茫婚_始真是惡心死我了券膀。
還是得自己去翻源碼君纫,先找到 CasServerProperties
這個類,他在
這個類就是配置的映射芹彬,還有少數(shù)注釋講了很多官方文檔沒說的操作蓄髓。
照著這個類來填配置就行了。
棄坑
好不容易終于跑起來這個坑B玩意了雀监,但十分不建議使用CAS双吆,CAS優(yōu)勢是開放,適合二次開發(fā)会前。
但十分難用......去用keycloak吧好乐。配置簡單,界面還好看瓦宜。