前提描述:通过本次了解cas是个什么东西,并使用它。
cas为oss(单点登录)的一种实现方案。要实现cas单点登录,首先需要部署cas的server服务。
CAS是Central Authentication Service的缩写,中央认证服务,。
cas-server-webapp-tomcat是cas的一个demo,github下载地址:https://repo1.maven.org/maven2/org/apereo/cas/cas-server-webapp-tomcat/5.3.14/
1.采用JDK自带的keytool工具生成秘钥库,别名casbm,生成的秘钥存储路径为D:\cas\keystore
2.打开cmd窗口执行以下命令,命令如下:
keytool -genkey -v -alias casbm -keyalg RSA -keystore D:\cas\keystore\casbm.keystore
秘钥口令随便输入,我这里输入123456,后面要用到
其他随便输入
最后确认输入y
4.将秘钥导出为证书
keytool -export -trustcacerts -alias casbm -file D:/cas/keystore/casbm.cer -keystore D:/cas/keystore/casbm.keystore
输入密码:为上面设置的 123456
5.将证书导入到JDK证书库
keytool -import -trustcacerts -alias casbm -file D:/cas/keystore/casbm.cer -keystore "D:/software/jdk1.8.0_162/jre/lib/security/cacerts"
①后面D:/software/jdk1.8.0_162/jre/lib/security/cacerts"本机jdk的证书保存路径
②设置的密码是固定的: changeit
③是否信任此证书,我们输入:y
下载:官网下载
由于cas需要https协议访问,所以我们要配置tomcat也支持https协议,我们找到我们的tomcat的server.xml文件,加入如下配置:
maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS" keystoreFile="D:\cas\keystore\casbm.keystore" keystorePass="123456"/>
1.我们将之前下载的cas包放入tomcat中启动,首先将war包放入tomcat的webapps下
为方便看,将名称 cas-server-webapp-tomcat-5.3.14.war 改为 cas.war
找到tomcat中bin下的startup.bat文件双击启动。
启动bin下的startup.bat,默认有中文乱码,可以我们来到tomcat目录的conf子目录中,找到一个名为 "logging.properties" 的文件,打开这个文本文件,找到如下配置项:
java.util.logging.ConsoleHandler.encoding = UTF-8
将 UTF-8 修改为 GBK,修改后的效果为:
java.util.logging.ConsoleHandler.encoding = GBK
保存后,重启tomcat!
1.访问地址:https://localhost:8443/cas ,用户名为:casuser 密码为:Mellon
2.用户名密码是在如下配置文件中指定的
D:\software\tomcat\apache-tomcat-8.5.68\webapps\cas\WEB-INF\classes\application.properties
3.配置用户名密码为数据库中的真实用户名密码
上面我们的用户名密码是写死的,但是实际开发中我们的用户名密码是存储在数据库中的,这个时候需要我们从数据库中读取,我们打开application.properties文件,添加如下配置
①修改配置
cas.authn.jdbc.query[0].url=jdbc:mysql://localhost:3306/cas?serverTimezone=GMT
cas.authn.jdbc.query[0].user=root
cas.authn.jdbc.query[0].password=root
cas.authn.jdbc.query[0].sql=select * from user where username = ?
cas.authn.jdbc.query[0].fieldPassword=password
cas.authn.jdbc.query[0].driverClass=com.mysql.jdbc.Driver
②将所需要的驱动jar包放入cas项目中
将所需要的jar包放入cas项目的lib中,jar包名称如下,可以去maven仓库中搜索
cas-server-support-jdbc-5.3.1.jar
cas-server-support-jdbc-drivers-5.3.1.jar
cas-server-support-jdbc-authentication-5.3.1.jar
connector-java-5.1.34_1.jar
④设置密码加密
可以使用数据库的用户名密码来登录cas了,但是明文的密码并不安全,现在我们需要配置一下加密。还是找到application.properties文件,添加如下代码
cas.authn.jdbc.query[0].passwordEncoder.type=DEFAULT
cas.authn.jdbc.query[0].passwordEncoder.characterEncoding=UTF-8
#MD5加密策略cas.authn.jdbc.query[0].passwordEncoder.encodingAlgorithm=MD5
测试:将123456加密,然后将密码改为加密后的,此时重启tomcat再用123456登录
4.配置CAS日志地址
D:\software\tomcat\apache-tomcat-8.5.68\webapps\cas\WEB-INF\classes\log4j2.xml
5.
创建过程略。。。
导包:
org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-autoconfigure org.springframework.boot spring-boot-starter-test test org.projectlombok lombok true mysql mysql-connector-java runtime org.jasig.cas.client cas-client-support-springboot ${cas.client.version}
server: port: 8881 spring: application: name: study-cas-demo1 cas: serverUrlPrefix: https://localhost:8443/cas serverLoginUrl: ${cas.server-url-prefix}/login clientHostUrl: http://localhost:${server.port}
server: port: 8882 spring: application: name: study-cas-demo2 cas: serverUrlPrefix: https://localhost:8443/cas serverLoginUrl: ${cas.server-url-prefix}/login clientHostUrl: http://localhost:${server.port}
启动类加注解: @EnableCasClient
cas-client1和cas-client2,访问http://localhost:8881,会跳转cas登录,然后在访问http://localhost:8882,会发现已经认证通过,不会跳转cas登录,达到了单点登录的效果。
解决办法:
①. 修改http支持的配置
打开文件:cas\WEB-INF\classes\services\HTTPSandIMAPS-10000001.json
{
"@class" : "org.apereo.cas.services.RegexRegisteredService",
"serviceId" : "^(https|imaps)://.*",
"name" : "HTTPS and IMAPS",
"id" : 10000001,
"description" : "This service definition authorizes all application urls that support HTTPS and IMAPS protocols.",
"evaluationOrder" : 10000
}
第三行,添加http请求支持
{
"@class" : "org.apereo.cas.services.RegexRegisteredService",
"serviceId" : "^(https|imaps|http)://.*",
"name" : "HTTPS and IMAPS",
"id" : 10000001,
"description" : "This service definition authorizes all application urls that support HTTPS and IMAPS protocols.",
"evaluationOrder" : 10000
}
②启用记载JSON配置文件
打开文件cas\WEB-INF\classes\application.properties
增加如下配置
cas.tgc.secure=false
cas.serviceRegistry.initFromJson=true
5.显示用户名
as客户端经过cas server认证后,cas客户端可以去cas server端拿到登录认证的信息,比如用户名。thymeleaf页面可以${session._const_cas_assertion_.principal.name} 这么取值;
具体我们通过源码找到Servlet3AuthenticationFilter
从cas server端获取认证信息后,设置到 _const_cas_assertion_ 里去,通过查看 principal.getAssertion() 可以发现有principal的name属性就是用户名;
demo地址:https://gitee.com/yemanrenyy/study-sso-cas.git
参考:CAS单点登录原理_cas单点登录实现原理_JasonWangQB的博客-CSDN博客
单点登录(Single Sign On):简称 SSO,在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统。比如,员工登录过OA系统后,可以直接访问邮件系统,而不再需要登录邮件系统。这里的OA系统和邮件系统,可以认为是相互信任的子系统,他们共用一套用户数据,这套用户数据的权限认证由统一的认证服务器来认证。
如上图所示,我们在浏览器(Browser)中访问一个应用,这个应用需要登录,我们填写完用户名和密码后,完成登录认证。这时,我们在这个用户的session中标记登录状态为yes(已登录),同时在浏览器(Browser)中写入Cookie,这个Cookie是这个用户的唯一标识。下次我们再访问这个应用的时候,请求中会带上这个Cookie,服务端会根据这个Cookie找到对应的session,通过session来判断这个用户是否登录。如果不做特殊配置,这个Cookie的名字叫做jsessionid,值在服务端(server)是唯一的。
一个企业一般情况下只有一个域名,通过二级域名区分不同的系统。比如我们有个域名叫做:a.com,同时有两个业务系统分别为:app1.a.com和app2.a.com。我们要做单点登录(SSO),需要一个登录系统,叫做:sso.a.com。
我们只要在sso.a.com登录,app1.a.com和app2.a.com就也登录了。通过上面的登陆认证机制,我们可以知道,在sso.a.com中登录了,其实是在sso.a.com的服务端的session中记录了登录状态,同时在浏览器端(Browser)的sso.a.com下写入了Cookie。那么我们怎么才能让app1.a.com和app2.a.com登录呢?这里有两个问题:
那么我们如何解决这两个问题呢?针对第一个问题,sso登录以后,可以将Cookie的域设置为顶域,即.a.com,这样所有子域的系统都可以访问到顶域的Cookie。我们在设置Cookie时,只能设置顶域和自己的域,不能设置其他的域。比如:我们不能在自己的系统中给baidu.com的域设置Cookie。
Cookie的问题解决了,我们再来看看session的问题。我们在sso系统登录了,这时再访问app1,Cookie也带到了app1的服务端(Server),app1的服务端怎么找到这个Cookie对应的Session呢?这里就要把3个系统的Session共享,如图所示。共享Session的解决方案有很多,例如:Spring-Session。这样第2个问题也解决了。
同域下的单点登录就实现了,但这还不是真正的单点登录。
同域下的单点登录是巧用了Cookie顶域的特性。如果是不同域呢?不同域之间Cookie是不共享的,怎么办?
这里我们就要说一说CAS流程了,这个流程是单点登录的标准流程。
上图是CAS官网上的标准流程,具体流程如下:
至此,跨域单点登录就完成了。以后我们再访问app系统时,app就是登录的。接下来,我们再看看访问app2系统时的流程。
这样,app2系统不需要走登录流程,就已经是登录了。SSO,app和app2在不同的域,它们之间的session不共享也是没问题的。
问题1:SSO系统登录后跳回原业务系统时,带了个参数ST,业务系统还要拿ST再次访问SSO进行验证,觉得这个步骤有点多余?
资源都在各个业务系统这边,不在SSO那一方。 用户在给SSO服务器提供了用户名密码后,作为业务系统并不知道这件事。 SSO随便给业务系统一个ST,那么业务系统是不能确定这个ST是用户伪造的,还是真的有效,所以要拿着这个ST去SSO服务器再问一下,这个用户给我的ST是否有效,是有效的我才能让这个用户访问。