CAS之六--CAS认证之客户端向服务端发起请求
客户端是如何向服务端发起请求的
前面客户端部分讲过,客户端的web.xml里配置了五个filter。下文中会使用序号来表示,具体的过滤器名称可以看上一章节。
1. 调用第1个filter
首先进入第一个filter:SingleSignOutFilter,因为现在是进行登录认证,没有登出请求,所以什么都没做,进入下一个filter。
2. 调用第2个filter
*AuthenticationFilter.java* public class AuthenticationFilter extends AbstractCasFilter { public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException { final HttpServletRequest request = (HttpServletRequest) servletRequest; final HttpServletResponse response = (HttpServletResponse) servletResponse; if (isRequestUrlExcluded(request)) { logger.debug("Request is ignored."); filterChain.doFilter(request, response); return; } //此时重定向回来,还没有创建session,因此不能获取到assertion 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; } final String serviceUrl = constructServiceUrl(request, response); final String ticket = retrieveTicketFromRequest(request); final boolean wasGatewayed = this.gateway && this.gatewayStorage.hasGatewayedAlready(request, serviceUrl); //由于CAS服务端认证成功后重定向到客户端会带ticket参数,因此ticket不为空进入此代码中 if (CommonUtils.isNotBlank(ticket) || wasGatewayed) { filterChain.doFilter(request, response); return;//执行到这 } ...... }
执行到对ticket的判断,CAS单点登录服务端认证成功后会带上ticket重定向到客户端,因为此时ticket不为空,所以进入下一个filter。
4. 调用第3个filter
该类Cas20ProxyReceivingTicketValidationFilter在org.jasig.cas.client.validation包中继承自AbstractTicketValidationFilter。首先执行doFilter方法,注意doFilter是在父类中进行调用的,我们先分析父类方法。
*AbstractTicketValidationFilter.java* public abstract class AbstractTicketValidationFilter extends AbstractCasFilter { public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException { if (!preFilter(servletRequest, servletResponse, filterChain)) { return; } final HttpServletRequest request = (HttpServletRequest) servletRequest; final HttpServletResponse response = (HttpServletResponse) servletResponse; //从请求中获取到ticket final String ticket = retrieveTicketFromRequest(request); //判断ticket是否为空 if (CommonUtils.isNotBlank(ticket)) { logger.debug("Attempting to validate ticket: {}", ticket); try { //通过ticket和service去校验ticket,并返回assertion用户认证信息 final Assertion assertion = this.ticketValidator.validate(ticket, constructServiceUrl(request, response)); logger.debug("Successfully authenticated user: {}", assertion.getPrincipal().getName()); request.setAttribute(CONST_CAS_ASSERTION, assertion); if (this.useSession) { //如果需要使用session,则把获取到的assertion认证信息放入会话 request.getSession().setAttribute(CONST_CAS_ASSERTION, assertion); } onSuccessfulValidation(request, response, assertion); if (this.redirectAfterValidation) { //认证成功后重定向到客户端的service地址 logger.debug("Redirecting after successful ticket validation."); response.sendRedirect(constructServiceUrl(request, response)); return; } } catch (final TicketValidationException e) { logger.debug(e.getMessage(), e); onFailedValidation(request, response); if (this.exceptionOnValidationFailure) { throw new ServletException(e); } response.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage()); return; } } filterChain.doFilter(request, response); } ………… }
通过ticketValidator.validate(ticket, constructServiceUrl(request, response))对ticket进行校验。下面我们分析是如何进行ticket校验的。
5. 调用ticketValidator.validate校验ticket
AbstractUrlBasedTicketValidator类在org.jasig.cas.client.validation包中。
*AbstractUrlBasedTicketValidator.java* public abstract class AbstractUrlBasedTicketValidator implements TicketValidator { ………… public final Assertion validate(final String ticket, final String service) throws TicketValidationException { //计算需要请求到CAS服务端的url地址(注意这里是普通登录模式,所以校验地址为:serviceValidate final String validationUrl = constructValidationUrl(ticket, service); logger.debug("Constructing validation url: {}", validationUrl); try { logger.debug("Retrieving response from server."); //到CAS单点登录服务进行校验,获取返回结果 final String serverResponse = retrieveResponseFromServer(new URL(validationUrl), ticket); if (serverResponse == null) { throw new TicketValidationException("The CAS server returned no response."); } logger.debug("Server response: {}", serverResponse); //解析从服务端获取到的信息 return parseResponseFromServer(serverResponse); } catch (final MalformedURLException e) { throw new TicketValidationException(e); } } …………
注意retrieveResponseFromServer()会访问CAS单点登录服务端。
6. 调用retrieveResponseFromServer请求CAS单点登录服务端
AbstractCasProtocolUrlBasedTicketValidator类在org.jasig.cas.client.validation包中。
*AbstractCasProtocolUrlBasedTicketValidator.java* public abstract class AbstractCasProtocolUrlBasedTicketValidator extends AbstractUrlBasedTicketValidator { protected AbstractCasProtocolUrlBasedTicketValidator(final String casServerUrlPrefix) { super(casServerUrlPrefix); } /** * Retrieves the response from the server by opening a connection and merely reading the response. */ protected final String retrieveResponseFromServer(final URL validationUrl, final String ticket) { return CommonUtils.getResponseFromServer(validationUrl, getURLConnectionFactory(), getEncoding()); } }
最终调用CommonUtils.getResponseFromServer(),我们还要再往下找。
7. 调用CommonUtils.getResponseFromServer()
最终其实是通过后端发起http请求获取到cas服务端的数据。
## CommonUtils.java public final class CommonUtils { ………… public static String getResponseFromServer(final URL constructedUrl, final HttpURLConnectionFactory factory, final String encoding) { HttpURLConnection conn = null; InputStreamReader in = null; try { //创建http的连接 conn = factory.buildHttpURLConnection(constructedUrl.openConnection()); if (CommonUtils.isEmpty(encoding)) { in = new InputStreamReader(conn.getInputStream()); } else { in = new InputStreamReader(conn.getInputStream(), encoding); } final StringBuilder builder = new StringBuilder(255); int byteRead; //通过输入流获取到服务端的响应 while ((byteRead = in.read()) != -1) { builder.append((char) byteRead); } //通过流创建返回的数据 return builder.toString(); } catch (final Exception e) { LOGGER.error(e.getMessage(), e); throw new RuntimeException(e); } finally { closeQuietly(in); if (conn != null) { conn.disconnect(); } } } ………… }
constructedUrl的传入值为:http://localhost:8080/cas/serviceValidate?ticket=ST-2-ZRix41JWjd3oHDoN3DhI-cas01.example.org&service=http%3A%2F%2Flocalhost%3A8088%2Fcas-client%2F
开始向服务端发验证请求,验证ticket,至此,完成了流程图的第5步
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下