SSO
简介
概念介绍
什么是单点登录?
单点登录全称 Single Sign On(以下简称SSO),是指在多系统应用群中登录一个系统,便可在其他所有系统中得到授权而无需再次登录,包括单点登录与单点注销两部分。
比如,一个项目使用了多个产品,每个产品有各自的登录用户名、密码,如果使用每个产品,都要输入用户、密码,这对使用者来说,肯定是比较痛苦的。
而现在,有个SSO技术,只需使用一套用户名及密码,就可以自动登录/登出任一产品,这样是不是方便多了呢?
单点登录≠门户。
门户系统:其主要框架为 IAM系统平台(Identity and Access Management 的缩写),即“身份识别与访问管理”,具有单点登录、强大的认证管理、基于策略的集中式授权和审计、动态授权、企业可管理性等功能。
其重点便在于身份管理,而单点登录,是其中一个重要的功能。单点登录在一个集中化系统中所起的作用,就是用户访问业务系统时所进行的身份认证
技术架构
SSO 从应用架构层面,主要可以分为集中验证模式和多点验证模式。
-
多点验证模式
应用系统提供各自的登录界面,登录了一套系统后,另外其他系统无需再次登录即可通过身份验证。
在多点验证模式的模式下,所有的登录操作都在应用系统完成,任何一套系统宕机不会对其它系统产生影响,也不会影响正常运行系统间的SSO。但若各套系统的账户不一样的时候,若要用户区分每套系统的用户密码,必定会降低用户的体验,为了解决该问题,多点登录模式最好有统一的用户密码验证的服务(如LDAP身份验证)。
-
集中验证模式(中国移动项目的单点登录、汉得各个服务平台的单点登录)
当应用系统需要登录时,统一交由验证服务器完成登录动作,应用系统不提供登录接入(如登录界面)
相对于多点验证模式来说,集中验证模式的适用范围更广,而且在SSO服务器中使用的是统一的用户名密码,用户无需关注登录的是哪套系统,所以用户体验更加优秀。由于所有的登录都放在了统一的服务器,所以当集中验证服务器宕机时,所有系统无法正常登录,或丢失SSO的功能,建议以独立服务器作为集中验证服务器,并需要保证登录服务器的稳定性。
技术实现
-
代理登录(agent)
代理登录的原理就是在IE端通过表单提交的方式模拟应用系统的登录操作,实现SSO。
优点:无需对原有系统做任何改造,适用于无法改造的旧系统;
缺点:
-
稳定性差,一旦登录期间某台服务器无法响应,则该服务器无法单点登录;
-
安全性差,用户名密码通过明文传输;
-
由于登录期间需要监控各个系统的响应,所以不建议大量使用,否则会影响 登录的性能;
-
由于IE的安全限制,代理登录必须在同域的情况下运行。
-
-
令牌环(token)
通过Cookie 共享令牌环的方式传递当前用户信息,实现SSO。(如 IBM 的LTPA Token,IBM 系列产品间能实现配置式SSO,就依靠此技术。如 IBM Websphere Portal Server 与 Lotus Domino Server 之间的SSO)。
优点:无需统一的验证服务器,是“多点验证模式”的主力实现技术,各个服务器都通过统一的密钥对令牌进行加密解密,安全性高、稳定性好、性能消耗低;
缺点:必须保证各台应用服务器同域;
-
身份票据(ticket)
与令牌环不一样,身份票据是通过URL的方式传递,通过“两次握手”的方式,是适用范围最广的一种SSO实现方式,
优点:可以解决跨域等问题,安全性高、稳定性好;
缺点:必须增加一台验证服务器,保证在高压下验证服务器的稳定运行,性能方面由于每次登录都需要访问验证服务器,所以比令牌环的方式略差一点。
CAS
CAS:Central Authentication Service,耶鲁大学开发的开源系统,使用身份票据实现
原理
结合上图流程和CAS的结构体系来看,CAS包括两部分:CAS Server 和 CAS Client 。
-
CAS Server
负责完成对用户的认证工作,会为用户签发两个重要的票据:登录票据(TGT)和服务票据(ST)来实现认证过程,CAS Server需要独立部署。
-
CAS Client
负责处理对客户端受保护资源的访问请求,需要对请求方进行身份认证时,重定向到 CAS Server 进行认证。准确地来说,它以 Filter 方式保护受保护的资源。
对于访问受保护资源的每个 Web 请求,CAS Client 会分析该请求的 Http 请求中是否包含 ServiceTicket(服务票据,由 CAS Server发出用于标识目标服务)。 CAS Client 与受保护的客户端应用部署在一起。
CAS的核心就是 Ticket,及其在Ticket之上的一系列处理操作。CAS的主要票据有TGT、ST、PGT、PGTIOU、PT,其中TGT、ST是CAS1.0(基础模式)协议中就有的票据,PGT、PGTIOU、PT是CAS2.0(代理模式)协议中有的票据。这里主要介绍CAS1.0(基础模式)中的几种票据。
-
TGT(Ticket Grangting Ticket)
TGT是CAS为用户签发的登录票据,拥有了TGT,用户就可以证明自己在CAS成功登录过。
TGT封装了Cookie值以及此Cookie值对应的用户信息。
用户在CAS认证成功后,生成一个TGT对象,放入自己的缓存(Session);同时,CAS生成cookie(叫TGC),写入浏览器。TGT对象的ID就是cookie的值,当HTTP再次请求到来时,如果传过来的有CAS生成的cookie,则CAS以此cookie值(SessionId)为key查询缓存中有无TGT(Session),如果有的话,则说明用户之前登录过,如果没有,则用户需要重新登录。 -
TGC (Ticket-granting cookie)
上面提到,CAS-Server生成TGT放入自己的Session中,而TGC就是这个Session的唯一标识(SessionId),以Cookie形式放到浏览器端,是CAS Server用来明确用户身份的凭证。(如果你理解Session的存放原理的话就很好理解)
-
ST(Service Ticket)
ST是CAS为用户签发的访问某一服务票据。用户访问service时,service发现用户没有ST,则要求用户去CAS获取ST。用户向CAS发出获取ST的请求,如果用户的请求中包含cookie,则CAS会以此cookie值为key查询缓存中有无TGT,如果存在TGT,则用此TGT签发一个ST,返回给用户。用户凭借ST去访问service,service拿ST去CAS验证,验证通过后,允许用户访问资源。
为了保证ST的安全性,其生产机制是随机的,没有规律性。而且,CAS规定 ST 只能存活一定的时间,然后 CAS Server 会让它失效。而且CAS 协议规定ST只能使用一次,无论 Service Ticket 验证是否成功, CASServer 都会清除服务端缓存中的该 Ticket ,从而可以确保一个 Service Ticket 不被使用两次
服务端搭建
前面说到CAS是基于身份票据的,也就是集中验证模式,我们需要搭建一个身份验证服务器。
下面的演示案例,是在本地搭建一个CAS服务器。
使用的开发工具及版本需求:
-
IDEA
-
Maven3.3.9+
-
JDK1.8+
-
tomcat8.0+
-
CAS 5.3
下载
地址:https://github.com/apereo/cas-overlay-template/,选择5.3,下载zip
创建项目
解压上一步的zip文件, 然后新建项目引入:
配置
终端执行命令: mvn install
创建服务器tomcat配置
测试
启动项目,会弹出CAS登录页,表明搭建成功:
默认用户密码:
启用HTTPS
为正常使用CAS,需启用HTTPS安全协议。
证书
启用HTTPS,需要使用证书,这里使用java自带的keytool工具,生成本地证书,供开发测试使用.
-
创建证书
keytool -genkey -alias ssodemo -keyalg RSA -keysize 1024 -keypass 123456 -validity 365 -keystore ssodemo.keystore -storepass 123456
注意这里的第一条设置,必须在本地host中配置对应条目:
-
导出证书
keytool -export -alias ssodemo -keystore ssodemo.keystore -file ssodemo.crt -storepass 123456
-
导入JDK
keytool -import -alias ssodemo -file ssodemo.crt -storepass changeit -keystore .../jre/lib/security/cacerts
注意这里要导入的目标文件是JDK下的cacerts,路径请根据自己实际路径修改
配置tomcat
修改tomcat安装目录下conf/server.xml文件:
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="150" SSLEnabled="true" scheme="https"
secure="true" clientAuth="false" sslProtocol="TLS"
keystoreFile="/Users/startong/Documents/CAS/ssodemo.keystore"
keystorePass="123456"/>
大概在八九十行:
配置项目
与tomcat端口一致:
启动项目测试:
密码存储-数据库
前面搭建好的项目中,只有一个默认的用户、密码,正常使用时,肯定是要有个地方存储的,这里先展示使用数据库存储的案例。
前置配置
搭建数据库,我这里使用使用MySql.
搭建过程略,我的MySql中,已建有数据库hzero_platform,表iam_user,用户名、密码:
修改项目配置
-
完善目录
-
将overlays中的application.properties拷贝过来
-
修改src/main/resources/application.propertis:
#cas.authn.accept.users=casuser::Mellon cas.authn.jdbc.query[0].url=jdbc:mysql://server.strive.com:3306/hzero_platform cas.authn.jdbc.query[0].user=hzero cas.authn.jdbc.query[0].password=hzero cas.authn.jdbc.query[0].sql=SELECT hash_password FROM iam_user WHERE login_name=upper(?) #下面这个字段很关键,对应的是数据库中的字段,和上面的sql结果中的字段保持一致 cas.authn.jdbc.query[0].fieldPassword=hash_password cas.authn.jdbc.query[0].driverClass=com.mysql.jdbc.Driver
url: 数据库连接地址
user: 数据库登录用户
password: 数据库登录密码
sql: 查询用户密码的语句
-
添加依赖
<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.47</version> </dependency>
-
启动项目
这里的用户、密码,与数据库中一致,密码加密,后续会有介绍。
客户端(EBS)搭建
这里使用EBS应用,作为客户端,版本为12.2.9
下载
可在maven官网下载cas-client-core:
这里选用3.2.x版本,可以测试通过。更高的3.3+版本好像不支持,可以自行测试使用。
上传
解压cas-client-core-3.2.2.jar,将META-INF 和 org文件夹上传到EBS的$OA_HTML/WEB-INF/classes。
配置template
参考官网
Using AutoConfig to Manage System Configurations in Oracle E-Business Suite Release 12 (ID 387859.1),章节 : 4.2. Implementing AutoConfig Customizations
第三小节: Customizing an AutoConfig template file delivered by Oracle
-
在EBS应用服务器$FND_TOP/admin/template下,创建custom文件夹
-
拷贝$FND_TOP/admin/template/oacore_web_xml_FMW.tmp至custom
-
修改custom/oacore_web_xml_FMW.tmp
-
在context-param配置下面添加客制化过滤器,位置一定不能错:
-
过滤器内容:
<!-- added by star.tong 2020-12-16 for cas sso begin --> <!-- ======================== 单点登录开始 ======================== --> <!-- 配置单点退出。如果此处不配置,不能单点退出,并且要放到所有过滤器的最前面 --> <listener> <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class> </listener> <filter> <filter-name>SingleSignOutFilter</filter-name> <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class> </filter> <filter-mapping> <filter-name>SingleSignOutFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!--该过滤器负责用户的认证工作,必须启用它 --> <filter> <filter-name>CASFilter</filter-name> <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class> <!-- CAS服务端登录地址 --> <init-param> <param-name>casServerLoginUrl</param-name> <param-value>https://real.strive.com:8443/cas/login</param-value> </init-param> <!-- EBS登录地址 --> <init-param> <param-name>serverName</param-name> <param-value>apptest.strive.com:8000</param-value> </init-param> </filter> <filter-mapping> <filter-name>CASFilter</filter-name> <url-pattern>/AppsLocalLogin.jsp</url-pattern> </filter-mapping> <!--该过滤器负责对Ticket的校验工作,必须启用它 --> <filter> <filter-name>CAS Validation Filter</filter-name> <filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class> <!-- CAS服务端登录地址 --> <init-param> <param-name>casServerUrlPrefix</param-name> <param-value>https://real.strive.com:8443/cas/</param-value> </init-param> <!-- EBS登录地址 --> <init-param> <param-name>serverName</param-name> <param-value>apptest.strive.com:8000</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Validation Filter</filter-name> <url-pattern>/AppsLocalLogin.jsp</url-pattern> </filter-mapping> <!-- 该过滤器负责实现HttpServletRequest请求的包裹, 比如允许开发者通过HttpServletRequest的getRemoteUser()方法获得SSO登录用户的登录名,可选配置。 --> <filter> <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name> <filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class> </filter> <filter-mapping> <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name> <url-pattern>/AppsLocalLogin.jsp</url-pattern> </filter-mapping> <filter> <filter-name>CAS Assertion Thread Local Filter</filter-name> <filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class> </filter> <filter-mapping> <filter-name>CAS Assertion Thread Local Filter</filter-name> <url-pattern>/AppsLocalLogin.jsp</url-pattern> </filter-mapping> <!-- added by star.tong 2020-12-16 for cas sso end -->
-
-
-
在servlet(AuthenticateUser)下面添加客制化登录验证过滤器:
<!-- Customer for ad login ebs begin --> <filter> <filter-name>ValidUserFilter</filter-name> <filter-class>hand.oracle.apps.ad.ValidUserFilter</filter-class> </filter> <filter-mapping> <filter-name>ValidUserFilter</filter-name> <url-pattern>/AppsLocalLogin.jsp</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> <dispatcher>INCLUDE</dispatcher> </filter-mapping> <!-- Customer for ad login ebs end -->
-
安装客制化代码
- 将java代码文件夹hand上传到$OA_HTML/WEB-INF/classes
- 安装客制化程序包cux_ad_login_pkg
导入证书
-
找到JDK路径:cat $CONTEXT_FILE | grep -i s_fmw_jdktop
-
上传ssodemo.crt文件至EBS服务器
-
导入证书
keytool -import -alias ssodemo -file ssodemo.crt -storepass changeit -keystore <jdk_path>/jre/lib/security/cacerts
修改登出
-
在$OA_HTME下创建登出页面cux_fnd_sso_logout.html:
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> </head> <script> hostname = window.location.host; window.location.href = "https://real.strive.com:8443/cas"; </script> <body id="cas"> <div class="alert alert-success"> <br> <p>单点登出跳转页面,请<a href="https://real.strive.com:8443/cas"> <font size="3" color="red"><b>登出</b></font> </a></p> </div> <div class="foot" style="margin-top: 50px; width: 100%; text-align: center; position: fixed; bottom: 20px; left: 0px;"> <div class="clearfloat"></div> <div class="copyright" style="width: 100%"></div> </div> </body> </html>
-
修改$OA_HTML/OALogout.jsp(记得备份)
response.sendRedirect("http://apptest.strive.com:8000/OA_HTML/cux_fnd_sso_logout.html");
注释原来的跳转,添加新的跳转至上一步创建的页面。
不要直接跳转到CAS服务端地址,要通过EBS自己的页面,否则会跳转失败。
生效配置
-
添加证书host配置:
-
编译OALogout.jsp
$FND_TOP/patch/115/bin/ojspCompile.pl --compile --flush -s OALogout.jsp
-
执行AutoConfig
-
重启oacore
至此完成修改,再次登录EBS地址时,会跳转到CAS服务器端,登录后自动跳回EBS系统!
启用HTTP
添加依赖
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-support-json-service-registry</artifactId>
<version>${cas.version}</version>
</dependency>
添加文件
拷贝overlay下的services文件及,到resource下:
修改第二个.json文件,添加http:
修改application.properties文件,添加:
###### 启用HTTP
#此配置不同于5.1,改了个名字
cas.serviceRegistry.json.location=classpath:/services
#让cas的票据安全性设置为false,控制是否在安全的情况下在浏览器中传递票据,
#如果不设置false,那么就无法做到多个应用只登陆一次的效果。
cas.tgc.secure=false