CAS源码追踪系列二:AuthenticationFilter对于请求的处理

上一篇咱们说了在web项目中了和spring整合以后,如何进行对应Filter的初始化,若是你还没看过,请点击 《CAS源码追踪系列一:Filter的初始化》。 本篇咱们来看看在初始化完成之后,cas-client是如何处理请求的。java

源码地址:https://github.com/apereo/java-cas-clientgit

如何你还不太清楚sso的原理,你能够看看这篇文章《单点登陆原理与简单实现》。
当访问系统受保护的资源时,cas的过滤器AuthenticationFilter会对它进行拦截,发现用户没有登陆(系统中没有对应的session信息),就会跳转到统一登陆页面。不然调用FilterChain中下一个Filter。github

来看AuthenticationFilter的web

doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,final FilterChain filterChain)
if (isRequestUrlExcluded(request)) {
logger.debug("Request is ignored.");
filterChain.doFilter(request, response);
return;
}
判断请求是不是不须要被拦截,是,则进行直接调用FilterChain中下一个Filter,不然向下执行
来看看isRequestUrlExcluded方法spring

private boolean isRequestUrlExcluded(final HttpServletRequest request) {
/
是否有忽略url匹配类,
在AuthenticationFilter的initInternal(final FilterConfig filterConfig)进行初始化
*/
if (this.ignoreUrlPatternMatcherStrategyClass == null) {
return false;
}session

final StringBuffer urlBuffer = request.getRequestURL();
    if (request.getQueryString() != null) {
        urlBuffer.append("?").append(request.getQueryString());//拼装url
    }
    final String requestUri = urlBuffer.toString();
    /*
    *匹配url是否是不用被拦截,拦截规则须要用户在配置文件配置,
    *在AuthenticationFilter的initInternal(final FilterConfig *filterConfig)加载到ignoreUrlPatternMatcherStrategyClass
    */
    return this.ignoreUrlPatternMatcherStrategyClass.matches(requestUri);
}

final HttpSession session = request.getSession(false);//获取用户session,注意到参数是false,表示session没有的话不新建一个session
final Assertion assertion = session != null ? (Assertion) session.getAttribute(CONST_CAS_ASSERTION) : null;//若是有session,则从CONST_CAS_ASSERTION属性值获取断言
若是session不存在或者session中CONST_CAS_ASSERTION为空则表明用户还未登陆,则须要继续向下执行,不然执行下一个Filter。
final String serviceUrl = constructServiceUrl(request, response);//解析请求地址
该方法最终调用app

CommonUtils.constructServiceUrl(final HttpServletRequest request, final HttpServletResponse response,
final String service, final String serverNames, final String serviceParameterName,
final String artifactParameterName, final boolean encode);
@param service: 服务地址或者说是你要请求的地址,该方法更乐意你提供这个参数(可在配置文件配置)
@param serverName:服务名,如http://localhost:8080学习

if (CommonUtils.isNotBlank(service)) {
return encode ? response.encodeURL(service) : service;
}
代码一开始先对server参数进行校验,如何不为空,直接返回。这也就是为何为何指望你配置这个参数,逻辑简单啊。 若是不为空,则须要对请求的url进行解析。ui

final String serverName = findMatchingServerName(request, serverNames);
final URIBuilder originalRequestUrl = new URIBuilder(request.getRequestURL().toString(), encode);
originalRequestUrl.setParameters(request.getQueryString());this

final URIBuilder builder;
    if (!serverName.startsWith("https://") && !serverName.startsWith("http://")) {
        String scheme = request.isSecure() ? "https://" : "http://";
        builder = new URIBuilder(scheme + serverName, encode);
    } else {
        builder = new URIBuilder(serverName, encode);
    }

    if (builder.getPort() == -1 && !requestIsOnStandardPort(request)) {
        builder.setPort(request.getServerPort());
    }

    builder.setEncodedPath(builder.getEncodedPath() + request.getRequestURI());

根据url获取请求参数(request.getQueryString())时,会判断有没有携带ticket参数且必须在第一个位置(location==0),若是是就直接返回以前拼装的url,不然继续拼装参数 。

final List serviceParameterNames = Arrays.asList(serviceParameterName.split(","));
if (!serviceParameterNames.isEmpty() && !originalRequestUrl.getQueryParams().isEmpty()) {
for (final URIBuilder.BasicNameValuePair pair : originalRequestUrl.getQueryParams()) {
String name = pair.getName();
if (!name.equals(artifactParameterName) && !serviceParameterNames.contains(name)) {
if (name.contains("&") || name.contains("=") ){
URIBuilder encodedParamBuilder = new URIBuilder();
encodedParamBuilder.setParameters(name);
for (final URIBuilder.BasicNameValuePair pair2 :encodedParamBuilder.getQueryParams()){
String name2 = pair2.getName();
if (!name2.equals(artifactParameterName) && !serviceParameterNames.contains(name2)) {
builder.addParameter(name2, pair2.getValue());
}
}
} else {
builder.addParameter(name, pair.getValue());
}
}
}
主要就是过滤掉名为“ticket”的参数。
再回到AuthenticationFilter

final String ticket = retrieveTicketFromRequest(request);//获取请求中的ticket
而后判断若是有ticket并且设置了网关,就直接调用下一个Filter,不然继续向下执行。

final String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl,
getProtocol().getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway);
根据已有的数据构造一个重定向url,也就是对于请求认证失败的时候跳转的地址,相似于https://localhost:8443/cas/login?service=https%3A%2F%2Flocalhost%3A8443%2Ftest%3Ftest%3D12456%26sss%3D111。“?”前面部分为认证中心登陆页面,后面则为登陆成功以后要跳转的地址。

最后一步就是执行重定向。

总结

这一片咱们简单分析了cas-client如何处理请求,对于认证成功的继续执行下一个Filter,失败的则跳转到登陆页面。下一篇咱们会讲cas-server端是如何处理登陆的。

平时的学习过程记录一下,没有那么高深,但愿能帮到你们,与君共同进步。我是敲代码的小鲁班,喜欢的话给个推荐,点赞,关注吧。

相关文章
相关标签/搜索