一 什么是单点登录
简单点说 单点登录的英文名称为Single Sign-On,简写为SSO,它是一个用户认证的过程,允许用户一次性进行认证之后,就访问系统中不同的应用;而不需要访问每个应用时,都重新输入密码。
二 认证流程
三. cas-overlay-template安装环境
四.下载项目并编译
git clone https://github.com/apereo/cas-overlay-template.git
执行./gradlew.bat clean build 第一次构建比较慢,耐心等待
./gradlew.bat explodeWar 解压
此时将会在bulid目录下生成一个cas-resources 文件夹,我们把里面的文件全部拷贝到src/main/resources,将\etc\cas\thekeystore 也拷贝到该目录下
更改配置application.properties server.ssl.key-store=classpath:thekeystore
./gradlew.bat run 将cas运行在内嵌的 Embedded Tomcat 中
启动完成后浏览器中打开 https://localhost:8443/cas/login ,
此时我们的cas已经部署成功 在登录也面输入用户名和密码 casuser Mellon ,出现下面界面表明cas已经部署成功
五.客户端应用接入CAS 服务
github 上有个简单的demo我们下载下来 https://github.com/casinthecloud/java-jasig-cas-client-demo.git
有一点错误,修改一下,加入如下依赖
javax.servlet javax.servlet-api 3.0.1 provided
接入cas 主要需要配置三个filter即可,下面是完整的web.xml配置
Demo webapp protected by the Java Jasig / Apereo CAS client
CAS Single Sign Out Filter
org.jasig.cas.client.session.SingleSignOutFilter
casServerUrlPrefix
http://localhost:8080/cas
CAS Single Sign Out Filter
/*
org.jasig.cas.client.session.SingleSignOutHttpSessionListener
CAS Authentication Filter
org.jasig.cas.client.authentication.AuthenticationFilter
casServerLoginUrl
https://localhost:8443/cas/login
serverName
http://localhost:8081
method
redirect
CAS Authentication Filter
/protected/*
CAS Validation Filter
org.jasig.cas.client.validation.Cas30ProxyReceivingTicketValidationFilter
casServerUrlPrefix
https://localhost:8443/cas
serverName
http://localhost:8081
CAS Validation Filter
*
CAS HttpServletRequest Wrapper Filter
org.jasig.cas.client.util.HttpServletRequestWrapperFilter
CAS HttpServletRequest Wrapper Filter
*
NOW让我们部署一下该项目,逐一端口为8081,启动服务后打开http://localhost:8081,激动人心的时刻到了。。。。
发现在访问受保护的页面时 出现如下问题:
未认证授权的服务
不允许使用CAS来认证您访问的目标应用。
什么问题呢,看起来是CAS服务不认识我们的应用。是的,我们并没有在CAS中注册该应用,那接下来注册一下应用
Cas 6 提供了已json格式的文件来完成客户端应用的注册
现在 我们将 /etc/cas/services目录也拷贝到 src\main\resources\下,简单做一下修改即可
{ "@class": "org.apereo.cas.services.RegexRegisteredService", "serviceId": "^(http|https|imaps)://.*", "name": "HTTPS and IMAPS", "id": 10000001, "description": "This service definition authorizes all application urls that support HTTPS and IMAPS protocols.", "evaluationOrder": 10000 }
在原有的https和imaps基础上增加了http应用,我们还需要再application.properties中配置注册文件的位置
cas.serviceRegistry.initFromJson=false cas.serviceRegistry.json.location=classpath:/services
并且需要在build.gradle中加入依赖包
dependencies {
// Other CAS dependencies/modules may be listed here...
compile "org.apereo.cas:cas-server-support-json-service-registry:${casServerVersion}"
}
好了一切就绪,重新编译,重启服务,再次访问我们看到如下界面
ok,我们输入静态认证用户casuser和密码Mellon 登录 然而登录后发现:
HTTP Status 500 – Internal Server Error
Type Exception Report
Message javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
Description The server encountered an unexpected condition that prevented it from fulfilling the request.
Exception
java.lang.RuntimeException: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
org.jasig.cas.client.util.CommonUtils.getResponseFromServer(CommonUtils.java:440)
org.jasig.cas.client.validation.AbstractCasProtocolUrlBasedTicketValidator.retrieveResponseFromServer(AbstractCasProtocolUrlBasedTicketValidator.java:42)
org.jasig.cas.client.validation.AbstractUrlBasedTicketValidator.validate(AbstractUrlBasedTicketValidator.java:191)
org.jasig.cas.client.validation.AbstractTicketValidationFilter.doFilter(AbstractTicketValidationFilter.java:207)
org.jasig.cas.client.authentication.AuthenticationFilter.doFilter(AuthenticationFilter.java:191)
org.jasig.cas.client.session.SingleSignOutFilter.doFilter(SingleSignOutFilter.java:94)
Root Cause
javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
java.base/sun.security.ssl.Alert.createSSLException(Alert.java:128)
java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:321)
java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:264)
java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:259)
java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.checkServerCerts(CertificateMessage.java:1329)
java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.onConsumeCertificate(CertificateMessage.java:1204)
java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.consume(CertificateMessage.java:1151)
java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392)
java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:444)
java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:421)
java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:178)
java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:164)
java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1152)
java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1063)
java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:402)
java.base/sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:567)
java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1581)
java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1509)
java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:245)
org.jasig.cas.client.util.CommonUtils.getResponseFromServer(CommonUtils.java:424)
org.jasig.cas.client.validation.AbstractCasProtocolUrlBasedTicketValidator.retrieveResponseFromServer(AbstractCasProtocolUrlBasedTicketValidator.java:42)
org.jasig.cas.client.validation.AbstractUrlBasedTicketValidator.validate(AbstractUrlBasedTicketValidator.java:191)
org.jasig.cas.client.validation.AbstractTicketValidationFilter.doFilter(AbstractTicketValidationFilter.java:207)
org.jasig.cas.client.authentication.AuthenticationFilter.doFilter(AuthenticationFilter.java:191)
org.jasig.cas.client.session.SingleSignOutFilter.doFilter(SingleSignOutFilter.java:94)
Root Cause
sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
java.base/sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:385)
java.base/sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:290)
java.base/sun.security.validator.Validator.validate(Validator.java:264)
java.base/sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:321)
java.base/sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:221)
java.base/sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:129)
java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.checkServerCerts(CertificateMessage.java:1313)
java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.onConsumeCertificate(CertificateMessage.java:1204)
java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.consume(CertificateMessage.java:1151)
java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392)
java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:444)
java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:421)
java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:178)
java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:164)
java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1152)
java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1063)
java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:402)
java.base/sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:567)
java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1581)
java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1509)
java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:245)
org.jasig.cas.client.util.CommonUtils.getResponseFromServer(CommonUtils.java:424)
org.jasig.cas.client.validation.AbstractCasProtocolUrlBasedTicketValidator.retrieveResponseFromServer(AbstractCasProtocolUrlBasedTicketValidator.java:42)
org.jasig.cas.client.validation.AbstractUrlBasedTicketValidator.validate(AbstractUrlBasedTicketValidator.java:191)
org.jasig.cas.client.validation.AbstractTicketValidationFilter.doFilter(AbstractTicketValidationFilter.java:207)
org.jasig.cas.client.authentication.AuthenticationFilter.doFilter(AuthenticationFilter.java:191)
org.jasig.cas.client.session.SingleSignOutFilter.doFilter(SingleSignOutFilter.java:94)
Root Cause
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
java.base/sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141)
java.base/sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126)
java.base/java.security.cert.CertPathBuilder.build(CertPathBuilder.java:297)
java.base/sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:380)
java.base/sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:290)
java.base/sun.security.validator.Validator.validate(Validator.java:264)
java.base/sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:321)
java.base/sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:221)
java.base/sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:129)
java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.checkServerCerts(CertificateMessage.java:1313)
java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.onConsumeCertificate(CertificateMessage.java:1204)
java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.consume(CertificateMessage.java:1151)
java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392)
java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:444)
java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:421)
java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:178)
java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:164)
java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1152)
java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1063)
java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:402)
java.base/sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:567)
java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1581)
java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1509)
java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:245)
org.jasig.cas.client.util.CommonUtils.getResponseFromServer(CommonUtils.java:424)
org.jasig.cas.client.validation.AbstractCasProtocolUrlBasedTicketValidator.retrieveResponseFromServer(AbstractCasProtocolUrlBasedTicketValidator.java:42)
org.jasig.cas.client.validation.AbstractUrlBasedTicketValidator.validate(AbstractUrlBasedTicketValidator.java:191)
org.jasig.cas.client.validation.AbstractTicketValidationFilter.doFilter(AbstractTicketValidationFilter.java:207)
org.jasig.cas.client.authentication.AuthenticationFilter.doFilter(AuthenticationFilter.java:191)
org.jasig.cas.client.session.SingleSignOutFilter.doFilter(SingleSignOutFilter.java:94)
Note The full stack trace of the root cause is available in the server logs.
这特莫是什么鬼,仔细看看应该是证书的问题了,啥意思呢。 就是CAS客户端不信任CAS服务器提供的证书。我们使用keytool工具生成证书并导入jdk信任库中,
一切就绪,重新启动项目并登录
六.部署在外部的Tomcat中
在tomcat的server.xml中配置ssl
port="8443"
maxThreads="150"
SSLEnabled="true">
certificateKeystorePassword="changeit"
type="RSA"/>
另外根据文档我们还需添加下面的依赖
dependencies {
// Other CAS dependencies/modules may be listed here...
compile "org.apereo.cas:cas-server-webapp:${casServerVersion}"
}
将bulid/lib中的cas.war 拷贝到tomcat的webapp中,启动即可正常访问
七.参考资料
https://apereo.github.io/cas/6.1.x/installation/WAR-Overlay-Installation.html
https://github.com/apereo/cas-overlay-template