CAS 单点登录的实现原理(一)

article/2025/9/4 0:23:47

一,会话和Cookie

HTTP是无状态协议,客户端与服务端之间的每次通信都是独立的,而会话机制可以让服务端鉴别每次通讯过程中的客户端是否是同一个,从而保证业务的关联性。 Session是服务器使用一种类似于散列表的结构,用来保存用户会话所需要的信息.Cookie作为浏览器缓存,存储Session ID以达到会话跟踪的目的。

会话和饼干

由于Cookie的跨域策略限制,Cookie携带的会话标识无法在域名不同的服务端之间共享。
因此引入CAS服务端作为用户信息鉴别和传递中介,达到单点登录的效果。

二,CAS流程图

官方流程图,地址:https://apereo.github.io/cas/ ...

cas_flow_diagram

浏览器与APP01服务端

  1. 浏览器第一次访问受保护的APP01服务端,由于未经授权而被拦截并重定向到CAS服务端。
  2. 浏览器第一次与CAS服务端通讯,鉴权成功后由CAS服务端创建全局会话SSO会话,生成全局会话标识TGT并存在浏览器Cookie中。
  3. 浏览器重定向到APP01,重写URL地址带上全局会话标识TGT。
  4. APP01拿到全局会话标识TGT后向CAS服务端请求校验,若校验成功,则APP01会获取到已经登录的用户信息。
  5. APP01创建局部会话Session,并将SessionID存储到浏览器Cookie中。
  6. 浏览器与APP01建立会话。

浏览器与APP02服务端

  1. 浏览器第一次访问受保护的APP02服务端,由于未经授权而被拦截并重定向到CAS服务端。
  2. 浏览器第二次与CAS服务端通讯,CAS校验Cookie中的全局会话标识TGT。
  3. 浏览器重定向到APP02,重写URL地址带上全局会话标识TGT。
  4. APP02 拿到全局会话标识 TGT 后向 CAS 服务端请求校验,若校验成功,则 APP02 会获取到已经登录的用户信息。
  5. APP02 创建局部会话 Session,并将 SessionID 存储到浏览器 Cookie 中。
  6. 浏览器与 APP02 建立会话。

三、相关源码

3.1 CAS客户端

3.1.1 根据是否已登录进行拦截跳转

以客户端拦截器作为入口,对于用户请求,如果是已经校验通过的,直接放行:
org.jasig.cas.client.authentication.AuthenticationFilter#doFilter

// 不进行拦截的请求地址
if (isRequestUrlExcluded(request)) {logger.debug("Request is ignored.");filterChain.doFilter(request, response);return;
}// Session已经登录
final HttpSession session = request.getSession(false);
final Assertion assertion = session != null ? (Assertion) session.getAttribute(CONST_CAS_ASSERTION) : null;
if (assertion != null) {filterChain.doFilter(request, response);return;
}// 从请求中获取ticket
final String serviceUrl = constructServiceUrl(request, response);
final String ticket = retrieveTicketFromRequest(request);
final boolean wasGatewayed = this.gateway && this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);
if (CommonUtils.isNotBlank(ticket) || wasGatewayed) {filterChain.doFilter(request, response);return;
}

否则进行重定向:
org.jasig.cas.client.authentication.AuthenticationFilter#doFilter

this.authenticationRedirectStrategy.redirect(request, response, urlToRedirectTo);

对于Ajax请求和非Ajax请求的重定向,进行分别处理:
org.jasig.cas.client.authentication.FacesCompatibleAuthenticationRedirectStrategy#redirect

public void redirect(final HttpServletRequest request, final HttpServletResponse response,final String potentialRedirectUrl) throws IOException {if (CommonUtils.isNotBlank(request.getParameter(FACES_PARTIAL_AJAX_PARAMETER))) {// this is an ajax request - redirect ajaxlyresponse.setContentType("text/xml");response.setStatus(200);final PrintWriter writer = response.getWriter();writer.write("<?xml version='1.0' encoding='UTF-8'?>");writer.write(String.format("<partial-response><redirect url=\"%s\"></redirect></partial-response>",potentialRedirectUrl));} else {response.sendRedirect(potentialRedirectUrl);}
}

3.1.2 校验Ticket

如果请求中带有 Ticket,则进行校验,校验成功返回用户信息:
org.jasig.cas.client.validation.AbstractTicketValidationFilter#doFilter

final Assertion assertion = this.ticketValidator.validate(ticket, constructServiceUrl(request, response));
logger.debug("Successfully authenticated user: {}", assertion.getPrincipal().getName());
request.setAttribute(CONST_CAS_ASSERTION, assertion);

打断点得知返回的信息为 XML 格式字符串:
org.jasig.cas.client.validation.AbstractUrlBasedTicketValidator#validate

logger.debug("Retrieving response from server.");
final String serverResponse = retrieveResponseFromServer(new URL(validationUrl), ticket);

XML 文件内容示例:

<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'><cas:authenticationSuccess><cas:user>casuser</cas:user><cas:attributes><cas:credentialType>UsernamePasswordCredential</cas:credentialType><cas:isFromNewLogin>true</cas:isFromNewLogin><cas:authenticationDate>2018-03-25T22:09:49.768+08:00[GMT+08:00]</cas:authenticationDate><cas:authenticationMethod>AcceptUsersAuthenticationHandler</cas:authenticationMethod><cas:successfulAuthenticationHandlers>AcceptUsersAuthenticationHandler</cas:successfulAuthenticationHandlers><cas:longTermAuthenticationRequestTokenUsed>false</cas:longTermAuthenticationRequestTokenUsed></cas:attributes></cas:authenticationSuccess>
</cas:serviceResponse>

最后将 XML 字符串转换为对象 org.jasig.cas.client.validation.Assertion,并存储在 Session 或 Request 中。

断言

3.1.3 重写Request请求

定义过滤器:
org.jasig.cas.client.util.HttpServletRequestWrapperFilter#doFilter

其中定义 CasHttpServletRequestWrapper,重写 HttpServletRequestWrapperFilter:

final class CasHttpServletRequestWrapper extends HttpServletRequestWrapper {private final AttributePrincipal principal;CasHttpServletRequestWrapper(final HttpServletRequest request, final AttributePrincipal principal) {super(request);this.principal = principal;}public Principal getUserPrincipal() {return this.principal;}public String getRemoteUser() {return principal != null ? this.principal.getName() : null;}// 省略其他代码

这样使用以下代码即可获取已登录用户信息。

AttributePrincipal principal = (AttributePrincipal) request.getUserPrincipal();

3.2 CAS服务端

3.2.1 用户密码校验

服务端采用了 Spirng Web Flow,以 login-webflow.xml 为入口:

<action-state id="realSubmit"><evaluate expression="authenticationViaFormAction"/><transition on="warn" to="warn"/><transition on="success" to="sendTicketGrantingTicket"/><transition on="successWithWarnings" to="showAuthenticationWarningMessages"/><transition on="authenticationFailure" to="handleAuthenticationFailure"/><transition on="error" to="initializeLoginForm"/>
</action-state>

action-state代表一个流程,其中 id 为该流程的标识。
evaluate expression为该流程的实现类。
transition表示对返回结果的处理。

定位到该流程对应的实现类authenticationViaFormAction,可知在项目启动时实例化了对象AbstractAuthenticationAction

@ConditionalOnMissingBean(name = "authenticationViaFormAction")
@Bean
@RefreshScope
public Action authenticationViaFormAction() {return new InitialAuthenticationAction(initialAuthenticationAttemptWebflowEventResolver,serviceTicketRequestWebflowEventResolver,adaptiveAuthenticationPolicy);
}

在页面上点击登录按钮,进入:
org.apereo.cas.web.flow.actions.AbstractAuthenticationAction#doExecute
org.apereo.cas.authentication.PolicyBasedAuthenticationManager#authenticate

PolicyBasedAuthenticationManager

经过层层过滤,得到执行校验的AcceptUsersAuthenticationHandler和待校验的UsernamePasswordCredential

执行校验,进入
org.apereo.cas.authentication.AcceptUsersAuthenticationHandler#authenticateUsernamePasswordInternal

@Override
protected HandlerResult authenticateUsernamePasswordInternal(final UsernamePasswordCredential credential,final String originalPassword) throws GeneralSecurityException {if (this.users == null || this.users.isEmpty()) {throw new FailedLoginException("No user can be accepted because none is defined");}// 页面输入的用户名final String username = credential.getUsername();// 根据用户名取得缓存中的密码final String cachedPassword = this.users.get(username);if (cachedPassword == null) {LOGGER.debug("[{}] was not found in the map.", username);throw new AccountNotFoundException(username + " not found in backing map.");}// 校验缓存中的密码和用户输入的密码是否一致if (!StringUtils.equals(credential.getPassword(), cachedPassword)) {throw new FailedLoginException();}final List<MessageDescriptor> list = new ArrayList<>();return createHandlerResult(credential, this.principalFactory.createPrincipal(username), list);
}

3.2.2登录页Ticket校验

在login-webflow.xml中定义了Ticket校验流程:

<action-state id="ticketGrantingTicketCheck"><evaluate expression="ticketGrantingTicketCheckAction"/><transition on="notExists" to="gatewayRequestCheck"/><transition on="invalid" to="terminateSession"/><transition on="valid" to="hasServiceCheck"/>
</action-state>

org.apereo.cas.web.flow.TicketGrantingTicketCheckAction#doExecute

@Override
protected Event doExecute(final RequestContext requestContext) {// 从请求中获取TicketIDfinal String tgtId = WebUtils.getTicketGrantingTicketId(requestContext);if (!StringUtils.hasText(tgtId)) {return new Event(this, NOT_EXISTS);}String eventId = INVALID;try {// 根据TicketID获取Tciket对象,校验是否失效final Ticket ticket = this.centralAuthenticationService.getTicket(tgtId, Ticket.class);if (ticket != null && !ticket.isExpired()) {eventId = VALID;}} catch (final AbstractTicketException e) {LOGGER.trace("Could not retrieve ticket id [{}] from registry.", e.getMessage());}return new Event(this, eventId);
}

可知Ticket存储在服务端的一个Map集合中:
org.apereo.cas.AbstractCentralAuthenticationService#getTicket(java.lang.String,java.lang.Class <T>)

AbstractCentralAuthenticationService

3.2.3客户端Ticket校验

对于从CAS客户端发送过来的Ticket校验请求,则会进入服务端以下代码:
org.apereo.cas.DefaultCentralAuthenticationService#validateServiceTicket

从Ticket仓库中,根据TicketID获取Ticket对象:

final ServiceTicket serviceTicket = this.ticketRegistry.getTicket(serviceTicketId, ServiceTicket.class);

在同步块中校验Ticket是否失效,以及是否来自合法的客户端:

synchronized (serviceTicket) {if (serviceTicket.isExpired()) {LOGGER.info("ServiceTicket [{}] has expired.", serviceTicketId);throw new InvalidTicketException(serviceTicketId);}if (!serviceTicket.isValidFor(service)) {LOGGER.error("Service ticket [{}] with service [{}] does not match supplied service [{}]",serviceTicketId, serviceTicket.getService().getId(), service);throw new UnrecognizableServiceForServiceTicketValidationException(serviceTicket.getService());}
}

根据Ticket获取已登录用户:

final TicketGrantingTicket root = serviceTicket.getGrantingTicket().getRoot();
final Authentication authentication = getAuthenticationSatisfiedByPolicy(root.getAuthentication(),new ServiceContext(selectedService, registeredService));
final Principal principal = authentication.getPrincipal();

最后将用户信息返回给客户端。


http://chatgpt.dhexx.cn/article/0NYqDM1S.shtml

相关文章

CAS单点登录原理及实现

CAS单点登录原理及实现 标签&#xff08;空格分隔&#xff09;&#xff1a; 单点登录 CAS SSO ###目录 概念介绍搭建流程原理分析 ###概念介绍 CAS介绍 Central Authentication Service&#xff08;缩写CAS&#xff09;是一种针对万维网的单点登录协议。它的目的是允许一个用…

单点登录CAS实现

单点登录&#xff08;Single Sign-On&#xff0c;简称SSO&#xff09;是一种身份验证技术&#xff0c;它允许用户使用一组凭据&#xff08;如用户名和密码&#xff09;登录到多个应用程序或系统中&#xff0c;而无需在每个应用程序或系统中单独登录。这种技术可以提高用户体验&…

CAS单点登录原理及改造

CAS是由耶鲁大学开发的单点登录系统&#xff0c;其核心的知识点包括以下几个概念: 1) TGT: 票据&#xff0c;或称大令牌&#xff0c;在登录成功之后生成&#xff0c;其中包含了用户信息 2) TGC: TGT的key&#xff0c;TGT存储在session中&#xff0c;TGC以cookie形式保存在浏览器…

SSO单点登录原理详解(从入门到精通)

本文主要对SSO单点登录与CAS、OAuth2.0两种授权协议的关系和原理进行详细说明。 1. 基础概念 SSO单点登录&#xff08;Single sign-on&#xff09; 所谓单点登录就是在多个应用系统中&#xff0c;用户只需登录一次就可以访问所有相互信任的系统。 CAS 中央认证服务&#xf…

CAS单点登录的实现

这篇文章对CAS单点登录具体实现的一些步骤就行讲述&#xff0c;至于CAS单点登录的实现原理分析&#xff0c;请参看下面这篇文章&#xff1a;CAS单点登录原理分析(一) https://blog.csdn.net/qq_41258204/article/details/84036875 CAS 包含两个部分&#xff1a; CAS Server 和…

CAS单点登录原理解析

推荐阅读 1. SpringBoot 整合篇 2. 手写一套迷你版HTTP服务器 3. 记住&#xff1a;永远不要在MySQL中使用UTF-8 4. Springboot启动原理解析 1、基于Cookie的单点登录的回顾 基于Cookie的单点登录核心原理&#xff1a; 将用户名密码加密之后存于Cookie中&#xff0c;之后访…

CAS单点登录的实现(二)

这篇文章对CAS单点登录具体实现的一些步骤就行讲述&#xff0c;至于CAS单点登录的实现原理分析&#xff0c;请参看下面这篇文章&#xff1a; CAS单点登录原理分析(一) https://blog.csdn.net/qq_41258204/article/details/84036875 CAS 包含两个部分&#xff1a; CAS Server 和…

CAS单点登录系列之原理简单介绍

文章目录 一、 SSO简介1.1 单点登录定义1.2 单点登录角色1.3 单点登录分类 二、 CAS简介2.1 CAS简单定义2.2 CAS体系结构2.3 CAS原理 三、CAS服务端部署附录 一、 SSO简介 1.1 单点登录定义 单点登录(Single sign on)&#xff0c;英文名称缩写SSO&#xff0c;SSO的意思就是在…

CAS实现单点登录SSO执行原理探究(终于明白了)

一、不落俗套的开始 1、背景介绍 单点登录:Single Sign On,简称SSO,SSO使得在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。 CAS框架:CAS(Central Authentication Service)是实现SSO单点登录的框架。 2、盗一张学习CAS绝大多都看过的图以及执…

CAS实现单点登录

1.简介 SSO单点登录 在多个相互信任的系统中&#xff0c;用户只需要登录一次就可以访问其他受信任的系统。 新浪微博与新浪博客是相互信任的应用系统。 *当用户首次访问新浪微博时&#xff0c;新浪微博识别到用户未登录&#xff0c;将请求重定向到认证中心&#xff0c;认证中心…

cas server + cas client 单点登录 原理介绍

CAS 介绍 CAS 是 Yale 大学发起的一个开源项目&#xff0c;旨在为 Web 应用系统提供一种可靠的单点登录方法&#xff0c;CAS 在 2004 年 12 月正式成为 JA-SIG 的一个项目。CAS 具有以下特点&#xff1a; 开源的企业级单点登录解决方案。 CAS Server 为需要独立部署的 Web 应用…

CAS单点登录原理(包含详细流程,讲得很透彻,耐心看下去一定能看明白!)

转载地址 http://www.cnblogs.com/lihuidu/p/6495247.html 1、基于Cookie的单点登录的回顾 基于Cookie的单点登录核心原理&#xff1a; 将用户名密码加密之后存于Cookie中&#xff0c;之后访问网站时在过滤器&#xff08;filter&#xff09;中校验用户权限&#xff0c;如果没有…

CAS方式实现单点登录

单点登录&#xff0c;英文是 Single Sign On&#xff0c;缩写为 SSO。 多个站点(192.168.1.20X)共用一台认证授权服务器(192.168.1.110&#xff0c;用户数据库和认证授权模块共用)。用户经由其中任何一个站点(比如 192.168.1.201)登录后&#xff0c;可以免登录访问其他所有站点…

单点登录之CAS原理和实现

1.开源单点登录系统CAS入门 1.1 什么是单点登录 单点登录&#xff08;Single Sign On&#xff09;&#xff0c;简称为 SSO&#xff0c;是目前比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中&#xff0c;用户只需要登录一次就可以访问所有相互信任的应用…

CAS单点登录原理分析(一)

一&#xff0c;业务分析 在分布式系统架构中&#xff0c;假设把上述的三个子系统部署在三个不同的服务器上。前提是用户登录之后才能访问这些子系统。那么使用传统方式&#xff0c;可能会存在这样的问题&#xff1a; 1.当访问用户中心&#xff0c;需要用户登录帐号 2.当访问购物…

cas单点登录原理与实现(整合springsecurity)

一、cas原理分析 SSO英文全称Single Sign On&#xff0c;单点登录。SSO是在多个应用系统中&#xff0c;用户只需要登录一次就可以访问所有相互信任的应用系统。CAS是一种基于http协议的B/S应用系统单点登录实现方案&#xff0c;认识CAS之前首先要熟悉http协议、Session与Cookie…

CAS 实现单点登录(SSO)原理

原地址&#xff1a;https://blog.csdn.net/hejingyuan6/article/details/44277023 一、概念&#xff1a;     单点登录&#xff08;Single Sign On&#xff09;&#xff1a;简称为SSO&#xff0c;是目前比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中…

CAS单点登录原理

转载地址&#xff1a;转载博客 1、基于Cookie的单点登录的回顾 基于Cookie的单点登录核心原理&#xff1a; 将用户名密码加密之后存于Cookie中&#xff0c;之后访问网站时在过滤器&#xff08;filter&#xff09;中校验用户权限&#xff0c;如果没有权限则从Cookie中取出用户…

python iloc函数_如何使用python语言中pandas模块中的iloc方法

在python语言中,pandas模块中的iloc方法,可以截取矩阵,获取部分矩阵元素、行和列。下面利用实例说明iloc方法的用法,操作如下: 工具/原料 python pycharm 截图工具 方法/步骤 1 打开pycharm工具,新建python文件,导入numpy和pandas,调用DataFrame生成矩阵 2 保存…