单点登录前奏
1.单点登录认识及环境搭建
1.单点登录(Single Sign On)
简称SSO。定义是在多个应用系统中,用户只需要登录一次就可以访问所有互相信任的应用系统。一般用于公司内部产品或某个产品系列的所有子系统中。比如公司的oa系统、icode系统、邮箱系统等等;再比如豆瓣系列的豆瓣FM、豆瓣读书、豆瓣电影、豆瓣日记等等,只需要登录一次,访问其他系统就没有必要再登录了。
背景:
- 公司内部现存系统较多,各个系统分布在不同服务器上,用户使用过程中需要多次重复登陆,使用麻烦
- 各系统实现技术不相同,但都是基于Web,客户端语言可以是非java语言
- 各个系统用户数据不尽相同,缺乏统一管理,用户需要维护多套密码,管理员也需要维护多套用户信息
- SSO服务端仅负责认证,权限管理可以交由各系统自己完成
想了解更多单点登录原理进入单点登录原理与简单实现【转】
2.创建证书
证书是单点登录认证系统中很重要的一把钥匙,客户端与服务器的交互安全靠的就是证书;本次由于是演示所以就用JDK自带的keytool工具生成证书;如果以后真正在产品环境中使用肯定要去证书提供商去购买,证书认证一般都是由VeriSign认证,中文网站:http://www.verisign.com/cn/
JDK自带的keytool工具生成证书过程如下:
注意:以管理员的身份打开cmd
1.生成证书
keytool -genkey -alias tomcat -keyalg RSA -keystore D:/software/keys/keystore
需要设置密码:这里设置成123456
注:www.wp.com是域名,以后配置CAS客户端时需要用到且必须一致。
2.导出证书
keytool -export -trustcacerts -alias tomcat -file D:/software/keys/tomcat.cer -keystore D:/software/keys/keystore
3.将证书导入JDK信任库
keytool -import -trustcacerts -alias tomcat -file D:/software/keys/tomcat.cer -keystore "D:\Program Files\Java\jdk1.8.0_131\jre\lib\security/cacerts"
密码是默认的changeit,而不是之前设置的密码。
https://www.cnblogs.com/hamfy/archive/2012/07/31/2616805.html
4.其他命令
查看证书列表
keytool -list -v -keystore "D:\Program Files\Java\jdk1.8.0_131\jre\lib\security/cacerts"
删除证书
keytool -delete -trustcacerts -alias tomcat -keystore "D:\Program Files\Java\jdk1.8.0_131\jre\lib\security/cacerts"
3.Tomcat配置
启用We服务器的SSL,也就是HTTPS加密协议。
配置tomcat的server.xml
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS"
keystoreFile="D:/software/keys/keystore"
keystorePass="123456" />
keystoreFile:创建的key存放的位置
keystorePass:创建证书时的密码
4. 配置www.wp.com域名指向本地
编辑host文件,添加下面内容,保存即可。
5.启动tomcat,浏览器输入https:www.wp.com:8443/ 是否正常访问
如果正常访问到该地址,那么表示配置成功。
若还不能访问,则在server.xml中将下面内容注释掉。
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
2.单点登录实现
1.服务端cas-server
下载cas-server-3.5.0-release
https://www.apereo.org/projects/cas/news
下载完成后解压,并改名为cas,复制到tomcat/webapp目录下,启动tomcat,并访问地址:
https:www.wp.com:8443/cas/login 用户名/密码:admin/admin点击登录。cas默认的验证规则是只要用户名和密码相同就可以通过。
2.客户端
新建springboot web项目,使用maven管理jar包,pom.xml文件加入包
<dependencies>
<!--web应用基本环境配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.jasig.cas.client</groupId>
<artifactId>cas-client-core</artifactId>
<version>${cas.version}</version>
</dependency>
</dependencies>
application.yaml配置文件
server:
port: 8081
spring:
profiles:
active: dev
---
# 开发环境配置
spring:
profiles: dev
cas:
casServerUrlPrefix: https://www.wp.com:8443/cas
casServerLoginUrl: https://www.wp.com:8443/cas/login
serverName: http://localhost:8081
encoding: UTF-8
urlPatterns: /*
新建一个配置类,配置单点登录客户端过滤器CasConfig
package com.baidu.bpit.uuap.conf;
import org.jasig.cas.client.authentication.AuthenticationFilter;
import org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "cas", ignoreUnknownFields = true)
public class CasConfig {
private String casServerUrlPrefix; // sso验证地址
private String casServerLoginUrl; // sso登录地址
private String serverName; // sso验证或者登陆后返回的地址
private String urlPatterns; // sso过滤匹配
private String encoding; // sso编码
/**
* 注册CAS SSO 验证用户是否存在Filter
*
* @return
*/
@Bean
public FilterRegistrationBean cas20ValidateRegistration() {
FilterRegistrationBean cas20 = new FilterRegistrationBean();
cas20.setFilter(new Cas20ProxyReceivingTicketValidationFilter());
cas20.addUrlPatterns(urlPatterns);
cas20.addInitParameter("casServerUrlPrefix", casServerUrlPrefix);
cas20.addInitParameter("serverName", serverName);
cas20.addInitParameter("encoding", encoding);
return cas20;
}
/**
* 注册CAS SSO 用户登录Filter
*
* @return
*/
@Bean
public FilterRegistrationBean cas20LoginRegistration() {
FilterRegistrationBean cas20 = new FilterRegistrationBean();
cas20.setFilter(new AuthenticationFilter());
cas20.addUrlPatterns(urlPatterns);
cas20.addInitParameter("casServerLoginUrl", casServerLoginUrl);
cas20.addInitParameter("serverName", serverName);
cas20.addInitParameter("encoding", encoding);
return cas20;
}
public String getCasServerUrlPrefix() {
return casServerUrlPrefix;
}
public void setCasServerUrlPrefix(String casServerUrlPrefix) {
this.casServerUrlPrefix = casServerUrlPrefix;
}
public String getCasServerLoginUrl() {
return casServerLoginUrl;
}
public void setCasServerLoginUrl(String casServerLoginUrl) {
this.casServerLoginUrl = casServerLoginUrl;
}
public String getServerName() {
return serverName;
}
public void setServerName(String serverName) {
this.serverName = serverName;
}
public String getUrlPatterns() {
return urlPatterns;
}
public void setUrlPatterns(String urlPatterns) {
this.urlPatterns = urlPatterns;
}
public String getEncoding() {
return encoding;
}
public void setEncoding(String encoding) {
this.encoding = encoding;
}
}
创建访问首页controller,获取到session中的用户心
@ResponseBody
@RequestMapping("/demo")
public String getLoginUser(HttpServletRequest httpServletRequest) {
// 获取session里面的用户信息
HttpSession session = httpServletRequest.getSession();
String userName = "-";
if (null != session) {
Assertion assertion = (Assertion) session.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION);
if (null != assertion) {
userName = assertion.getPrincipal().getName();
}
}
logger.info("get current user: {}", userName);
return "Welcome you : " + "demo2";
}
然后启动项目,浏览器访问http://localhost:8081/demo
输入用户名和密码,回车
3.单点登录流程解析
1.理解几个概念
1、TGC (Ticket-granting cookie):存放用户身份认证凭证的cookie,在浏览器和SSO server之间通讯时使用,并且只能用HTTPS传输,说白了,就是你登陆SSO系统后,SSO server会在你的浏览器Cookie里面植入一段Cookie,这样在Cookie的有效期内你访问任何接入SSO的系统都不需要再次输入密码,只需要校验该Cookie的有效性,因为基于HTTPS传输,所以说能够保证传输过程中的安全性,但是注意保管你自己电脑的不被别人窃取该信息,不然别人就能伪造以你的身份登陆。也称为全局会话。
查看你的cookie就能看到该信息,就是叫CASTGC的cookie,可以看到该cookie的有效期为浏览会话结束时。可以设置一个月时间。
2、TGT(Ticket Granting ticket):票据授权票据。该票据存在Server端,其实TGC是TGT的id而已,在你完成认证后服务端生成TGT存起来,然后把TGT的id下发给用户保存在cookie中,这样下次用户来就可以通过TGC找到TGT,然后校验该TGT的一系列信息,认证通过就可以免密码登录。
3、ST(Service Ticket):这个ST便是浏览器中的ticket参数,sso认证通过后携带该参数让用户重定向到下游系统,下游系统拿到该参数便去sso server验证该参数的有效性,通过验证便可以让用户进入下游系统。
怎样才能保证ticket参数的安全性呢?
(1)、https传输。
(2)、ticket只能使用一次,不管认证成功或者失败
(3)、ticket有有效期,默认5分钟
(4)、ticket是随机生成的,具有不可伪造性
2.流程图及流程解析
单点登录常用的操作有三个:login、serviceValidate、logout,最复杂的就是login接口。
单点登录服务端使用spring-webflow技术实现了单点登录的login流程,整理login逻辑流程图如下:
1.用户访问系统1的受保护资源,系统1发现用户未登录,跳转到SSO认证中心,并将用户访问系统1的地址作为参数传递过去
2.SSO认证中心接收到请求后,走login-webflow流程,配置信息在cas-servlet.xml中,
首先会进入初始化操作流程 initialFlowSetupAction,执行初始化数据等操作。
然后响应给浏览器casLoginView.jsp页面
用户提交用户名和密码后去SSO认证中心校验
校验success后,去生成全局会话,将id保存到cookie中;sendTicketGrantingTicketAction
然后去创建系统1的授权令牌ticket;generateServiceTicketAction
最后,SSO以之前传过来的参数地址为地址并且拼接ticket参数,重定向到系统1中。
系统1拿到ticket参数后,去SSO认证中心校验ticket的有效性,有效,再重定向到系统1,系统1会建立用户和系统1的局部会话,证明用户是否登录系统1,最后返回系统1访问的资源。
注意:全局会话TGT的id TGC保存在cookie中CASTGC;若该cookie存在,那么其他客户端也是登录状态,不会再验证用户密码信息了,然后生成新的ticket。
ticket完成客户端service在SSO认证中心的校验。
cas client、cas server、浏览器之间的流程
在图中第3步用户认证成功后,cas server会生成Ticket Granting Ticket(票据授权票据,简称TGT),同时将TGT值以CASTGC为名保存到浏览器的cookie中,之后生成Service Ticket(服务票据,简称ST)并缓存,在第4步时将ST通过浏览器重定向的URL传给cas client。
当客户访问另一个cas client时,同样会被重定向到cas server,而此时我们并不希望再次让用户输入用户密码登陆,名为CASTGC的cookie这时就体现出作用来了,cas server发现存在名为CASTGC的cookie就将其值在已保存的TGT中查找,若存在,则说明已存在合法的TGT,cas server就根据该TGT生成新的ST,接下来的流程就和以前一样了。
当然,在实际产品中不能直接使用从官网下载下来的cas-server,需要对其进行相关的改造,比如登陆页面需要改成体现产品特色的,添加验证码,用户登录信息验证方案,TGT、serviceTicket缓存方案和持久化到数据库、加入域登录等等诸多地方。那么下一篇单点登录cas-server改造是很有必要了解的。