CAS实现SSO单点登录原理 .
2014-03-07 09:20
666 查看
yale cas可以百度一下,这是学习cas后的一点总结,以备日后使用!
安全性:
用户只须在cas录入用户名和密码,之后通过ticket绑定用户,在cas客户端与cas校验是通过ticket,并不会在网上传输密码,所以可以保证安全性,密码不被窃取
原理:1个cookie+N个session
CAS创建cookie在所有应用中登录时cas使用,各应用通过在IE创建各自的session来标识应用是否已经登录。
Cookie:在cas为各应用登录时使用,实现了只须一次录入用户密码
Session:各应用会创建自己的session表示是否登录
第一步:cas往浏览器增加cookie(TGC)
CAS向浏览器送回一个所谓的“内存cookie”。这种cookie并不是真的保存在内存中,而只是浏览器一关闭,cookie就自动过期。这个cookie称为“ticket-granting cookie”,用来表明用户已经成功地登录。
这个Cookie是一个加密的Cookie,其中保存了用户登录的信息。用于以后其它应用客户端登录。
第二步:cas同时创建一个ticket重定向到原来的cas客户端
认证成功后,CAS服务器创建一个很长的、随机生成的字符串,称为“Ticket”。随后,CAS将这个ticket和成功登录的用户,以及服务联系在一起。这个ticket是一次性使用的一种凭证,它只对登录成功的用户及其服务使用一次。使用过以后立刻失效。
2. Cas 客户端应用A的处理
第一步:收到ticket后,向cas提交验证ticket
Cas客户端收到ticket之后,应用程序需要验证ticket。这是通过将ticket 传递给一个校验URL来实现的。校验URL也是CAS服务器提供的。CAS通过校验路径获得了ticket之后,通过内部的数据库对其进行判断。如果判断是有效性,则返回一个NetID给应用程序。随后CAS将ticket作废,并且在客户端留下一个cookie。(谁来创建cookie?),
第二步:ticket验证后创建session
以后登录此应用时,没有ticket,但IE能提供session,从session中取得CASReceipt,并验证如果有效说明已经在此应用认证过,允许访问此应用,
到此为止,CAS会记录用户已在应用A已经登录
3. 用户登录到应用B是如何处理
用户进入应用B时,首先仍然会重定向到CAS服务器。不过此时CAS服务器不再要求用户输 入用户名和密码,而是首先自动寻找Cookie,根据Cookie中保存的信息,进行登录。然后,CAS同样给出新的ticket重定向应用B给cas验证(流程同应用A验证方式),如果验证成功则应用B创建session记录CASReceipt信息到session中,以后凭此session登录应用B。
到此为止,CAS会记录用户已在应用A和应用B进行登录,但是当用户在应用B退出cas登录时,要通知应用A进行退出,如何通知应用A呢?
CAS server接受请求后,会检测用户的TCG Cookie,把对应的session清除,同时会找到所有通过该TGC sso登录的应用服务器URL提交请求,所有的回调请求中,包含一个参数logoutRequest,内容格式如下:
<samlp:LogoutRequest ID="[RANDOM ID]" Version="2.0" IssueInstant="[CURRENT DATE/TIME]">
<saml:NameID>@NOT_USED@</saml:NameID>
<samlp:SessionIndex>[SESSION IDENTIFIER]</samlp:SessionIndex>
</samlp:LogoutRequest>
所有收到请求的应用服务器application会解析这个参数,取得sessionId,根据这个Id取得session后,把session删除。
这样就实现单点登出的功能。
知道原理后,下面是结合源代码来讲述一下内部的代码怎么实现的。
<filter>
<filter-name>CAS Single Sign Out Filter</filter-name>
<filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CAS Single Sign Out Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
</listener>
注:如果有配置CAS client Filter,则CAS Single Sign Out Filter 必须要放到CAS client Filter之前。
配置部分的目的是在CAS server回调所有的application进行单点登出操作的时候,需要这个filter来实现session清楚。
主要代码如下:
org.jasig.cas.client.session.SingleSignOutFilter
1 public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain
2
3 filterChain) throws IOException, ServletException {
4 final HttpServletRequest request = (HttpServletRequest) servletRequest;
5
6 if ("POST".equals(request.getMethod())) {
7 final String logoutRequest = request.getParameter("logoutRequest");
8
9 if (CommonUtils.isNotBlank(logoutRequest)) {
10
11 if (log.isTraceEnabled()) {
12 log.trace ("Logout request=[" + logoutRequest + "]");
13 }
14 //从xml中解析 SessionIndex key值
15 final String sessionIdentifier = XmlUtils.getTextForElement(logoutRequest, "SessionIndex");
16
17 if (CommonUtils.isNotBlank(sessionIdentifier)) {
18 //根据sessionId取得session对象
19 final HttpSession session = SESSION_MAPPING_STORAGE.removeSessionByMappingId(sessionIdentifier);
20
21 if (session != null) {
22 String sessionID = session.getId();
23
24 if (log.isDebugEnabled()) {
25 log.debug ("Invalidating session [" + sessionID + "] for ST [" + sessionIdentifier + "]");
26 }
27
28 try {
29 //让session失效
30 session.invalidate();
31 } catch (final IllegalStateException e) {
32 log.debug(e,e);
33 }
34 }
35 return;
36 }
37 }
38 } else {//get方式 表示登录,把session对象放到SESSION_MAPPING_STORAGE(map对象中)
39 final String artifact = request.getParameter(this.artifactParameterName);
40 final HttpSession session = request.getSession();
41
42 if (log.isDebugEnabled() && session != null) {
43 log.debug("Storing session identifier for " + session.getId());
44 }
45 if (CommonUtils.isNotBlank(artifact)) {
46 SESSION_MAPPING_STORAGE.addSessionById(artifact, session);
47 }
48 }
49
50 filterChain.doFilter(servletRequest, servletResponse);
51 }
SingleSignOutHttpSessionListener实现了javax.servlet.http.HttpSessionListener接口,用于监听session销毁事件
1 public final class SingleSignOutHttpSessionListener implements HttpSessionListener {
2
3 private Log log = LogFactory.getLog(getClass());
4
5 private SessionMappingStorage SESSION_MAPPING_STORAGE;
6
7 public void sessionCreated(final HttpSessionEvent event) {
8 // nothing to do at the moment
9 }
10
11 //session销毁时
12 public void sessionDestroyed(final HttpSessionEvent event) {
13 if (SESSION_MAPPING_STORAGE == null) {//如果为空,创建一个sessionMappingStorage 对象
14 SESSION_MAPPING_STORAGE = getSessionMappingStorage();
15 }
16 final HttpSession session = event.getSession();//取得当然要销毁的session对象
17
18 if (log.isDebugEnabled()) {
19 log.debug("Removing HttpSession: " + session.getId());
20 }
21 //从SESSION_MAPPING_STORAGE map根据sessionId移去session对象
22 SESSION_MAPPING_STORAGE.removeBySessionById(session.getId());
23 }
24
25 /**
26 * Obtains a {@link SessionMappingStorage} object. Assumes this method will always return the same
27 * instance of the object. It assumes this because it generally lazily calls the method.
28 *
29 * @return the SessionMappingStorage
30 */
31 protected static SessionMappingStorage getSessionMappingStorage() {
32 return SingleSignOutFilter.getSessionMappingStorage();
33 }
34 }
下载个httpwatch监控一下cookie的变化
CLIENT:没票据且SESSION中没有消息所以跳转至CAS
CAS:拿不到TGC故要求用户登录
2. 认证成功后回跳
CAS:通过TGT生成ST发给客户端,客户端保存TGC,并重定向到http://localhost:8080/a
CLIENT:带有票据所以不跳转只是后台发给CAS验证票据(浏览器中无法看到这一过程)
3. 第一次访问http://localhost:8080/b
CLIENT:没票据且SESSION中没有消息所以跳转至CAS
CAS:从客户端取出TGC,如果TGC有效则给用户ST并后台验证ST,从而SSO。【如果失效重登录或注销时,怎么通知其它系统更新SESSION信息呢??TicketGrantingTicketImpl类grantServiceTicket方法里this.services.put(id,service);可见CAS端已经记录了当前登录的子系统】
4. 再次访问http://localhost:8080/a
CLIENT:没票据但是SESSION中有消息故不跳转也不用发CAS验证票据,允许用户访问
安全性:
用户只须在cas录入用户名和密码,之后通过ticket绑定用户,在cas客户端与cas校验是通过ticket,并不会在网上传输密码,所以可以保证安全性,密码不被窃取
原理:1个cookie+N个session
CAS创建cookie在所有应用中登录时cas使用,各应用通过在IE创建各自的session来标识应用是否已经登录。
Cookie:在cas为各应用登录时使用,实现了只须一次录入用户密码
Session:各应用会创建自己的session表示是否登录
登录
1. CAS 登录时处理:第一步:cas往浏览器增加cookie(TGC)
CAS向浏览器送回一个所谓的“内存cookie”。这种cookie并不是真的保存在内存中,而只是浏览器一关闭,cookie就自动过期。这个cookie称为“ticket-granting cookie”,用来表明用户已经成功地登录。
这个Cookie是一个加密的Cookie,其中保存了用户登录的信息。用于以后其它应用客户端登录。
第二步:cas同时创建一个ticket重定向到原来的cas客户端
认证成功后,CAS服务器创建一个很长的、随机生成的字符串,称为“Ticket”。随后,CAS将这个ticket和成功登录的用户,以及服务联系在一起。这个ticket是一次性使用的一种凭证,它只对登录成功的用户及其服务使用一次。使用过以后立刻失效。
2. Cas 客户端应用A的处理
第一步:收到ticket后,向cas提交验证ticket
Cas客户端收到ticket之后,应用程序需要验证ticket。这是通过将ticket 传递给一个校验URL来实现的。校验URL也是CAS服务器提供的。CAS通过校验路径获得了ticket之后,通过内部的数据库对其进行判断。如果判断是有效性,则返回一个NetID给应用程序。随后CAS将ticket作废,并且在客户端留下一个cookie。(谁来创建cookie?),
第二步:ticket验证后创建session
以后登录此应用时,没有ticket,但IE能提供session,从session中取得CASReceipt,并验证如果有效说明已经在此应用认证过,允许访问此应用,
到此为止,CAS会记录用户已在应用A已经登录
3. 用户登录到应用B是如何处理
用户进入应用B时,首先仍然会重定向到CAS服务器。不过此时CAS服务器不再要求用户输 入用户名和密码,而是首先自动寻找Cookie,根据Cookie中保存的信息,进行登录。然后,CAS同样给出新的ticket重定向应用B给cas验证(流程同应用A验证方式),如果验证成功则应用B创建session记录CASReceipt信息到session中,以后凭此session登录应用B。
到此为止,CAS会记录用户已在应用A和应用B进行登录,但是当用户在应用B退出cas登录时,要通知应用A进行退出,如何通知应用A呢?
登出
CAS server接受请求后,会检测用户的TCG Cookie,把对应的session清除,同时会找到所有通过该TGC sso登录的应用服务器URL提交请求,所有的回调请求中,包含一个参数logoutRequest,内容格式如下:
<samlp:LogoutRequest ID="[RANDOM ID]" Version="2.0" IssueInstant="[CURRENT DATE/TIME]">
<saml:NameID>@NOT_USED@</saml:NameID>
<samlp:SessionIndex>[SESSION IDENTIFIER]</samlp:SessionIndex>
</samlp:LogoutRequest>
所有收到请求的应用服务器application会解析这个参数,取得sessionId,根据这个Id取得session后,把session删除。
这样就实现单点登出的功能。
知道原理后,下面是结合源代码来讲述一下内部的代码怎么实现的。
客户端实现:
首先,要实现single sign out在 应用服务器application端的web.xml要加入以下配置<filter>
<filter-name>CAS Single Sign Out Filter</filter-name>
<filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CAS Single Sign Out Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
</listener>
注:如果有配置CAS client Filter,则CAS Single Sign Out Filter 必须要放到CAS client Filter之前。
配置部分的目的是在CAS server回调所有的application进行单点登出操作的时候,需要这个filter来实现session清楚。
主要代码如下:
org.jasig.cas.client.session.SingleSignOutFilter
1 public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain
2
3 filterChain) throws IOException, ServletException {
4 final HttpServletRequest request = (HttpServletRequest) servletRequest;
5
6 if ("POST".equals(request.getMethod())) {
7 final String logoutRequest = request.getParameter("logoutRequest");
8
9 if (CommonUtils.isNotBlank(logoutRequest)) {
10
11 if (log.isTraceEnabled()) {
12 log.trace ("Logout request=[" + logoutRequest + "]");
13 }
14 //从xml中解析 SessionIndex key值
15 final String sessionIdentifier = XmlUtils.getTextForElement(logoutRequest, "SessionIndex");
16
17 if (CommonUtils.isNotBlank(sessionIdentifier)) {
18 //根据sessionId取得session对象
19 final HttpSession session = SESSION_MAPPING_STORAGE.removeSessionByMappingId(sessionIdentifier);
20
21 if (session != null) {
22 String sessionID = session.getId();
23
24 if (log.isDebugEnabled()) {
25 log.debug ("Invalidating session [" + sessionID + "] for ST [" + sessionIdentifier + "]");
26 }
27
28 try {
29 //让session失效
30 session.invalidate();
31 } catch (final IllegalStateException e) {
32 log.debug(e,e);
33 }
34 }
35 return;
36 }
37 }
38 } else {//get方式 表示登录,把session对象放到SESSION_MAPPING_STORAGE(map对象中)
39 final String artifact = request.getParameter(this.artifactParameterName);
40 final HttpSession session = request.getSession();
41
42 if (log.isDebugEnabled() && session != null) {
43 log.debug("Storing session identifier for " + session.getId());
44 }
45 if (CommonUtils.isNotBlank(artifact)) {
46 SESSION_MAPPING_STORAGE.addSessionById(artifact, session);
47 }
48 }
49
50 filterChain.doFilter(servletRequest, servletResponse);
51 }
SingleSignOutHttpSessionListener实现了javax.servlet.http.HttpSessionListener接口,用于监听session销毁事件
1 public final class SingleSignOutHttpSessionListener implements HttpSessionListener {
2
3 private Log log = LogFactory.getLog(getClass());
4
5 private SessionMappingStorage SESSION_MAPPING_STORAGE;
6
7 public void sessionCreated(final HttpSessionEvent event) {
8 // nothing to do at the moment
9 }
10
11 //session销毁时
12 public void sessionDestroyed(final HttpSessionEvent event) {
13 if (SESSION_MAPPING_STORAGE == null) {//如果为空,创建一个sessionMappingStorage 对象
14 SESSION_MAPPING_STORAGE = getSessionMappingStorage();
15 }
16 final HttpSession session = event.getSession();//取得当然要销毁的session对象
17
18 if (log.isDebugEnabled()) {
19 log.debug("Removing HttpSession: " + session.getId());
20 }
21 //从SESSION_MAPPING_STORAGE map根据sessionId移去session对象
22 SESSION_MAPPING_STORAGE.removeBySessionById(session.getId());
23 }
24
25 /**
26 * Obtains a {@link SessionMappingStorage} object. Assumes this method will always return the same
27 * instance of the object. It assumes this because it generally lazily calls the method.
28 *
29 * @return the SessionMappingStorage
30 */
31 protected static SessionMappingStorage getSessionMappingStorage() {
32 return SingleSignOutFilter.getSessionMappingStorage();
33 }
34 }
服务器端实现
已经登录的应用会在服务器端保存,所以服务端分别对各个应用发送http请求进行session清除操作。网上参考资料
看了下面的浏览器cookie变化,会对cas有更深的理解下载个httpwatch监控一下cookie的变化
客户端消息流程
1. 第一次访问http://localhost:8080/a,CLIENT:没票据且SESSION中没有消息所以跳转至CAS
CAS:拿不到TGC故要求用户登录
2. 认证成功后回跳
CAS:通过TGT生成ST发给客户端,客户端保存TGC,并重定向到http://localhost:8080/a
CLIENT:带有票据所以不跳转只是后台发给CAS验证票据(浏览器中无法看到这一过程)
3. 第一次访问http://localhost:8080/b
CLIENT:没票据且SESSION中没有消息所以跳转至CAS
CAS:从客户端取出TGC,如果TGC有效则给用户ST并后台验证ST,从而SSO。【如果失效重登录或注销时,怎么通知其它系统更新SESSION信息呢??TicketGrantingTicketImpl类grantServiceTicket方法里this.services.put(id,service);可见CAS端已经记录了当前登录的子系统】
4. 再次访问http://localhost:8080/a
CLIENT:没票据但是SESSION中有消息故不跳转也不用发CAS验证票据,允许用户访问
相关文章推荐
- CAS实现单点登录SSO执行原理探究
- CAS实现SSO单点登录原理
- CAS实现单点登录SSO执行原理探究(终于明白了)
- CAS实现SSO单点登录原理【强烈推荐】
- CAS实现SSO单点登录原理
- CAS实现SSO(单点登录)原理
- CAS实现SSO单点登录原理介绍
- CAS实现SSO单点登录原理
- CAS实现单点登录SSO执行原理探究
- CAS实现单点登录SSO执行原理探究(终于明白了)
- CAS实现单点登录SSO执行原理探究(终于明白了)
- CAS实现单点登录SSO执行原理探究(终于明白了)
- CAS实现SSO单点登录原理
- CAS实现单点登录SSO执行原理探究(终于明白了)
- CAS实现单点登录(sso)原理分析
- 单点登录SSO的实现原理
- 使用CAS实现SSO(单点登录)
- 单点登录SSO的实现原理
- 单点登录SSO的实现原理
- CAS 实现单点登录SSO