什么是单点登录?
先说一个需求场景,比如:一个企业的内部有N多个子系统,每个子系统都有一套自己的用户名和密码,那么企业的员工要登录N个子系统,这样一个员工 就要记住N个用户名和密码,就算各个子系统的用户名和密码都是统一的,登录每个子系统都要输入用户名和密码进行登录也是一个繁琐的操作过程,那么单点登录功能由此便应运而生了。
单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。
CAS架构图
这里直接说CAS整体架构是因为这不是一个入门教程,我已经默认你知道什么是SSO、什么是CAS,以及你已经装好了开发工具IDEA和Java环境Jdk1.8,并且你也知道什么是Web容器,知道Tomcat8.5+该从哪下载并启动部署,你也知道什么是Maven项目该怎么引入相关的jar包等等,所以如果你不具备以上这些知识,那么CAS服务端的学习我建议你还是先不要开始,弄清楚前面的知识点,才能畅通无阻的学习CAS服务端。
好了,这里先放架构图:
现在来讲解这个架构图:
首先搭建好CAS服务端后,服务端会自定义一个数据库和用户表,用户表中存放的是用户名和密码,通过访问子系统的URL地址,如果CAS系统判定你没有登录,就会将URL重定向到CAS的服务端登录界面,用户通过输入用户数据库的用户名和密码来进行登录,待登录成功后,CAS服务端会给CAS客户端(子系统)发送登录的用户名,CAS客户端接到用户名后,会从CAS客户端的用户表中寻找对应用户名的userid,并通过该userid获取到该名用户在CAS客户端的相关权限。
我们还可以基于CAS服务端配置的用户数据库做一个用户管理界面,该界面可以对CAS服务端的用户进行一些基本的业务操作,比如新增、修改、删除等,新增用户的时候,需要同时在各个子系统的用户表中插入该用户的相关信息,然后各个子系统的管理员就可以通过子系统的角色权限设置,给该用户配置相关的角色权限了。
安装CAS服务端
讲了这么多,现在开始进入正题,我们要实现我们的目的首先要做的就是安装CAS服务端,现在网上的很多Springboot整合CAS的教程基本上都是只讲客户端的配置,看完之后云里雾里的,所以我这个教程要从CAS的服务端开始讲起,如果你们公司有人专门搭建CAS服务端,那就可以跳过本篇教程继续下一篇了。
- 下载Overlay
通过阅读官网文档(https://apereo.github.io/cas/5.1.x/planning/Getting-Started.html)了解到官方建议我们:
通过使用一个名叫Overlay的项目来生成一个可以直接用的war包,来部署服务端,于是我们先下载这个项
目,我这里使用Maven的,下载地址:https://github.com/apereo/cas-overlay-template。 搭建Overlay项目
将项目下好后解压缩并放入我们的工作空间,然后打开IDEA,然后选择File–Open打开我们项目所在路径,然后我们只要静静的等待Maven将项目构筑好就可以了。这时你看到的项目是这个样子的:
这是我的项目,你的项目中并没有src目录和target目录,src目录是用户自己建立的,至于为什么后边会讲,target目录是导出war时才出现的,新导入的项目是没有的。
制成可用的CAS服务端
现在的CAS服务端基本上是不可用的,为什么说基本上呢?因为如果你现在讲项目打成war包,并将它部署到Tomcat中去后是可以启动服务的,并且访问本地地址:http://localhost:8080/cas/login还可以看到如下界面:
使用 默认账号:casuser 默认密码:Mellon还可以登录成功,不过这个CAS服务端目前并没有什么卵用,只是可以看看而已。
这两个红色警告,一个是说没有使用Https,另一个是说你只有一个静态的权限,一个写死的用户,其实就是告诉你现在的服务端是不安全的。
- 第一个问题:关于https请求
服务端是http的其实也可以使用,我在这个教程中不会讲解如何搭建https请求的服务端,因为我们的子项目都是http请求的,所以服务端我也不打算用https请求了,如果你想学习https请求,请参看别人的参考教程,这里不做讲解。 - 第二个问题:关于静态用户
- 我们先打开项目的pom.xml文件,引入相关的jar包,注释掉用不到的jar包,我的数据库是mysql的,如果你的数据库不是,请找到对应的jar包进行引入即可:
1 | <?xml version="1.0" encoding="UTF-8"?> |
- 修改配置文件
我们在cas-overly这个项目的根目录下新建一个src目录,并在src目录下创建一个resource目录,在resource目录下再新建一个application.properties文件,该文件中要写的就是我们的配置文件内容了:
1 | #STEP 3 在TOMCAT8.5中跑一个模板然后将其war包中解压出来的的application.properties复制出来,放到手动创建的src下的resources里面 ## # CAS Server Context Configuration #z server.context-path=/cas server.port=9092 #STEP 5添加认证服务 cas.serviceRegistry.initFromJson=true #STEP 4签发证书,如果是用spring boot之类嵌入式的容器,则需要改这里的配置,如果是直接部在tomcat中,则需要把tomcat改成https的 #server.ssl.key-store=file:/etc/cas/thekeystore #server.ssl.key-store-password=changeit #server.ssl.key-password=changeit # server.ssl.ciphers= # server.ssl.client-auth= # server.ssl.enabled= # server.ssl.key-alias= # server.ssl.key-store-provider= # server.ssl.key-store-type= # server.ssl.protocol= # server.ssl.trust-store= # server.ssl.trust-store-password= # server.ssl.trust-store-provider= # server.ssl.trust-store-type= #server.max-http-header-size=2097152 #server.use-forward-headers=true #server.connection-timeout=20000 #server.error.include-stacktrace=NEVER #server.tomcat.max-http-post-size=2097152 #server.tomcat.basedir=build/tomcat #server.tomcat.accesslog.enabled=true #server.tomcat.accesslog.pattern=%t %a "%r" %s (%D ms) #server.tomcat.accesslog.suffix=.log #server.tomcat.max-threads=10 #server.tomcat.port-header=X-Forwarded-Port #server.tomcat.protocol-header=X-Forwarded-Proto #server.tomcat.protocol-header-https-value=https #server.tomcat.remote-ip-header=X-FORWARDED-FOR #server.tomcat.uri-encoding=UTF-8 spring.http.encoding.charset=UTF-8 spring.http.encoding.enabled=true spring.http.encoding.force=true ## # CAS Cloud Bus Configuration # spring.cloud.bus.enabled=false # spring.cloud.bus.refresh.enabled=true # spring.cloud.bus.env.enabled=true # spring.cloud.bus.destination=CasCloudBus # spring.cloud.bus.ack.enabled=true endpoints.enabled=false endpoints.sensitive=true endpoints.restart.enabled=false endpoints.shutdown.enabled=false management.security.enabled=true management.security.roles=ACTUATOR,ADMIN management.security.sessions=if_required management.context-path=/status management.add-application-context-header=false security.basic.authorize-mode=role security.basic.enabled=false security.basic.path=/cas/status/** ## # CAS Web Application Session Configuration # server.session.timeout=300 server.session.cookie.http-only=true server.session.tracking-modes=COOKIE ## # CAS Thymeleaf View Configuration # spring.thymeleaf.encoding=UTF-8 spring.thymeleaf.cache=false spring.thymeleaf.mode=HTML ## # CAS Log4j Configuration # # logging.config=file:/etc/cas/log4j2.xml server.context-parameters.isLog4jAutoInitializationDisabled=true ## # CAS AspectJ Configuration # spring.aop.auto=true spring.aop.proxy-target-class=true ## # CAS Authentication Credentials # #STEP4 注释掉写死的用户 改用jdbc的用户 START #cas.authn.accept.users=casuser::Mellon cas.authn.jdbc.query[0].sql=select * from s_user where username=? cas.authn.jdbc.query[0].healthQuery= cas.authn.jdbc.query[0].isolateInternalQueries=false cas.authn.jdbc.query[0].url=jdbc:mysql://172.18.18.25:3306/pa_db?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false cas.authn.jdbc.query[0].failFast=true cas.authn.jdbc.query[0].isolationLevelName=ISOLATION_READ_COMMITTED cas.authn.jdbc.query[0].dialect=org.hibernate.dialect.MySQLDialect cas.authn.jdbc.query[0].leakThreshold=10 cas.authn.jdbc.query[0].propagationBehaviorName=PROPAGATION_REQUIRED cas.authn.jdbc.query[0].batchSize=1 cas.authn.jdbc.query[0].user=root #cas.authn.jdbc.query[0].ddlAuto=create-drop cas.authn.jdbc.query[0].maxAgeDays=180 cas.authn.jdbc.query[0].password=dhcc cas.authn.jdbc.query[0].autocommit=false cas.authn.jdbc.query[0].driverClass=com.mysql.jdbc.Driver cas.authn.jdbc.query[0].idleTimeout=5000 # cas.authn.jdbc.query[0].credentialCriteria= # cas.authn.jdbc.query[0].name= # cas.authn.jdbc.query[0].order=0 # cas.authn.jdbc.query[0].dataSourceName= # cas.authn.jdbc.query[0].dataSourceProxy=false cas.authn.jdbc.query[0].fieldPassword=password # cas.authn.jdbc.query[0].fieldExpired= # cas.authn.jdbc.query[0].fieldDisabled= # cas.authn.jdbc.query[0].principalAttributeList=sn,cn:commonName,givenName #cas.authn.jdbc.query[0].passwordEncoder.type=DEFAULT #cas.authn.jdbc.query[0].passwordEncoder.type=com.example.CustomPasswordEncoder #cas.authn.jdbc.query[0].passwordEncoder.characterEncoding=UTF-8 #cas.authn.jdbc.query[0].passwordEncoder.encodingAlgorithm=MD5 #cas.authn.jdbc.query[0].passwordEncoder.secret= #cas.authn.jdbc.query[0].passwordEncoder.strength=16 # cas.authn.jdbc.query[0].principalTransformation.suffix= # cas.authn.jdbc.query[0].principalTransformation.caseConversion=NONE|UPPERCASE|LOWERCASE # cas.authn.jdbc.query[0].principalTransformation.prefix= # STEP4 END ## # CAS Delegated Authentication # #cas.authn.pac4j.bitbucket.clientName=Bitbucket #cas.authn.pac4j.dropbox.clientName=Dropbox #cas.authn.pac4j.facebook.clientName=Facebook #cas.authn.pac4j.foursquare.clientName=Foursquare #cas.authn.pac4j.github.clientName=Github #cas.authn.pac4j.google.clientName=Google #cas.authn.pac4j.linkedIn.clientName=LinkedIn #cas.authn.pac4j.paypal.clientName=PayPal #cas.authn.pac4j.twitter.clientName=Twitter #cas.authn.pac4j.yahoo.clientName=Yahoo #cas.authn.pac4j.windowsLive.clientName=Windows Live #cas.authn.pac4j.wordpress.clientName=WordPress #多属性 cas.authn.attributeRepository.jdbc[0].singleRow=true cas.authn.attributeRepository.jdbc[0].order=0 cas.authn.attributeRepository.jdbc[0].url=jdbc:mysql://172.18.18.25:3306/pa_db?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false cas.authn.attributeRepository.jdbc[0].username=username cas.authn.attributeRepository.jdbc[0].user=root cas.authn.attributeRepository.jdbc[0].password=dhcc cas.authn.attributeRepository.jdbc[0].sql=select * from s_user where {0} cas.authn.attributeRepository.jdbc[0].dialect=org.hibernate.dialect.MySQLDialect cas.authn.attributeRepository.jdbc[0].ddlAuto=none cas.authn.attributeRepository.jdbc[0].driverClass=com.mysql.jdbc.Driver cas.authn.attributeRepository.jdbc[0].leakThreshold=10 cas.authn.attributeRepository.jdbc[0].propagationBehaviorName=PROPAGATION_REQUIRED cas.authn.attributeRepository.jdbc[0].batchSize=1 cas.authn.attributeRepository.jdbc[0].healthQuery=SELECT 1 cas.authn.attributeRepository.jdbc[0].failFast=trueyeshi #http访问 cas.tgc.secure=false |
看上面的配置文件你会发现我配置了mysql的jdbc连接请求,其实就是为了给CAS服务端配置用户名和密码验证的,你自己做的话,需要在你的数据库中创建一个数据库空间和一张用户表,并且保证该表中最少有三个字段:id,username和password,这个配置文件中写的是我的测试服务器的配置,你要改成自己的哦。
这里有几个点要注意下:
cas.authn.accept.users=casuser::Mellon这个配置记得删掉,这就是那个写死的用户
cas.authn.jdbc.query[0]这些配置就是数据库验证相关的内容
在cas.authn.jdbc.query[0].sql中,程序会把你登录时输入的用户名作为参数传进去
cas.authn.jdbc.query[0].fieldPassword则是指明那一列对应的是你输入的密码,目前没有做MD5
如果使用http访问 必须加入 cas.tgc.secure=false 否则登录状态不能共享。
- 加入http访问服务
请在系统的C:\WINDOWS\System32\drivers\etc\hosts 文件中加入:
127.0.0.1 com.dhcc.cas
这么做的目的是将这个IP地址指向这个域名,其实本意是为了https访问做的工作,如果想知道其目的可以查看cas https访问相关的资料,本教程里就不再细说了。
这里可能不好理解,我先来讲一个我遇到的问题,你就可以理解了:
配置好CAS的客户端后,我就开始测试我的客户端子系统,在浏览器中输入我的子系统项目地址:
http://com.dhcc.cas:9093
后由于我的子系统的配置,访问界面会重定向到
http://com.dhcc.cas:9092/cas/login?service:http://com.dhcc.cas:9093/
但是这个界面理论上来说应该是跳转到CAS服务端的登录界面的,可是我的这个路径却无法跳转,
并展示如下界面:
我百思不得其解,尝试了各种方法也不行,直到我发现了将路径改为:
http://com.dhcc.cas:9092/cas/login?service=https://com.dhcc.cas:9093/
界面会自动跳转到CAS服务端的登录界面,这时我才开始回忆起CAS服务端是不是哪里我没有配置对,于是我在网上查资料才知道CAS4.2以上光加入cas.tgc.secure=false 这个参数配置是不够的,还要对HTTPSandIMAPS-10000001.json这个文件中进行修改,大家可能会问了,我怎么没看到这个文件?
因为默认的项目架构是没有这个文件的,我们要将导出的war包解压,然后在WEB-INF\classes\services这个路径下找到这个文件,然后在我们的项目中的resources目录下再新建一个services目录,并将该文件拷贝到这个路径下即可。
现在你的项目架构应该是这个样子的:
引入这个文件肯定是为了修改它,我们可以看到这个json中有行代码,其实是个正则表达式:1
"serviceId" : "^(https|imaps)://.*"
了解json的都知道,这里其实是说serviceId的值是所有带https和imaps的url请求都会被当成已经注册的服务,我们只要将它改成下面这样就可以了:1
"serviceId" : "^(https|imaps|http)://.*"
这样我们就可以安心的使用CAS的http请求服务了。
结语
至此,我们的CAS服务端基本上就搭建好了,只要将它打成war包,并放到tomcat中启动起来,便可以作为们的单点登录的服务端使用了。
这里需要注意一下几点:
- 注意自定义的配置文件是否写对了,尤其是数据库的配置。
- 一定要加入并修改HTTPSandIMAPS-10000001.json文件,否则后期你的CAS客户端肯定不会自动跳转到登录界面。
只要你的服务端可以正常启用,你就成功了一半了,下一篇我们就开始讲解CAS客户端的配置了。