今天有同事问子涵先生,cas获取用户数据的时候,是请求了什么接口?额,,,一时语顿……似乎问题没提在点子上。
请求什么接口……明明是客户端远程请求cas Server验证了ticket之后,由cas Server重定向送过来的嘛!当然你也可以理解为请求了什么接口,只是这个接口的参数是ticket和客户端的serviceUrl组成的,对,就是你登录的那个url。想了解更多,请阅读下文吧。
本以为cas源码中提供的仅一种方式,于是乎想一探究竟,居然发现了多种。
cas ticke验证方式大体上讲有2个体系:
有几个实现类
接下来看一下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<String,Object> 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;
}
当时这个项目是基于Filter集成的,在web.xml中配置了ticket验证的Filter。
<filter>
<filter-name>CAS Validation Filterfilter-name>
<filter-class>com.xxxx.fuie.filter.Cas20ProxyReceivingTicketValidationFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>UTF-8param-value>
init-param>
filter>
<filter-mapping>
<filter-name>CAS Validation Filterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
我们参考源码自定义了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();
//=======忽略不相干的代码===================
}
感谢您的赏读。客官,点赞、留言再走呗~或者留下您的问题我们一起探讨