本文共 15392 字,大约阅读时间需要 51 分钟。
sso :http://blog.csdn.net/q383965374/article/category/6692471
什么是单点登录(SSO)
单点登录主要用于多系统集成,即在多个系统中,用户只需要到一个中央服务器登录一次即可访问这些系统中的任何一个,无须多次登录。
单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。
目前已经有了成熟的单点登录实现方案,比如CAS,我们只要在web系统中应用单点登录方案CAS即可。(主要涉及到注册登录验证等模块的改动)
CAS (Central Authentication Service) 是耶鲁 Yale 大学发起的一个java开源项目,旨在为 Web应用系统提供一种可靠的 单点登录 解决方案( Web SSO ), CAS 具有以下特点:
1、 开源的企业级单点登录解决方案;
2、 CAS Server 为需要独立部署的 Web 应用----一个独立的Web应用程序(cas.war)。 ;
3、 CAS Client 支持非常多的客户端 ( 指单点登录系统中的各个 Web 应用 ) ,包括 Java, .Net, PHP, Perl, 等。
CAS在2004年12月成为Jasig项目,所以也叫JA-SIG CAS。
官网1: 官网2:
论坛:
github:
开发者快捷资源:
下载链接:开发文档:
(后面代码小节会详细介绍,这里初步了解即可)
TGC(ticket-granting cookie)-- ------- 授权的票据证明,由 CAS Server 通过 SSL 方式发送给终端用户;
KDC( Key Distribution Center ) ---------- 密钥发放中心;
ST (Service ticket) --------- 服务票据, 由 KDC 的 TGS 发放, ST 只能被尝试验证一次。 任何一台 Workstation 都需要拥有一张有效的 Service Ticket 才能访问域内部的应用 (Applications) 。 如果能正确接收 Service Ticket ,说明在 CASClient-CASServer 之间的信任关系已经被正确建立起来,通常为一张数字加密的证书;
Ticket Granting tieckt(TGT) --------- 票据授权票据,由 KDC 的 AS 发放。即获取这样一张票据后,以后申请各种其他服务票据 (ST) 便不必再向 KDC 提交 Credentials (凭证或身份认证信息 ) ;
Authentication Service (AS) --------- 认证服务,索取 Crendential ,发放 TGT ;
Ticket-Granting Service (TGS) --------- 票据授权服务,索取 TGT ,发放 ST 。
下图是 CAS 最基础协议:
1 、 CAS Client 与受保护的客户端应用部署在一起,以 Filter 方式保护受保护的资源。对于访问受保护资源的每个 Web 请求, CAS Client 会分析该请求的 Http 请求中是否包含 Service Ticket ( ST )和 Ticket Granting tieckt(TGT) ,如果没有,则说明当前用户尚未登录,于是将请求重定向到指定好的 CAS Server 登录地址,并传递 Service (也就是要访问的目的资源地址),以便登录成功过后转回该地址。用户在第 3 步中输入认证信息,如果登录成功, CAS Server 随机产生一个相当长度、唯一、不可伪造的 Service Ticket ,并缓存以待将来验证,之后系统自动重定向到 Service 所在地址,并为客户端浏览器设置一个 Ticket Granted Cookie ( TGC ), CAS Client 在拿到 Service 和新产生的 Ticket 过后,在第 5 , 6 步中与 CAS Server 进行身份核实,以确保 Service Ticket 的合法性。
2 、在该协议中,所有与 CAS 的交互均采用 SSL 协议确保 ST 和 TGC 的安全性。协议工作过程中会有 2 次重定向的过程,但是 CAS Client 与 CAS Server 之间进行 Ticket 验证的过程对于用户是透明的。
3 、 CAS 如何实现 SSO
当用户访问另一服务再次被重定向到 CAS Server 的时候, CAS Server 会主动获到这个 TGC cookie ,然后做下面的事情:
1) 如果 User 的持有 TGC 且其还没失效,那么就走基础协议图的 Step4 ,达到了 SSO 的效果;
2) 如果 TGC 失效,那么用户还是要重新认证 ( 走基础协议图的 Step3) 。
另外,CAS 协议中还提供了 Proxy (代理)模式,以适应更加高级、复杂的应用场景,具体介绍可以参考 CAS 官方网站上的相关文档。
1. 用户浏览受系统保护的URL。 2. CAS Client服务端收到请求,Filter拦截该请求,在Filter中判断该用户是否已经登陆,如果已经登陆,就直接进入系统,否则,将请求转发到CAS Server服务端的LoginURL。 3. 在LoginURL中会获取到用户的Cookie,检验用户是否已经在其他相关使用SSO的系统登陆成功。如果已经在其他的系统登陆了,则将请求转回 CAS Client,并且带回一个ticket, CAS Client再次发送请求到ValidateURL。否则,系统提示用户输入ID和PASSWORD。 4. 提交后请求到ValidateURL,CAS Server验证ticket的有效性。然后返回结果给CAS Client。如果ticket有效,则CAS Client应让该用户浏览受保护的资源。否则,重定向到登陆页面,提示用户输入ID和PASSWORD。 5. 校验ID和Password是否匹配。如不匹配,再次要求用户输入ID和PASSWORD。否则,CAS Server记录用户登陆成功。并向浏览器回送Cookie,记录用户已经登陆成功。如果浏览器不支持Cookie,则无法实现单点登陆。
用户第一次访问一个CAS 服务的客户web 应用时(例如访问URL :http://192.168.1.90:8081/web1 ),部署在客户web 应用的cas AuthenticationFilter ,会截获此请求,生成service 参数,然后redirect 到CAS 服务的login 接口,url为https://cas:8443/cas/login?service=http%3A%2F%2F192.168.1.90%3A8081%2Fweb1%2F ,认证成功后,CAS 服务器会生成认证cookie ,写入浏览器,同时将cookie 缓存到服务器本地,CAS 服务器还会根据service 参数生成ticket,ticket 会保存到服务器,也会加在url 后面,然后将请求redirect 回客户web 应用,url 为http://192.168.1.90:8081/web1/?ticket=ST-5-Sx6eyvj7cPPCfn0pMZuMwnbMvxpCBcNAIi6-20 。
这时客户端的AuthenticationFilter 看到ticket 参数后,会跳过,由其后面的TicketValidationFilter 处理,TicketValidationFilter 会利用httpclient 工具访问cas 服务的/serviceValidate 接口, 将ticket 、service 都传到此接口,由此接口验证ticket 的有效性,TicketValidationFilter 如果得到验证成功的消息,就会把用户信息写入web 应用的session里。
至此为止,SSO 会话就建立起来了,以后用户在同一浏览器里访问此web 应用时,AuthenticationFilter 会在session 里读取到用户信息,所以就不会去CAS 认证,如果在此浏览器里访问别的web 应用时,AuthenticationFilter 在session 里读取不到用户信息,会去CAS 的login 接口认证,但这时CAS 会读取到浏览器传来的cookie ,所以CAS 不会要求用户去登录页面登录,只是会根据service 参数生成一个ticket ,然后再和web 应用做一个验证ticket 的交互。
知道工作原理和流程之后我们来看看应该怎么在代码上实现CAS
一般来说,我们写Web应用只需要熟悉这几个Filter就可以了,如果不需要https连接,连第一个也不用熟悉。但是有人肯定会想,这些 Filter怎么和我的数据库联系起来呢?不用着急,这些Filter并不直接处理用户的认证,也不直接处理用户的授权,而是把它们交给了认证管理器和决 策管理器。
对于这两种管理器,那也是不需要我们写代码的,Acegi也提供了现成的类。那么大家又奇怪了:又是现成的,那怎么和我的数据库关联起来呢?别 着急,其实这两个管理器自己也不做事,认证管理器把任务交给了Provider,而决策管理器则把任务交给了Voter,如下图:
现在我要告诉你们,这里的Provider和Voter也是不需要我们写代码的。不要崩溃,快到目标了。Acegi提供了多个Provider 的实现类,也就是说CAS的认证方式有很多种,我们可以到数据库检索一条用户帐号信息,也可能在 XML 文件中检索用户密码, 对于很多种方式,CAS 均提供一种灵活但同一的接口 / 实现分离的方式, CAS 究竟是用何种认证方式,可以由我们自己来决定,也就是,这个认证的实现细节可以自己定制和扩展。例如如果我们想用数据库来储存用户的认证数据,那么我们就选择DaoAuthenticationProvider。对于Voter,我们一般选择 RoleVoter就够用了,它会根据我们配置文件中的设置来决定是否允许某一个用户访问制定的Web资源。
而DaoAuthenticationProvider也是不直接操作数据库的,它把任务委托给了UserDetailService,如下图:
而我们要做的,就是实现这个UserDetailService。说了这么多总算是引出了我们开发中的关键,那就是我们要实现自己的UserDetailService,它就是连接我们的数据库和Acegi的桥梁。UserDetailService的要求也很简 单,只需要一个返回org.springframework.security.userdetails.User对象的 loadUserByUsername(String userName)方法。因此,怎么设计数据库都可以,不管我们是用一个表还是两个表还是三个表,也不管我们是用户-授权,还是用户-角色-授权,还是用 户-用户组-角色-授权,这些具体的东西Acegi统统不关心,它只关心返回的那个User对象,至于怎么从数据库中读取数据,那就是我们自己的事了。
反过来再看看上面的过程,我们发现,即使我们要做的只是实现自己的UserDetailService类,但是我们不得不在Spring中配置那一 大堆的Bean,包括几个Filter,几个Manager,几个Provider和Voter,而这些配置往往都是重复的无谓的。好在Acegi 2.0也认识到了这个问题,所以,它设计了一个<http>标签,让Acegi的配置得到了简化。
在后面具体的部署实施中我们再来详细讲这些细节。
CAS Ticket类图 如下:
TicketGrantingTicket 的 grantServiceTicket方法 方法声明:public synchronized ServiceTicket grantServiceTicket(final String id,final Service service, final ExpirationPolicy expirationPolicy, final boolean credentialsProvided) 方法描述: 1:生成SerivceTicketImpl 2:更新属性: this.previousLastTimeUsed = this.lastTimeUsed; this.lastTimeUsed = System.currentTimeMillis(); this.countOfUses++; 3:给service对象的principal属性赋值 4:将service对象放入map services ServiceTicket 的 grantTicketGrantingTicket方法
方法声明: public TicketGrantingTicket grantTicketGrantingTicket(final String id, final Authentication authentication,final ExpirationPolicy expirationPolicy) 方法描述:在CAS3.3对CAS2.0协议的实现中,PGT是由ST签发的,调用的就是ServiceTicket的grantTicketGrantingTicket方法。方法返回的TicketGrantingTicket对象,表征的是一个PGT对象,其中的ticketGrantingTicket属性的值是签发ST的TGT对象。 TicketGrantingTicket 的 expire方法 方法声明:void expire() 方法描述: 在CAS的logout接口实现中,要调用TGT对象的expire方法,然后会在缓存中清除此TGT对象。 expire方法的内容:循环遍历 services 中的Service对象,调用其logoutOfService方法。具体Service实现类中的logoutOfService方法的实现,要通知具体的应用,客户要退出。
CAS 服务端总共对外定义了9 个接口,客户端通过访问这9 个接口与服务端交互,这9个接口为:
接口 | 说明 | 备注 |
/login | 认证接口 | |
/logout | 退出接口,负责销毁认证cookie | |
/validate | 验证ticket 用的接口,CAS1.0 定义 | |
/serviceValidate | 验证ticket 用的接口,CAS2.0 定义,返回xml 格式的数据 | |
/proxy | 支持代理认证功能的接口 | |
/proxyValidate | 支持代理认证功能的接口 | |
/CentralAuthenticationService | 用于和远程的web services 交互 | |
/remoteLogin(新增) | 认证接口 | |
/directLogin(新增) | 认证接口 |
CAS 默认的登录处理流程图如下:
说明:
1 : InitialFlowSetupAction: 是流程的入口。用 request.getContextPath() 的值来设置 cookie 的 Path 值, Cookie 的 path 值是在配置文件里定义的,但这个 Action 负责将 request.getContextPath() 的值设置为 Cookie 的 path 值,这是在 cas 部署环境改变的情况下,灵活地设置 cookie path 的方式;把 cookie 的值以及 service 参数的值放入 requestContext 的 flowscope 里。
2 : GenerateServiceTicketAction 此 Action 负责根据 service 、 GTC cookie 值生成 ServiceTicket 对象, ServiceTicket 的 ID 就是返回给客户应用的 ticket 参数,如果成功创建 ServiceTicket ,则转发到 WarnAction ,如果创建失败,且 gateway 参数为 true ,则直接redirect 到客户应用, 否则则需要重新认证。
3 : viewLoginForm 这是登录页面, CAS 在此收集用户凭证。 CAS 提供的默认实现是 /WEB-INF/view/jsp/simple/ui/casLoginView.jsp 。
4 : bindAndValidate 对应 AuthenticationViaFormAction 的 doBind 方法,该方法负责搜集登录页面上用户录入的凭证信息(用户名、密码等),然后把这些信息封装到 CAS 内部的 Credentials 对象中。用户在 casLoginView.jsp 页面上点击提交后,会触发此方法。
5:submit 对应 AuthenticationViaFormAction 的 submit 方法 , 如果 doBind 方法成功执行完, 则触发 submit 方法,此方法负责调用centralAuthenticationService 的 grantServiceTicket 方法,完成认证工作,如果认证成功,则生成 TicketGrantingTicket 对象,放在缓存里, TicketGrantingTicket 的 ID 就是 TGC Cookie 的 value 值。
6 : warn CAS 提供了一个功能:用户在一个 web 应用中跳到另一个 web 应用时, CAS 可以跳转到一个提示页面,该页面提示用户要离开一个应用进入另一个应用,可以让用户自己选择。用户在登录页面 viewLoginForm 上选中了 id=”warn” 的复选框,才能开启这个功能。
WarnAction 就检查用户有没有开启这个功能,如果开启了,则转发到showWarnView, 如果没开启,则直接redirect 到客户应用。
7 :SendTicketGrantingTicketAction 此Action 负责为response 生成TGC Cookie ,cookie 的值就是 AuthenticationViaFormAction 的submit 方法生成的 TicketGrantingTicket 对象的 ID 。
8 : viewGenerateLoginSuccess 这是 CAS 的认证成功页面。
第一次访问Web 应用的流程走向图如下:
已经登录web1 后,访问web1 的资源(web1 没有启动session ),或访问web2 的资源走向图如下:
( 对应实现类 org.jasig.cas.web.LogoutController )
处理逻辑: 1) removeCookie 2) 在服务端删除TicketGrantingTicket 对象(此对象封装了cookie 的value 值) 3 ) redirect 到退出页面,有2 种选择: if(LogoutController 的followServiceRedirects 属性为true 值,且url 里的service 参数非空){ redirect 到 sevice 参数标识的url } else{ redirect 到内置的casLogoutView (cas/WEB-INF/view/jsp/default/ui/casLogoutView.jsp ),如果url 里有url 参数,则此url 参数标识的链接会显示在casLogoutView 页面上。 }(对应实现类 org.jasig.cas.web.ServiceValidateController ) 处理逻辑: 如果service 参数为空或ticket 参数为空,则转发到failureView (/WEB-INF/view/jsp/default/protocol/2.0/casServiceValidationFailure.jsp ) 验证ticket 。以ticket 为参数,去缓存里找ServiceTicketImpl 对象,如果能找到,且没有过期,且ServiceTicketImpl 对象对应的service 属性和service 参数对应,则验证通过,验证通过后,请求转发至casServiceSuccessView (cas/WEB-INF/view/jsp/default/protocol/2.0/casServiceValidationSuccess.jsp ),验证不通过,则转发到failureView 。
参数 | 是否必须 | 说明 |
com.olymtech.cas.client.filter.loginUrl | 是 | 指定 CAS 提供登录页面的 URL |
com.olymtech.cas.client.filter.validateUrl | 是 | 指定 CAS 提供 service ticket 或 proxy ticket 验证服务的 URL |
com.olymtech.cas.client.filter.serverName | 是 | 指定客户端的域名和端口,是指客户端应用所在机器而不是 CAS Server 所在机器,该参数或 serviceUrl 至少有一个必须指定 |
com.olymtech.cas.client.filter.serviceUrl | 否 | 该参数指定过后将覆盖 serverName 参数,成为登录成功过后重定向的目的地址 |
com.olymtech.cas.client.filter.wrapRequest | 否 | 如果指定为 true,那么 CASFilter 将重新包装 HttpRequest,并且使 getRemoteUser() 方法返回当前登录用户的用户名 |
com.olymtech.cas.client.filter.noFilter | 否 | 设置不过滤的路径,语法如下:/name1/,/name2, |
com.olymtech.cas.client.filter.proxyCallbackUrl | 否 | 用于当前应用需要作为其他服务的代理(proxy)时获取 Proxy Granting Ticket 的地址 |
com.olymtech.cas.client.filter.authorizedProxy | 否 | 用于允许当前应用从代理处获取 proxy tickets,该参数接受以空格分隔开的多个 proxy URLs,但实际使用只需要一个成功即可。当指定该参数过后,需要修改 validateUrl 到 proxyValidate,而不再是 serviceValidate |
com.olymtech.cas.client.filter.renew | 否 | 如果指定为 true,那么受保护的资源每次被访问时均要求用户重新进行验证,而不管之前是否已经通过 |
com.olymtech.cas.client.filter.gateway | 否 | 指定 gateway 属性 |
CAS 的安全性从 v1 到 v3 ,都很依赖于 SSL ,所有与 CAS 的交互均采用 SSL 协议,确保,ST 和 TGC 的传输安全性。
通过上面的学习我们已经对单点登录以及CAS有了一定的了解,那么我们怎么入手搭建集群的单点登录呢?
步骤如下
首先我们肯定是要下载cas server的代码,然后进行用户名密码验证认证方式的选型,选型结束后进行相应配置以及登录界面的修改等等。
然后把cas server的war包发布到服务器上,运行即可。
cas server搭建完成后登录认证模块已经有了,那么怎么跟我们的web项目关联起来呢? 这就需要在web 项目中集成cas的client客户端了,集成了之后,对cas client的配置文件进行配置,对应到cas server的地址,这样它们就能联系起来了。
我们之前已经说过了cas支持很多种认证方式,我们即可把用户名密码写在xml中,然后cas来读取之后跟用户输入的帐号密码对比,也可以把用户名密码存在数据库中。
目前cas提供给了以下认证方式:
GENERIC
JAAS
JDBC--关系型数据库
MongoDB--非关系数据库
LDAP LEGACY RADIUS SPNEGO TRUSTED X509 OAUTH OPENID 在cas server的默认配置中,可以修改认证管理器中的认证处理器属性链表,增加或者修改相应的认证方式。有些认证方式对于web开发,基本上不太好用或者不常用,因为他们的配置太不灵活或者太专业化。在此之所以列举了那么多关于cas的认证处理器,主要为了让大家知道,cas支持这种认证方式。比如现在新浪微博就对外开通了oauth认证接口,如果哪一天公司进行战略转形,希望用cas接新浪微博账号认证,那么就可以直接在此基础上进行好好研究。
一般LDAP和数据库比较常用,我们接下来会着重讲这两种的运用。