SSO单点登录技术-CAS
SSO单点登录技术简介
快速上手与原理探究
概述
什么是SSO?
单点登录(Single Sign On)通过SSO整合多个应用,可以避免进入多个应用重复登录、此外也可以避免系统安全问题。
如淘宝和天猫,如果登录一个,再登录天猫,天猫是不需要登陆的
什么是CAS
CAS(Central Authentication Service,统一身份认证服务)是一种实现SSO的一种成熟实现方案,一般由服务端与客户端组成。
原来是Yale大学2004年发起的开发项目。
通过引入CAS 系统架构就演变成这样了
这样单个系统一旦需要认证服务,都会前往CAS server 去认证
所以层架构上看,一定要有一个 CAS server(一般需要单独部署),而原来需要认证的单个服务就是 CAS client 负责处理对客户端受保护资源的访问请求,需要进行认证时就会重定向到 CAS Server
CAS原理 (协议流程)
CAS基本的协议过程
CAS 包含两个部分: CAS Server 和 CAS Client。CAS Server 需要独立部署,主要负责对用户的认证工作;CAS Client 负责处理对客户端受保护资源的访问请求,需要登录时,重定向到 CAS Server。
CAS Client 与受保护的客户端应用部署在一起,以 Filter 方式保护受保护的资源。对于访问受保护资源的每个 Web 请求,CAS Client 会分析该请求的 Http 请求中是否包含 Service Ticket,如果没有,则说明当前用户尚未登录,于是将请求重定向到指定好的 CAS Server 登录地址,并传递 Service (也就是要访问的目的资源地址),以便登录成功过后转回该地址。用户在第 3 步中输入认证信息,如果登录成功,CAS Server 随机产生一个相当长度、唯一、不可伪造的 Service Ticket,并缓存以待将来验证,之后系统自动重定向到 Service 所在地址,并为客户端浏览器设置一个 Ticket Granted Cookie(TGC),CAS Client 在拿到 Service 和新产生的 Ticket 过后,在第 5,6 步中与 CAS Server 进行身份核实,以确保 Service Ticket 的合法性。
在该协议中,所有与 CAS 的交互均采用 SSL 协议,确保,ST 和 TGC 的安全性。协议工作过程中会有 2 次重定向的过程,但是 CAS Client 与 CAS Server 之间进行 Ticket 验证的过程对于用户是透明的。
另外,CAS 协议中还提供了 Proxy (代理)模式,以适应更加高级、复杂的应用场景,具体介绍可以参考 CAS 官方网站上的相关文档。
流程图形式(来自B站UP@自由的加百列)
首次登录
这里的认证中心即CAS server, 而A系统与B系统是指被 认证中心管控下的 CAS client
当用浏览器第一次访问A系统,发现妹有token 或者 token失效 (token验证通过但是发现有效期过了)
A系统的CAS client 会将通知浏览器重定向 认证中心
认证中心发现 妹有cookie 或者cookie失效重定向到统一登录页
我们输入用户名密码后提交
认证中心完成认证后,并通知浏览器重定向到 A系统并在url中携带ticket
A系统获取ticket , 并与认证中心交互完成认证,认证中心向A系统颁发令牌(token)(浏览器不可见)
A系统通知浏览器认证通过并给浏览器颁发token
这样,浏览器就能凭借token与A系统正常交互了。
PS:在浏览器端你能看到网址的变化是
1、输入www.a.com (尝试获取A系统资源,但发现妹有令牌)
2、自动跳转到认证中心 www.cas.com/ (发现妹有cookie)
3、自动跳转到登录页 www.cas.com/login(输入用户名密码)
4、点击登录跳转 www.a.com?ticket=123abc (a系统拿ticket取认证中心认证)
5、认证通过后给我们颁发令牌,我们拿令牌获取资源 www.a.com/index
再次登录
当我们用浏览器第一次访问B系统,B系统发现妹有token 或者token失效
B系统通知浏览器,让浏览器重定向 认证中心
认证中心发现浏览器已经有了cookie ,并且cookie有效,直接跳过登录完成认证,会通知浏览器重定向B系统并在url中携带ticket
B系统获取了ticket在后台与认证中心交互获取令牌完成认证,B系统将获取的令牌(token)再颁发给浏览器
这样浏览器就能正常访问B系统资源了
PS:在浏览器端你能看到网址的变化是
1、输入 www.b.com (尝试获取b系统资源,但发现妹有令牌)
2、跳转认证中心 www.cas.com 直接完成认证
3、跳转b系统并携带 ticket www.b.com?ticket=123abc (b系统拿ticket取认证中心认证)
4、认证通过后给我们颁发令牌,我们拿令牌获取资源 www.b.com/index
CAS Server 服务端
CAS 服务端软件包下载
前往 服务端的 overlay包
https://github.com/apereo/cas-overlay-template
下载仓库的压缩包
服务端项目构建:
再命令行状态下,再文件夹 cas-overlay-template-master种执行命令:
build.cmd.package
# build.cmd 是包中的一个软件
注意:
1、jdk 大于等于1.8
2、maven 大于等于 3.5
3、要联网还要下载依赖
最后会生成一个war包供使用
搭建tomcat HTTPS支持
CAS采用HTTPS协议处理用户请求,所以我们需要配置Tomcat支持HTTPS协议
1、生成密钥库
我们采用JDK自带的keytool工具生成密钥库
别名java1234 存储路径 如 D:\cas\keystore
keytool -genkey -v -alias java1234 -keyalg RSA -keystore E:\other\CAS\keystore\java1234.keystore
输入密钥库口令:666666
您的名字于姓氏:java1234.com
您的组织单位名称:java1234.com
您的组织名称是什么:java1234.com
您所在的城市或区域名称是什么:beijing
您所在的省市自治区名称是什么:beijing
该单位的双字母国家代码是什么:cn
CN=java1234.com,OU=java1234.com *****
是否正确?y
输入java1234的密钥口令《如果于密钥库口令相同按回车》按回车:回车
2、从密钥库里导出证书
keytool -export -trustcacerts -alias java1234 -file E:\other\CAS\keystore\java1234.cer -keystore E:\other\CAS\keystore\java1234.keystore
输入密钥库口令:666666
这样就再目录里面生成了证书文件(cer文件)
3、将证书导入到JDK证书库
keytool -import -trustcacerts -alias java1234 -file E:\other\CAS\keystore\java1234.cer -keystore "C:\Program Files\Java\jdk1.8.0_271\jre\lib\security\cacerts"
密码:changeit
4、tomcat 配置http支持
启动tomcat
就可以通过https来访问tomcat了!
CAS server 配置
CAS server war下载
CAS的github 网址:https://github.com/apereo/cas
CAS 服务端:
https://github.com/apereo/cas-overlay-template
可以在这里将所需要的 CAS 服务端代码clone下来打包发布
https://repo1.maven.org/maven2/cas/
CAS的maven仓库 亦可以用下面的war包
https://repo1.maven.org/maven2/org/apereo/cas/cas-server-webapp-tomcat/5.3.14/
下载:cas-server-webapp-tomcat-5.3.14
把下载的war包放在 tomacat 安装目录 /webapps下
把war包放tomcat下,启动tomcat会自动解压,我们把名称改成cas,方便访问;
cas 8443端口
CAS server 配置
配置hosts: C:\Windows\System32\drivers\etc
127.0.0.1 java1234.com
可以通过域名访问:
用户名再配置文件中
/webapps/cas/WEB-INF/classes/ 中的application.properties 中获取
最后一行由默认接收用户
cas.authn.accept.users=casuser::Mellon
即用户名 casuser 密码 Mellon
登录
配置日志
tomcat/webapps/cas/WEB-INF/classes/ 中的log4j2.xml文件中
可以修改日志存放的路径
配置数据源 数据库用户认证
定义数据库
定义一个用来用户认证的数据库,数据表中包含用户名和密码选项
CREATE DATABASE /*!32312 IF NOT EXISTS*/`db_sso` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `db_sso`;
/*Table structure for table `t_cas` */
DROP TABLE IF EXISTS `t_cas`;
CREATE TABLE `t_cas` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(30) DEFAULT NULL,
`password` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
/*Data for the table `t_cas` */
insert into `t_cas`(`id`,`username`,`password`) values (1,'java1234','123456');
修改application.properties配置文件
注释掉写死的认证用户
加上jdbc数据源配置
# cas.authn.accept.users=casuser::Mellon
cas.authn.jdbc.query[0].dialect=org.hibernate.dialect.MySQL5Dialect
cas.authn.jdbc.query[0].url=jdbc:mysql://localhost:3306/db_sso?serverTimezone=GMT
cas.authn.jdbc.query[0].user=root
cas.authn.jdbc.query[0].password=123456
cas.authn.jdbc.query[0].sql=select * from t_cas where username=?
cas.authn.jdbc.query[0].fieldPassword=password
cas.authn.jdbc.query[0].driverClass=com.mysql.jdbc.Driver
加上jdbc驱动包以及支持jar
把课程资料里面向相关依赖包放进 tomcat/webapps/cas/WEB-INF/lib目录中
点击tomcat 中 bin 目录中的staerup.bash 启动tomcat
发现原来配置文件中的已经无法登录了,用数据库中的便可以登录了。
CAS加密校验
修改配置文件
增加
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
默认密码加密方式md5
数据库生成下md5密码
将密码都进行md5加密后存进数据库
SELECT MD5('123456');
将这段结果把原来的密码替换
重新启动
用户名密码不变可以继续登录(默认帮我们进行md5加密然后进行匹配)
CAS Client 客户端
CAS Client+SpringBoot客户端整合搭建
假设采用父子模块的方式搭建
新建父项目sso-sys
新建一个maven项目
然后在这个父项目中加一些依赖管理和规范
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.java1234</groupId>
<artifactId>sso-sys</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<cas.version>2.3.0-GA</cas.version>
<spring-boot.version>2.3.4.RELEASE</spring-boot.version>
</properties>
<modules>
<module>common-sys</module>
<module>hr-sys</module>
<module>crm-sys</module>
<module>fd-sys</module>
<module>oa-sys</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>net.unicon.cas</groupId>
<artifactId>cas-client-autoconfig-support</artifactId>
<version>${cas.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
新建子项目crm-sys
右键父项目 新建模块(子项目)
pom文件中加入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>sso-sys</artifactId>
<groupId>com.java1234</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>crm-sys</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<!-- 客户端依赖是必须的-->
<dependency>
<groupId>net.unicon.cas</groupId>
<artifactId>cas-client-autoconfig-support</artifactId>
</dependency>
<!-- cas支持-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-cas</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
</dependency>
</dependencies>
</project>
application.yml
server:
port: 7777
cas:
server-url-prefix: https://java1234.com:8443/cas
server-login-url: https://java1234.com:8443/cas/login
client-host-url: http://java1234.com:7777
validation-type: cas3
指定端口7777
resources 下面新建templates
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>crm客户关系</title>
</head>
<body>
欢迎: 进入crm客户关系;
</body>
</html>
controller
package com.java1234.controller;
import org.jasig.cas.client.validation.Assertion;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpSession;
/**
* @author java1234_小锋
* @site www.java1234.com
* @company Java知识分享网
* @create 2020-10-08 19:44
*/
@Controller
public class IndexController {
/**
* 网站根目录请求
* @return
*/
@RequestMapping("/crm")
public ModelAndView root(HttpSession session){
ModelAndView mav=new ModelAndView();
mav.setViewName("index");
return mav;
}
}
启动类
package com.java1234;
import net.unicon.cas.client.configuration.EnableCasClient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
/**
}
* 开启CAS @EnableCasClient
*/
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableCasClient
public class CrmSysApplication {
public static void main(String[] args) {
SpringApplication.run(CrmSysApplication.class, args);
}
}
启动项目(7777)端口
http://localhost:7777/crm 访问首页
解释一下配置
cas:
server-url-prefix: https://java1234.com:8443/cas #CAS 服务端地址
server-login-url: https://java1234.com:8443/cas/login # CAS 登陆地址
client-host-url: http://java1234.com:7777 # 登陆成功后回调的地址,该服务首页地址
validation-type: cas3 # cas类型 2已经淘汰
在SpringBoot启动类上加@EnableCasClient 后便可以登陆了
PS:如果不加@EnableCasClient 那么登录的就是普通Springboot项目,直接进入该系统
现在加了@EnableCasClient 再次输入 localhost:7777/crm 不会直接访问index.html 而是会转到 CAS 服务端
这里报了错是因为?
CAS只支持默认只支持https请求不支持 http请求。而我们CAS登录请求的地址这里是(看上面的url)是 http://java1234.com:8443
可以修改配置来进行修改
1、在tomcat/webapps/cas/WEB-INF/classes/service 下面进行配置 修改里面的HTTPSandIMAPS-10000001.json
- 更改里面的serviceId 加一个http即可
2、在tomcat/webapps/cas/WEB-INF/classes/application-properties (就是刚开始配置数据源的那个地方)在最后加一个
cas.tgc.secure=false
cas.serviceRegistry.initFromJson=true
再次启动tomcat
发现正常跳转到登录界面,输入正确的用户名密码后就可以访问index.html了
简单回顾一下CAS基本的协议过程
CAS 包含两个部分: CAS Server 和 CAS Client。CAS Server 需要独立部署,主要负责对用户的认证工作;CAS Client 负责处理对客户端受保护资源的访问请求,需要登录时,重定向到 CAS Server。
CAS Client 与受保护的客户端应用部署在一起,以 Filter 方式保护受保护的资源。对于访问受保护资源的每个 Web 请求,CAS Client 会分析该请求的 Http 请求中是否包含 Service Ticket,如果没有,则说明当前用户尚未登录,于是将请求重定向到指定好的 CAS Server 登录地址,并传递 Service (也就是要访问的目的资源地址),以便登录成功过后转回该地址。用户在第 3 步中输入认证信息,如果登录成功,CAS Server 随机产生一个相当长度、唯一、不可伪造的 Service Ticket,并缓存以待将来验证,之后系统自动重定向到 Service 所在地址,并为客户端浏览器设置一个 Ticket Granted Cookie(TGC),CAS Client 在拿到 Service 和新产生的 Ticket 过后,在第 5,6 步中与 CAS Server 进行身份核实,以确保 Service Ticket 的合法性。
在该协议中,所有与 CAS 的交互均采用 SSL 协议,确保,ST 和 TGC 的安全性。协议工作过程中会有 2 次重定向的过程,但是 CAS Client 与 CAS Server 之间进行 Ticket 验证的过程对于用户是透明的。
另外,CAS 协议中还提供了 Proxy (代理)模式,以适应更加高级、复杂的应用场景,具体介绍可以参考 CAS 官方网站上的相关文档。
CAS 客户端获取 用户名
有时客户端服务登陆后需要回显用户名,这个用户名怎么获取呢?
我们在上面的 CAS协议执行流程可以看到在认证通过后,CAS client 可以从 CAS server去获取登录认证的信息比如 用户名
thymeleaf(java的模板引擎)页面可以通过 ${session.const_cas_assertion_.principal.name} 取值
具体可以看看源码怎么找到的
可以在client项目依赖包(cas-client-core-3.5.1.jar)中Servlet3AuthenticationFilter中看看
可见他将 principal.getAssertion() 的结果封装到了 session里面,看看 principal.getAssertion() 的返回值是什么
它实现了 SimplePrincipal (里面封装了name属性)所以本质上就是对name等基本属性的封装
那么在 全段就可以用${session._const_cas_assertion_.principal.name}
来获取用户名了
<font th:text="${session._const_cas_assertion_.principal.name}"></font>
登陆成功后显示
CAS 客户端统一注销功能
cas注销方式
我们通过重定向到cas服务的logout接口,来实现统一注销
增加 controller代码
/**
* 注销
* @return
*/
@RequestMapping("/logout")
public String logout(){
return "redirect:https://java1234.com:8443/cas/logout";
}
在index.html 中加入按钮
<a href="/logout">安全退出</a>
CAS 单点登录实现测试
在CAS父项目中添加三个系统 crm、fd、hr
实现下面的功能
我们点击任意一个系统、进入系统如果发现是没有登陆的就会跳转到 servcer 的登录界面,我们输入永明密码后再跳转回来原服务
就不写了
CAS server 登录/登出界面修改
CAS server 用springMVC + webflow 实现的
Spring Web Flow 构建与SpringMVC 之上允许实现Web 应用程序的流程,流程封装了一系列的步骤,指导用户执行某些业务任务,它跨越多个HTTP请求,具有状态处理事务数据,可重用,并且可能是动态的,并且本质上是长期运行的。
登录和等处的界面修改步骤
webflow
再 tomcat/webapps/cas/WEB-INF/classes/webflow/
里面一个login 一个logout,这里是业务逻辑,暂时不改
static
static下主要是一些静态文件包括css样式js脚本、images图片等
浏览器的icon(标签页做左边的图标 修改 favicon即可)
templates(修改)
去掉head foot
在classes/templates下
我们用 idea open打开 cas目录
打开 templates下面的layout.html
尝试简单修改一下布局(把 head和foot 注释掉)
重启
head foot 已经去掉了,现在再尝试去掉右边的,怎么去掉呢?
右边可以看到 Links to CAS Resources 我们复制一下,进入全局搜索
edit->Find->Find in path 粘贴即可找到
将notices 部分注释掉
然后在注释中的dev部分添加自己的图片,然后添加自己的图片样式
再用户名密码下面有一段提示信息,这里在哪里修改呢?
- 在loginform.html里面
- loginsiderbar.html 里面
它默认读取的优先级应该是中文配置所以最终读取的是 classes目录下面的message_zh_CN.properties 找到里面的screen.welcome.security 选项,修改为自己的中文对应的语句(注意,这里应该改为 \unicode 编码形式,前往 中文转 unicode 编码网站自行替换)若把连接取消则取的是 <p>
标签之间的文本内容
最终实现的界面如下