cas登录如何传用户名密码
A. 关于 CAS单点登录的不同数据库的问题,谢谢
难道要判断用户输入的用户名和密码是否正确还要每个数据库都验证一遍?
每套系统传递用户和密码给CAS的时候再带一个参数(表示哪一个系统), 这样就可以根据这个来连接指定的数据库去验证了
B. 在cas server中登录后怎么取得当前的用户名
多个web客户端应用,那么每个地方都做个注册功能,还是所有客户端都统一到某一个客户注册功能。
这两种方式都可以,根据你的需要,如果所有系统的注册数据都一样的话就统一到一个客户注册功能
C. 在cas server中登录后怎么取得当前的用户名
在server的org.jasig.cas.web.flow.AuthenticationViaFormAction类的doBind方法中
java代码
UsernamePasswordCredentials credentials = (UsernamePasswordCredentials) binder.getTarget();
credentials里就有用户名
在cas client上注册时,把用户和密码再同步到cas server数据库中
就是这个意思。
D. cas自定义登录页面的提交按钮要怎么改
1. 动机
用过 CAS 的人都知道 CAS-Server端是单独部署的,作为一个纯粹的认证中心。在用户每次登录时,都需要进入CAS-Server的登录页填写用户名和密码登录,但是如果存在多个子应用系统时,它们可能都有相应风格的登录页面,我们希望直接在子系统中登录成功,而不是每次都要跳转到CAS的登录页去登录。
2. 开始分析问题
其实仔细想一想,为什么不能直接在子系统中将参数提交至 cas/login 进行登录呢? 于是便找到了CAS在登录认证时主要参数说明:
service [OPTIONAL] 登录成功后重定向的URL地址;
username [REQUIRED] 登录用户名;
password [REQUIRED] 登录密码;
lt [REQUIRED] 登录令牌;
主要有四个参数,其中的三个参数倒好说,最关键的就是 lt , 据官方说明该参数是login ticket id, 主要是在登录前产生的一个唯一的“登录门票”,然后提交登录后会先取得"门票",确定其有效性后才进行用户名和密码的校验,否则直接重定向至 cas/login 页。
于是,便打开CAS-Server的登录页,发现其每次刷新都会产生一个 lt, 其实就是 Spring WebFlow 中的 flowExecutionKey值。 那么问题的关键就在于在子系统中如何获取 lt 也就是登录的ticket?
3. 可能的解决方案
一般对于获取登录ticket的解决方案可能大多数人都会提到两种方法:
AJAX: 熟悉 Ajax 的可能都知道,它的请求方式是严格按照沙箱安全模型机制的,严格情况下会存在跨域安全问题。
IFrames: 这也是早期的 ajax 实现方式,在页面中嵌入一个隐藏的IFrame,然后通过表单提交到该iframe来实现不刷新提交,不过使用这种方式同样会带来两个问题:
a. 登录成功之后如何摆脱登录后的IFrame呢?如果成功登录可能会导致整个页面重定向,当然你能在form中使
用属性target="_parent",使之弹出,那么你如何在父页面显示错误信息呢?
b. 你可能会受到布局的限止(不允许或不支持iframe)
- 对于以上两种方案,并非说不能实现,只是说对于一个灵活的登录系统来说仍然还是会存在一定的局限性的,我们坚信能有更好的方案来解决这个问题。
- 4. 通过JS重定向来获取login ticket (lt)
- 当第一次进入子系统的登录页时,通过 JS 进行redirect到cas/login?get-lt=true获取login ticket,然后在该login中的 flow 中检查是否包含get-lt=true的参数,如果是的话则跳转到lt生成页,生成后,并将lt作为该redirect url 中的参数连接,如 remote-login.html?lt=e1s1,然后子系统再通过JS解析当前URL并从参数中取得该lt的值放置登录表单中,即完成 lt 的获取工作。其中进行了两次 redirect 的操作。
- 5. 开始实践
- 首先,在我们的子系统中应该有一个登录页面,通过输入用户名和密码提交至cas认证中心。不过前提是先要获取到 login tickt id. 也就是说当用户第一次进入子系统的登录页面时,在该页面中会通过js跳转到 cas/login 中的获取login ticket. 在 cas/login 的 flow 中先会判断请求的参数中是否包含了 get-lt 的参数。
- 在cas的 login flow 中加入 ProvideLoginTicketAction 的流,主要用于判断该请求是否是来获取 lt,在cas-server端声明获取 login ticket action 类:
- com.denger.sso.web.ProvideLoginTicketAction
E. 在cas server中登录后怎么取得当前的用户名
在client中可以在session中取得用户名
CASReceipt receipt = (CASReceipt )session.getAttribute(CASFilter.CAS_FILTER_RECEIPT);
或者
CASReceipt receipt = (CASReceipt )session.getAttribute("e.yale.its.tp.cas.client.filter.receipt");
注册这个功能(还有包括修改密码)放到client中,server端只做认证
F. CAS单点登录原理分析(一)
一,业务分析
在分布式系统架构中,假设把上述的三个子系统部署在三个不同的服务器上。前提是用户登录之后才能访问这些子系统。那么使用传统方式,可能会存在这样的问题:
1.当访问用户中心,需要用户登录帐号
2.当访问购物车,还需要用户登录帐号
3.当访问商品结算,又一次需要用户登录帐号
访问每一个子系统都需要用户登录帐号,这样的体验对于用户来说是极差。而使用单点登录就可以很好地解决上述的问题。
二,单点登录
单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。SSO 的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。
我们目前的系统存在诸多子系统,而这些子系统是分别部署在不同的服务器中,那么使用传统方式的 session 是无法解决的,我们需要使用相关的单点登录技术来解决。
第一步 :用户访问应用系统1。过滤器判断用户是否登录,没有登录,则重定向(302)到认证系统去进行认证操作。
第二步 :重定向到认证系统,显示登录界面,用户输入用户名密码。认证系统将用户登录的信息记录到服务器的session中。
第三步 :认证系统给浏览器发送一个特殊的凭证ticket,浏览器将凭证交给应用系统1,应用系统1则拿着浏览器交给他的凭证ticket去认证系统验证凭证ticket是否有效。凭证ticket若是有效,将用户信息保存到应用系统1的session中一份,并告知应用系统1,用户通过认证。
第四步 :用户通过认证,浏览器与网站之间进行正常的访问。
第五步 :当用户再次访问应用系统1,由于应用系统1的session中有用户信息,所以就不用经过认证系统认证,就可以直接访问应用系统1了。
第六步 :当用户再去访问其他应用系统时,浏览器会带着凭证ticket过去,其他应用系统到认证系统验证凭证,凭证ticket若是有效,将用户信息保存到其他应用系统的session中一份,并告知其他应用系统,用户通过认证。
第七步 :用户通过认证,浏览器与网站之间进行正常的访问。
第八步 :当用户再次访问其他应用系统,由于其他应用系统的session中有用户信息,所以就不用经过认证系统认证,就可以直接访问其他应用系统了。
三、Yelu大学研发的CAS(Central Authentication Server)
1.什么是CAS?
CAS 是 Yale 大学发起的一个开源项目,旨在为 Web 应用系统提供一种可靠的单点登录方法,CAS 在 2004 年 12 月正式成为 JA-SIG 的一个项目。CAS 具有以下特点:
【1】开源的企业级单点登录解决方案。
【2】CAS Server 为需要独立部署的 Web 应用。这个CAS框架已经提供
【3】CAS Client 支持非常多的客户端(这里指单点登录系统中的各个 Web 应用),包括Java, .Net, PHP, Perl, Apache, uPortal, Ruby 等。
从结构上看,CAS 包含两个部分: CAS Server 和 CAS Client。CAS Server 需要独立部署,主要负责对用户的认证工作;CAS Client 负责处理对客户端受保护资源的访问请求,需要登录时,重定向到 CAS Server。下图是 CAS 最基本的协议过程:
2.CAS的详细登录流程
该图主要描述
1.第一次访问http://shopping.xiaogui.com
2.在登录状态下第二次访问http://shopping.xiaogui.com
3.在登录状态下第一次访问http://pay.xiaogui.com
下面对图中序号代表的操作进行说明
当用户第一次访问http://shopping.xiaogui.com
序号1: 用户请求http://shopping.xiaogui.com,会经过AuthenticationFilter认证过滤器(在cas client 的web.xml中配置)
主要作用:判断是否登录,如果没有登录则重定向到认证中心。
大概知道这个就行,CAS的具体实现会在以后的博客中写道
序号2: AuthenticationFilter发现用户没有登录,则返回浏览器重定向地址。
重定向的地址就是认证服务器CAS Server的地址,后面的参数是我们请求的客户端地址,这个参数目的就是为了认证成功以后,根据这个参数的地址重定向回请求的客户端
序号3: 浏览器根据响应回来的重定向地址,向cas.xiaogui.com认证系统发出请求
序号4: 认证系统cas.xiaogui.com接收请求,响应登陆页面
序号5: :用户登陆页面输入用户名密码,提交请求
序号6: :CAS Server 认证服务器接收用户名和密码,就行验证,验证逻辑CAS Server 已经实现,并响应给浏览器信息
这里的用户名,密码不需要关心,后续会讲到
图中1,2部分表示序号5 输入的用户名,密码,以及发出的请求。当认证服务器验证通过之后,根据请求参数service的值,进行重定向,其实就是回到了请求的客户端,同时会携带一个ticket令牌参数。同时会在Cookie中设置一个TGC,该cookie是网站认证系统cas.xiaogui.com的cookie,只有访问这个网站才会携带这个cookie过去。
*****注意:这个携带TGC的Cookie是实现CAS单点登录的关键所在!
Cookie中的TGC:向cookie中添加该值的目的是当下次访问cas.xiaogui.com认证系统时,浏览器将Cookie中的TGC携带到服务器,服务器根据这个TGC,查找与之对应的TGT。从而判断用户是否登录过了,是否需要展示登录页面。TGT与TGC的关系就像SESSION与Cookie中SESSIONID的关系。
TGT:Ticket Granted Ticket(俗称大令牌,或者说票根,他可以签发ST)
TGC:Ticket Granted Cookie(cookie中的value),存在Cookie中,根据他可以找到TGT。
ST:Service Ticket (小令牌),是TGT生成的,默认是用一次就生效了。也就是上面数字3处的ticket值。
序号7: 客户端拿到请求中的ticket信息,也就是图中1的位置
然后经过一个ticket过滤器,去认证系统CAS Server判断ticket是否有效
这个过滤器的主要工作就是校验客户端传过来的ticket是否有效
CAS Client 客户端 shopping.xiaogui.com 中web.xml的配置
序号8: 向CAS Server认证系统发出验证ticket的请求,也就是图中2的位置,然后执行ticket验证
序号9: 通过校验之后,把用户信息保存到客户端的session中,并把客户端的SessionID设置在Cookie中,同时告知客户端ticket有效。当用户再次访问该客户端,就可以根据Cookie 中的SessionID找到客户端的Session,获取用户信息,就不用再次进行验证了。也就是图中响应给浏览器的部分。
序号10: shopping.xiaogui.com客户端接收到cas-server的返回,知道了用户已经登录,ticket有效,告知浏览器可以进行访问。
至此,用户第一次访问流程结束。
当用户第二次访问http://shopping.xiaogui.com
序号11: 当用户第二次访问,仍然会经过AuthenticationFilter过滤器,但与第一次访问不同的是此时客户端session中已经存在用户的信息,浏览器中的Cookie会根据SessionID找到Session,获取用户信息,所以不需要进行验证,可以直接访问。
序号12: 客户端告知浏览器可以进行访问。
当用户第一次访问http://pay.xiaogui.com
序号13: 用户向pay.xiaogui.com CAS Client客户端发出请求
序号14: :pay.xiaogui.com接收到请求,发现第一次访问,于是给他一个重定向的地址,让他去找认证中心登录。
序号15: 浏览器根据上面响应的地址,发起重定向,因为之前访问过一次了,因此这次会携带上次返回的Cookie:TGC到认证中心。
序号16: 认证中心收到请求,发现TGC对应了一个TGT,于是用TGT签发一个ticket,并且返回给浏览器,让他重定向到pay.xiaogui.comCAS Client客户端。
序号17: 根据上面响应回来的地址,进行重定向到pay.xiaogui.comCAS Client客户端
序号18: pay.xiaogui.comCAS Client客户端带着ticket去认证中心验证是否有效。
序号19: 认证成功,把用户信息保存到客户端的session中,并把客户端的SessionID设置在Cookie中。当用户下次访问pay.xiaogui.comCAS Client客户端,直接登录,无需验证。
序号20: 告知浏览器可以进行访问
CAS单点登录的原理分析大致就是上述的这些,至于CAS单点登录的具体实现,将在下篇博客中写道。
G. cas单点登录怎么在服务器端获得用户信息
通过上述部署与配置,多个Web应用已经可以共用一个登录服务。但是,上述过程中作为CAS Client端的Web应用只取得了用户登录名称信息,而在实际应用中,Web应用往往需要获得登录用户更多的信息,例如会员等级、性别、住址等。要达到此目的,只需对Server端稍做修改即可实现。
1. 服务端配置及修改
假定上述存储用户信息的数据表userinfo中还包含一个名为address的用于存储用户地址的字段,而Web应用程序希望能够从CAS Server处获得当前登录用户的地址信息,则Server端需要按以下内容修改deployerConfigContext.xml。部分配置说明请参见注释。
<!--将原有attributeRepository配置注释 -->
<!--
<beanid="attributeRepository"
class="org.jasig.services.persondir.support.StubPersonAttributeDao">
<propertyname="backingMap">
<map>
<entrykey="uid" value="uid" />
<entrykey="ePersonAffiliation" value="ePersonAffiliation"/>
<entrykey="groupMembership" value="groupMembership" />
</map>
</property>
</bean>
-->
<!--新增attributeRepository配置(开始) -->
<bean class="org.jasig.services.persondir.support.jdbc."id="attributeRepository">
<!-- 指定使用的数据源,此处dataSource是已配置好的数据源 -->
<constructor-arg index="0"ref="dataSource"/>
<!-- 从数据库中查询信息的SQL语句,通常只需要修改表名即可 -->
<constructor-arg index="1" value="select * fromuserinfo where {0}"/>
<propertyname="queryAttributeMapping">
<map>
<!-- 上述查询的参数,将userName替换为表中表示用户名的字段名称 -->
<entrykey="username" value="userName"/>
</map>
</property>
<propertyname="resultAttributeMapping">
<map>
<!-- 需要返回给Web应用的其它信息,多个信息时可继续增加entry节点-->
<!--key值为数据表中的字段名称,value值为Client端取值时的名称标识-->
<entry key="address" value="address"/>
</map>
</property>
</bean>
<!--新增attributeRepository配置(结束) -->
<bean
id="serviceRegistryDao"
class="org.jasig.cas.services.">
<propertyname="registeredServices">
<list>
<beanclass="org.jasig.cas.services.RegexRegisteredService">
<propertyname="id" value="0" />
<propertyname="name" value="HTTP and IMAP" />
<propertyname="description" value="Allows HTTP(S) and IMAP(S)protocols" />
<propertyname="serviceId" value="^(https?|imaps?)://.*" />
<propertyname="evaluationOrder" value="10000001" />
<!--增加此项配置 -->
<property name="ignoreAttributes" value="true"/>
</bean>
… …
</list>
</property>
</bean>
CASServer要将额外的信息传递至Client端,还需要修改完成信息组装的文件WEB-INF/view/jsp/protocol/2.0/casServiceValidationSuccess.jsp。casServiceValidationSuccess.jsp负责组装包含用户信息的XML,因此修改部分是将需要传递的额外信息加入到它最终生成的XML文件之中。具体修改如下:
<cas:serviceResponsexmlns:cas='http://www.yale.e/tp/cas'>
<cas:authenticationSuccess> <cas:user>${fn:escapeXml(assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.id)}</cas:user>
<!-- 新增额外信息(开始) -->
<c:iftest="${fn:length(assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes)> 0}">
<cas:attributes>
<c:forEachvar="attr"items="${assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes}">
<!--注意此行的正确写法,网上资料基本都是错误的--> <cas:${fn:escapeXml(attr.key)}>${fn:escapeXml(attr.value)}</cas:${fn:escapeXml(attr.key)}>
</c:forEach>
</cas:attributes>
</c:if>
<!-- 新增额外信息(结束) -->
<c:if test="${not emptypgtIou}">
<cas:proxyGrantingTicket>${pgtIou}</cas:proxyGrantingTicket>
</c:if>
<c:if test="${fn:length(assertion.chainedAuthentications)> 1}">
<cas:proxies>
<c:forEachvar="proxy" items="${assertion.chainedAuthentications}"varStatus="loopStatus" begin="0"end="${fn:length(assertion.chainedAuthentications)-2}"step="1">
<cas:proxy>${fn:escapeXml(proxy.principal.id)}</cas:proxy>
</c:forEach>
</cas:proxies>
</c:if>
</cas:authenticationSuccess>
</cas:serviceResponse>
2. Java Client端取得更多用户信息
Java Client端不需要做任何修改就可以继续正常使用CAS服务,如果需要取得用户更多信息,可以通过AttributePrincipal对象取得Attribute列表(一个Map对象)后进行查询。
修改前述Java Client的示例代码,在最后追加取得address信息的代码,重启服务并重新访问页面,可以看到页面上显示了当前用户的address信息。
<%@pageimport="org.jasig.cas.client.authentication.AttributePrincipal" %>
<%@pageimport="org.jasig.cas.client.validation.Assertion" %>
<%@page import="java.util.*" %>
<%
String loginName1 = request.getRemoteUser();
%>
request.getRemoteUser(): <%=loginName1%><br/>
<%
AttributePrincipal principal = (AttributePrincipal)request.getUserPrincipal();
String loginName2 = principal.getName();
%>
request.getUserPrincipal().getName():<%=loginName2%><br/>
<%
Object object =request.getSession().getAttribute("_const_cas_assertion_");
Assertion assertion =(Assertion)object;
String loginName3 =assertion.getPrincipal().getName();
%>
request.getSession().getAttribute("_const_cas_assertion_").getPrincipal().getName():<%=loginName3%><br/>
H. 登录那点事
client:提供用户名和密码或者是其他的认证凭证
server:验证client提供的认证凭证,记录登录状态
过程:访问系统时,client必须输入用户名和密码,server进行验证,验证通过后server建立一个叫做session的东西,然后把sessionId通过cookie发送给浏览器,下次登录的时候会带着cookie一起发过来,server从cookie中拿到sessionId就知道已经登录啦。
cookie和session的作用就是保持client和server的交互状态,这样一来可以将无状态的通信转化成有状态的交互,也就是让server有了“记忆”能力。
现在有三个服务,需要输入三次用户信息,但是如果有成百上千的服务呢?HOW???
参考上面简单登录的原理:服务端如何有了“记忆”能力呢:在client和server之间引入了一个中间层(cookie或者是session)。(题外话:计算机世界的99%的问题都可以通过一个引入一个中间层来解决)
所有我们可以想到一个可行的解决方案:对这个“中间层”做共享。
比如说先登录了A,就有了一个cookie,然后在用cookie去登录其他的系统。但是这样明显是有问题的:
1,cookie不能跨域,比如说a.com产生的cookie是不能传递给b.com的
2,就算cookie可以传递,但是其他系统并没有session来校验这个cookie。
解决方案:
1.cookie做共享可以挂到同一个一级域名下,比如A叫a.corp.com,B叫b.corp.com,C叫c.corp.com,这样cookie不就能共享了嘛
2,将session也做共享,从内存里面拿出来,放入一个大家都能访问的中间层里面,比如说redis。(题外话:又是中间层)
像下面这样:
可以解决多系统登录的问题么?可以解决!!! 但是
1,要共享cookie就必须让所有的系统都在同一个域名下
2,多用户账号的存在。比如张三登录了A,然后去登录B,通过这种方式的话B需要去校验张三这个用户的合法性,此张三可能未必是彼张三!各个业务系统必须自己去维护自己系统的用户,而且用户信息还不止一个。
HOW????
Single Sign One :单点登录
消除多用户信息的问题,不用各个业务系统自己去维护用户信息,建立一个统一的用户认证中心(中间层:又是我哈哈哈),所有用户的认证工作都交给这个认证中心来完成。各个子业务只需要负责实现自己的业务即可,不在需要关心用户、认证等细节。
大致流程如下:
用户第一次访问A系统:www.a.corp.com,这个时候如果A发现用户没有登录的话,那他需要做的一项操作就是重定向认证中心:www.sso.com/login?redirect=www.a.corp.com。
其中www.sso.com/login就是认证中心的登录地址,redirect=www.a.corp.com就是登录完成后需要跳转到的地址。在认证中心登录之后认证中心会做以下几件事情:
1,建立一个session
2,发放ticket
3,重定向到你的地址,并在浏览器种下cookie信息。注意这个cookie是认证中心服务器的cookie哦。如:ssoid=1,domain=sso.com
需要注意的有两点:
1,进行了两次重定向。第一次是重定向到SSO的服务器,第二次是重定向我们的后端服务器。
2,流程完成后再浏览器种了两个cookie,一个是sso服务器的cookie,一个是子系统A的cookie。
接下来我们访问B系统:
这个时候会有三个cookie:
1,认证中心的cookie domain: sso.com
2,A系统的cookie domain: a.corp.com
3, B系统的cookie domain: b.corp.com
本质上就是保存了一个认证中心的cookie和多个子系统的cookie。一般我们称SSO的cookie的对应的会话成为全局会话,各自子系统cookie对应的会话称为局部会话。
概述
CAS(Central Authentication Service) 是 Yale 大学发起的一个企业级的、开源的项目,旨在为 Web 应用系统提供一种可靠的单点登录解决方法(属于 Web SSO)。
CAS 开始于 2001 年, 并在 2004 年 12 月正式成为 JA-SIG 的一个项目。
特性
1、 开源的、多协议的 SSO 解决方案;Protocols:Custom Protocol、CAS、OAuth、OpenID、RESTful API、SAML1.1、SAML2.0 等。
2、 支持多种认证机制:Active Directory、JAAS、JDBC、LDAP、X.509 Certificates 等;
3、 安全策略:使用票据(Ticket)来实现支持的认证协议;
4、 支持授权:可以决定哪些服务可以请求和验证服务票据(Service Ticket);
5、 提供高可用性:通过把认证过的状态数据存储在 TicketRegistry 组件中,这些组件有很多支持分布式环境的实现,如:BerkleyDB、Default 、EhcacheTicketRegistry、JDBCTicketRegistry、JBOSS TreeCache、JpaTicketRegistry、MemcacheTicketRegistry 等;
6、 支持多种客户端: Java、 .Net、 PHP、 Perl、 Apache, uPortal 等。
体系结构
从结构上看,CAS 包含两个部分:CAS Server 和 CAS Client,CAS 需要独立部署,主要负责对用户的认证工作,CAS Server 会处理用户名 / 密码等凭证 (Credentials)。
负责处理对客户端受保护资源的访问请求,需要对请求方进行身份认证时,重定向到 CAS Server 进行认证。CAS Client一般与 与受保护的客户端应用部署在一起,以 Filter 方式保护受保护的资源。过滤从客户端过来的每一个 Web 请求,同时, CAS Client 会分析 HTTP 请求中是否包请求 Service Ticket
术语
Ticket Granting ticket (TGT) :可以认为是CAS Server根据用户名密码生成的一张票,存在server端
Ticket-granting cookie (TGC) :其实就是一个cookie,存放用户身份信息,由server发给client端
Service ticket (ST) :由TGT生成的一次性票据,用于验证,只能用一次。相当于server发给client一张票,然后client拿着这是个票再来找server验证,看看是不是server签发的。就像是我给了你一张我的照片,然后你拿照片再来问我,这个照片是不是你。。。没错,就是这么无聊。
安全性
TGC安全性:
对于一个 CAS 用户来说,最重要是要保护它的 TGC,如果 TGC 不慎被 CAS Server 以外的实体获得,Hacker 能够找到该 TGC,然后冒充 CAS 用户访问所有授权资源。从基础模式可以看出, TGC 是 CAS Server 通过 SSL 方式发送给终端用户,因此,要截取 TGC 难度非常大,从而确保 CAS 的安全性。TGT 的存活周期默认为 120 分钟。
ST安全性:
ST(Service Ticket)是通过 Http 传送的,因此网络中的其他人可以 Sniffer 到其他人的 Ticket。CAS 通过以下几方面来使 ST 变得更加安全:
1、 ST 只能使用一次
CAS 协议规定,无论 Service Ticket 验证是否成功, CAS Server 都会清除服务端缓存中的该 Ticket,从而可以确保一个 Service Ticket 不被使用两次。
2、 ST 在一段时间内失效
CAS 规定 ST 只能存活一定的时间,然后 CAS Server 会让它失效。默认有效时间为 5 分钟。
3、 ST 是基于随机数生成的
ST 必须足够随机,如果 ST 生成规则被猜出,Hacker 就等于绕过 CAS 认证,直接访问对应的服务。
流程
上图是3个登录场景,分别为:第一次访问www.qian.com、第二次访问、以及登录状态下第一次访问mail.qian.com。
第一次访问www.qian.com
标号1: 用户访问http://www.qian.com,经过他的第一个过滤器:org.jasig.cas.client.authentication.AuthenticationFilter(cas提供,在web.xml中配置)。主要作用:判断是否登录,如果没有登录则重定向到认证中心
标号2: www.qian.com发现用户没有登录,则返回浏览器重定向地址。
首先可以看到我们请求www.qian.com,之后浏览器返回状态码302,然后让浏览器重定向到cas.qian.com并且通过get的方式添加参数service,该参数目的是登录成功之后会要重定向回来,因此需要该参数。并且你会发现,其实server的值就是编码之后的我们请求www.qian.com的地址。
标号3: 浏览器接收到重定向之后发起重定向,请求cas.qian.com。
标号4: 认证中心cas.qian.com接收到登录请求,返回登陆页面。
上图就是标号3的请求,以及标号4的响应。请求的URL是标号2返回的URL。之后认证中心就展示登录的页面,等待用户输入用户名密码。
标号5: 用户在cas.qian.com的login页面输入用户名密码,提交。
标号6 :服务器接收到用户名密码,则验证是否有效,验证逻辑可以使用cas-server提供现成的,也可以自己实现。
上图就是标号5的请求,以及标号6的响应了。当cas.qian.com即csa-server认证通过之后,会返回给浏览器302,重定向的地址就是Referer中的service参数对应的值。后边并通过get的方式挟带了一个ticket令牌,这个ticket就是ST(数字3处)。同时会在Cookie中设置一个CASTGC,该cookie是网站cas.qian.com的cookie,只有访问这个网站才会携带这个cookie过去。
Cookie中的CASTGC:向cookie中添加该值的目的是当下次访问cas.qian.com时,浏览器将Cookie中的TGC携带到服务器,服务器根据这个TGC,查找与之对应的TGT。从而判断用户是否登录过了,是否需要展示登录页面。TGT与TGC的关系就像SESSION与Cookie中SESSIONID的关系。点击这里了解Java如何操作Cookie。
TGT:Ticket Granted Ticket(俗称大令牌,或者说票根,他可以签发ST)
TGC:Ticket Granted Cookie(cookie中的value),存在Cookie中,根据他可以找到TGT。
ST:Service Ticket (小令牌),是TGT生成的,默认是用一次就生效了。也就是上面数字3处的ticket值。
标号7 :浏览器从cas.qian.com哪里拿到ticket之后,就根据指示重定向到www.qian.com,请求的url就是上面返回的url。
标号8 :www.qian.com在过滤器中会取到ticket的值,然后通过http方式调用cas.qian.com验证该ticket是否是有效的。
标号9 :cas.qian.com接收到ticket之后,验证,验证通过返回结果告诉www.qian.com该ticket有效。
标号10 :www.qian.com接收到cas-server的返回,知道了用户合法,展示相关资源到用户浏览器上。
第二次访问www.qian.com
标号11 :用户发起请求,访问www.qian.com。会经过cas-client,也就是过滤器,因为第一次访问成功之后www.qian.com中会在session中记录用户信息,因此这里直接就通过了,不用验证了。
标号12 :用户通过权限验证,浏览器返回正常资源。
访问mail.qian.com
标号13 :用户在www.qian.com正常上网,突然想访问mail.qian.com,于是发起访问mail.qian.com的请求。
标号14 :mail.qian.com接收到请求,发现第一次访问,于是给他一个重定向的地址,让他去找认证中心登录。
上图可以看到,用户请求mail.qian.com,然后返回给他一个网址,状态302重定向,service参数就是回来的地址。
标号15 :浏览器根据14返回的地址,发起重定向,因为之前访问过一次了,因此这次会携带上次返回的Cookie:TGC到认证中心。
标号16 :认证中心收到请求,发现TGC对应了一个TGT,于是用TGT签发一个ST,并且返回给浏览器,让他重定向到mail.qian.com
可以发现请求的时候是携带Cookie:CASTGC的,响应的就是一个地址加上TGT签发的ST也就是ticket。
标号17 :浏览器根据16返回的网址发起重定向。
标号18 :mail.qian.com获取ticket去认证中心验证是否有效。
标号19 :认证成功,返回在mail.qian.com的session中设置登录状态,下次就直接登录。
标号20 :认证成功之后就反正用想要访问的资源了。
配置filter
我们需要在应用的web.xml文件中配置四个Filter,这四个Filter必须按照固定的顺序来进行配置,而且它们必须配置在应用的其它Filter之前。它们的先后顺序要求如下:
1、AuthenticationFilter
casServerLoginUrl用来指定Cas Server登录地址,serverName或service用来指定认证成功后需要跳转地址。
2、TicketValidationFilter
请求通过AuthenticationFilter的认证之后,如果请求中携带了参数ticket则将会由TicketValidationFilter来对携带的ticket进行校验。 TicketValidationFilter只是对验证ticket的这一类Filter的统称,其并不对应Cas Client中的一个具体类型。Cas Client中有多种验证ticket的Filter,
都继承自,它们的验证逻辑都是一致的,都有实现,不同的是使用的TicketValidator不一样。这里我们使用Cas10TicketValidationFilter,也可以使用或Saml11TicketValidationFilter。
3、
用于将每一个请求对应的HttpServletRequest封装为其内部定义的CasHttpServletRequestWrapper,该封装类将利用之前保存在Session或request中的Assertion对象重写HttpServletRequest的getUserPrincipal()、getRemoteUser()和isUserInRole()方法。
这样在我们的应用中就可以非常方便的从HttpServletRequest中获取到用户的相关信息
4、AssertionThreadLocalFilter
AssertionThreadLocalFilter可以在应用的其它地方获取Assertion对象,找个过滤器会把Assertion对象存放到当前的线程变量中,我们在程序的任何地方都可以从线程变量中获取当前Assertion,就不需要再从Session或request中进行解析了。这个线程变量是由AssertionHolder持有的,我们在获取当前的 Assertion时也只需要通过AssertionHolder的getAssertion()方法获取即可