cas深度解析:cas 客户端是如何验证serviceTicket(简称st)的?

cas深度解析:cas 客户端是如何验证serviceTicket(简称st)的?

今天有同事问子涵先生,cas获取用户数据的时候,是请求了什么接口?额,,,一时语顿……似乎问题没提在点子上。 请求什么接口……明明是客户端远程请求cas Server验证了ticket之后,由cas Server重定向送过来的嘛!当然你也可以理解为请求了什么接口,只是这个接口的参数是ticket和客户端的serviceUrl组成的,对,就是你登录的那个url。想了解更多,请阅读下文吧。

文章目录

一、Cas ticket两种验证方式1-1 基于TicketValidator1-2 Filter

一、Cas ticket两种验证方式

本以为cas源码中提供的仅一种方式,于是乎想一探究竟,居然发现了多种。 cas ticke验证方式大体上讲有2个体系:

AbstractTicketValidationFilter,基于Filter的ticket的验证;AbstractUrlBasedTicketValidator,基于TicketValidator的验证;

1-1 基于TicketValidator

有几个实现类

Cas20ServiceTicketValidatorCas20ProxyTicketValidator等等

接下来看一下Cas20ServiceTicketValidator类图:

AbstractUrlBasedTicketValidator中有个validate方法:

public Assertion validate(final String ticket, final String service) throws TicketValidationException {

final String validationUrl = constructValidationUrl(ticket, service);

if (log.isDebugEnabled()) {

log.debug("Constructing validation url: " + validationUrl);

}

try {

log.debug("Retrieving response from server.");

//从后端访问cas Server验证st的有效性

final String serverResponse = retrieveResponseFromServer(new URL(validationUrl), ticket);

if (serverResponse == null) {

throw new TicketValidationException("The CAS server returned no response.");

}

if (log.isDebugEnabled()) {

log.debug("Server response: " + serverResponse);

}

//根据服务端的返回信息解析用户数据

return parseResponseFromServer(serverResponse);

} catch (final MalformedURLException e) {

throw new TicketValidationException(e);

}

}

org.jasig.cas.client.validation.Cas20ServiceTicketValidator#parseResponseFromServer

protected final Assertion parseResponseFromServer(final String response) throws TicketValidationException {

final String error = XmlUtils.getTextForElement(response,

"authenticationFailure");

if (CommonUtils.isNotBlank(error)) {

throw new TicketValidationException(error);

}

final String principal = XmlUtils.getTextForElement(response, "user");

final String proxyGrantingTicketIou = XmlUtils.getTextForElement(response, "proxyGrantingTicket");

final String proxyGrantingTicket = this.proxyGrantingTicketStorage != null ? this.proxyGrantingTicketStorage.retrieve(proxyGrantingTicketIou) : null;

if (CommonUtils.isEmpty(principal)) {

throw new TicketValidationException("No principal was found in the response from the CAS server.");

}

final Assertion assertion;

final Map attributes = extractCustomAttributes(response);

if (CommonUtils.isNotBlank(proxyGrantingTicket)) {

final AttributePrincipal attributePrincipal = new AttributePrincipalImpl(principal, attributes, proxyGrantingTicket, this.proxyRetriever);

assertion = new AssertionImpl(attributePrincipal);

} else {

assertion = new AssertionImpl(new AttributePrincipalImpl(principal, attributes));

}

customParseResponse(response, assertion);

return assertion;

}

1-2 Filter

当时这个项目是基于Filter集成的,在web.xml中配置了ticket验证的Filter。

CAS Validation Filter

com.xxxx.fuie.filter.Cas20ProxyReceivingTicketValidationFilter

encoding

UTF-8

CAS Validation Filter

/*

我们参考源码自定义了com.xxxx.fuie.filter.Cas20ProxyReceivingTicketValidationFilter并重新替换了CAS Validation Filter中的class。该class类图如下:

AbstractTicketValidationFilter负责解析用户数据,并放到session中,此处对于cas登录流程不展开描述了,请自行搜索,或结合server端的源码中的login-webflow进行查看。 com.xxxx.fuie.filter.Cas20ProxyReceivingTicketValidationFilter源码片段如下:

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;

final String ticket = CommonUtils.safeGetParameter(request, getArtifactParameterName());

if (CommonUtils.isNotBlank(ticket)) {

if (log.isDebugEnabled()) {

log.debug("Attempting to validate ticket: " + ticket);

}

try {

final Assertion assertion = this.ticketValidator.validate(ticket, constructServiceUrl(request, response));

if (log.isDebugEnabled()) {

log.debug("Successfully authenticated user: " + assertion.getPrincipal().getName());

}

//用户信息放入request

request.setAttribute(CONST_CAS_ASSERTION, assertion);

// 用户信息放入session

if (this.useSession) {

request.getSession().setAttribute(CONST_CAS_ASSERTION, assertion);

}

onSuccessfulValidation(request, response, assertion);

if (this.redirectAfterValidation) {

log. debug("Redirecting after successful ticket validation.");

response.sendRedirect(constructServiceUrl(request, response));

return;

}

} catch (final TicketValidationException e) {

response.setStatus(HttpServletResponse.SC_FORBIDDEN);

log.warn(e, e);

onFailedValidation(request, response);

if (this.exceptionOnValidationFailure) {

throw new ServletException(e);

}

return;

}

}

filterChain.doFilter(request, response);

}

用户认证完成后,就可以从此次request中获取用户信息:

@Override

public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) {

this.outTarget = target;

this.request = request;

int index = target.lastIndexOf(";jsessionid");

if (index > -1) {

outTarget = target.substring(0, index);

}

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

//=======忽略不相干的代码===================

}

感谢您的赏读。客官,点赞、留言再走呗~或者留下您的问题我们一起探讨

相关推荐

高希霸世纪一号
365bet体育娱乐

高希霸世纪一号

📅 07-18 👁️ 6820
DNF花篮奖励“更新”战灵天舞来临,策划留一手,玩家苦不堪言
电脑屏幕录像软件哪个好?这五款屏幕录像工具实打实推荐,记录精彩的瞬间!
Gradle构建加速终极指南:从配置镜像到最佳实践
为什么藏獒吃人
365bet足球平台

为什么藏獒吃人

📅 07-16 👁️ 265
阳历的一月份是什么星座
web服务中80端口及相关端口内容(包含各个端口的功能自己整理)
联想ThinkPad T400系列
石家庄区号查询