一、关于SSO单点登录
单点登录sso的实现常见的有Oauth2(当前主流,较复杂)和CAS(Center Authentication Server),它们的区别。这里先研究一把apereo 实现的CAS SSO。
二、CAS基本原理,参考https://www.jianshu.com/p/b7de8e4cf217
- 访问服务:SSO 客户端发送请求访问应用系统提供的服务资源。
- 定向认证:SSO 客户端会重定向用户请求到 SSO 服务器。
- 用户认证:用户身份认证。
- 发放票据: SSO 服务器会产生一个随机的 Service Ticket 。
- 验证票据: SSO 服务器验证票据 ST 的合法性,验证通过后,允许客户端访问服务。(每次请求都会到server端验证ST)
- 传输用户信息: SSO 服务器验证票据通过后,传输用户认证结果信息给客户端。
CAS Client 与受保护的客户端应用部署在一起,以 Filter 方式保护 Web 应用的受保护资源,过滤从客户端过来的每一个 Web 请求,同时, CAS Client 会分析 HTTP 请求中是否包含请求 Service Ticket( ST 上图中的 Ticket) ,如果没有,则说明该用户是没有经过认证的;于是 CAS Client 会重定向用户请求到 CAS Server ( Step 2 ),并传递 Service (要访问的目的资源地址)。 Step 3 是用户认证过程,如果用户提供了正确的 Credentials , CAS Server 随机产生一个相当长度、唯一、不可伪造的 Service Ticket ,并缓存以待将来验证,并且重定向用户到 Service 所在地址(附带刚才产生的 Service Ticket ) , 并为客户端浏览器设置一个 Ticket Granted Cookie ( TGC ) ; CAS Client 在拿到 Service 和新产生的 Ticket 过后,在 Step 5 和 Step6 中与 CAS Server 进行身份核实,以确保 Service Ticket 的合法性。在该协议中,所有与 CAS Server 的交互均采用 SSL 协议,以确保 ST 和 TGC 的安全性。协议工作过程中会有 2 次重定向 的过程。但是 CAS Client 与 CAS Server 之间进行 Ticket 验证的过程对于用户是透明的(使用 HttpsURLConnection )。
应用系统将登录请求转给认证中心,这个很好解决,我们一个HTTP重定向即可实现。现在的问题是,用户在认证中心登录后,认证中心如何将消息转回给该系统?这是在单web系统中不存在的问题。我们知道HTTP协议传递消息只能通过请求参数方式或cookie方式,cookie跨域问题不能解决,我们只能通过URL请求参数。我们可以将认证通过消息做成一个令牌(token)再利用HTTP重定向传递给应用系统。但现在的关键是:该系统如何判断这个令牌的真伪?如果判断这个令牌确实是由认证中心发出的,且是有效的?我们还需要应用系统和认证中心之间再来个直接通信,来验证这个令牌确实是认证中心发出的,且是有效的。由于应用系统和认证中心是属于服务端之间的通信,不经过用户浏览器,相对是安全的。
假如现在应用集群中又两个系统A、B。当客户首次登录A系统的时候,流程如下:
- 用户浏览器访问系统A需登录受限资源。
- 系统A发现该请求需要登录,将请求重定向到认证中心,进行登录。
- 认证中心呈现登录页面,用户登录,登录成功后,认证中心重定向请求到系统A,并附上认证通过令牌。
- 系统A与认证中心通信,验证令牌有效,证明用户已登录。
- 系统A将受限资源返给用户。
已登录用户首次访问应用群中系统B时:
- 浏览器访问另一应用B需登录受限资源。
- 系统B发现该请求需要登录,将请求重定向到认证中心,进行登录。
- 认证中心发现已经登录,即重定向请求响应到系统B,附带上认证令牌。
- 系统B与认证中心通信,验证令牌有效,证明用户已登录。
- 系统B将受限资源返回给客户端。
三、认证服务端部署
因为6.X版本需要JDK11+,故使用较旧的5.3(JDK8+)版本,地址https://github.com/apereo/cas-overlay-template/tree/5.3,clone或下载zip,进入项目根目录mvm clean package打成一个cas.war包。放到Tomcat webapp并启动tomcat,浏览器访问http://192.168.154.134:8080/cas/login ,页面稍微改一下,加上公司logo等就可以了
四、https配置(Tomcat、Nginx)
tomcat直接支持https,参考https://www.cnblogs.com/wanghaoyuhappy/p/5267702.html
#生成证书,密码123456
keytool -genkeypair -alias "futurecloud" -keyalg "RSA" -keystore "/etc/cas/tomcat.keystore"
tomcat server.xml配置https
<Connector
protocol="org.apache.coyote.http11.Http11NioProtocol"
port="8443" maxThreads="200"
scheme="https" secure="true" SSLEnabled="true"
keystoreFile="/etc/cas/tomcat.keystore" keystorePass="123456"
clientAuth="false" sslProtocol="TLS"/>
企业中一般配置Nginx支持https进行反向代理
检查Nginx是否已安装ssl模块
[root@master nginx-1.7.4]# nginx -V
nginx version: nginx/1.7.4
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-11) (GCC)
TLS SNI support enabled
configure arguments: --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module
如果没有
#重新配置
./configure --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module
#重新编译make,不需要make install安装。否则会覆盖原有的模块
或重新安装Nginx (docker安装参考)
wget http://nginx.org/download/nginx-1.7.4.tar.gz
tar -zxvf nginx-1.7.4.tar.gz
./configure --prefix=/usr/local/nginx --with-http_stub_status_module --with-
http_ssl_module
make
make install
nginx -V
nginx -s quit
nginx -c /usr/XXX/nginx.conf 如重新安装最好带配置启动,避免用错配置
通过openssl生成证书
1)设置server.key,这里需要设置两遍密码123456:
openssl genrsa -des3 -out server.key 1024
2)不填或随便填:
openssl req -new -key server.key -out server.csr
3) 写RSA秘钥(这里也要求输入之前设置的密码):
openssl rsa -in server.key -out server_nopwd.key
4)生成私钥:
openssl x509 -req -days 365 -in server.csr -signkey server_nopwd.key -out server.crt
Nginx使用生成的证书配置cas server端https访问
server {
listen 81; #侦听80端口,如果强制所有的访问都必须是HTTPs的,这行需要注销掉
listen 443 ssl;
server_name localhost; #域名
#ssl on; ####如果强制HTTPs访问,这行要打开
ssl_certificate /etc/cas/server.crt;
ssl_certificate_key /etc/cas/server_nopwd.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_protocols SSLv2 SSLv3 TLSv1.2; # 指定密码为openssl支持的格式
ssl_ciphers HIGH:!aNULL:!MD5; # 密码加密方式
ssl_prefer_server_ciphers on; # 依赖SSLv3和TLSv1协议的服务器密码将优先于客户端密码
location / {
#root html;
#index index.html index.htm;
proxy_pass http://localhost:8080; #tomcat就没必要配置https了
}
}
页面访问,application.properties内置用户密码casuser/Mellon
五、整合MySQL查询用户登录
在pom.xml中profile的id = default的dependencies中添加依赖
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-support-jdbc</artifactId>
<version>${cas.version}</version>
</dependency>
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-support-jdbc-drivers</artifactId>
<version>${cas.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.37</version>
</dependency>
application.properties配置数据库连接
cas.authn.jdbc.query[0].url=jdbc:mysql://4X.XX.XX.18:3306/cas?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
cas.authn.jdbc.query[0].user=root
cas.authn.jdbc.query[0].password=XXXX
cas.authn.jdbc.query[0].sql=select * from cas_user where username=?
cas.authn.jdbc.query[0].fieldPassword=password
cas.authn.jdbc.query[0].driverClass=com.mysql.jdbc.Driver
表结构与数据
CREATE TABLE `cas_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT '',
`email` varchar(30) DEFAULT NULL,
`telephone` varchar(11) DEFAULT NULL,
UNIQUE KEY `id` (`id`) USING HASH
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
INSERT INTO `cas_user` VALUES (1, 'jwolf', 'jwolf', NULL, NULL);
新用户jwolf/jwolf登录