CAS单点登录客户端跳转过程解析,主要针对客户端包cas-client-core
用户首次访问CAS单点登录客户端(对应流程图第1步),访问地址:http://localhost:8088/cas-client/

1. CAS单点登录客户端被客户端5个filter拦截
CAS单点登录客户端总共有5个filter,上述url都能匹配上,所以按顺序进入5个filter 。本文客户端分析源码采用cas-client-core-3.4.1版本。
一般在CAS单点登录客户端中的Web.xml里都会配置五个filter-mapping如下。
*客户端Web.xml:*
<filter-mapping>
<filter-name>CAS Single Sign Out Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CASFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CAS Validation Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CAS Assertion Thread Local Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
访问客户端时都能匹配这5个地址,每一个过滤器都会去执行。但是执行中根据不同的条件有不同的处理策略。
通过这5个filter-mapping我们来看一下具体的过滤器是什么。
*CAS单点登录客户端Web.xml:*
<!-- 该过滤器用于实现单点登出功能,单点退出配置,一定要放在其他filter之前 可选-->
<filter>
<filter-name>CAS Single Sign Out Filter</filter-name>
<filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>
<init-param>
<param-name>casServerUrlPrefix</param-name>
<param-value>http://localhost:8080/cas/</param-value>
</init-param>
</filter>
<!-- 该过滤器负责用户的认证工作,必须 -->
<filter>
<filter-name>CASFilter</filter-name>
<filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
<init-param>
<!--casServerLoginUrl:CAS单点登录服务端的登录url -->
<param-name>casServerLoginUrl</param-name>
<param-value>http://localhost:8080/cas/</param-value>
</init-param>
<init-param>
<!--serverName:CAS单点登录客户端本的ip+port -->
<param-name>serverName</param-name>
<param-value>http://localhost:8088</param-value>
</init-param>
<init-param>
<param-name>useSession</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>redirectAfterValidation</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<!-- 该过滤器负责对Ticket的校验工作,必须-->
<filter>
<filter-name>CAS Validation Filter</filter-name>
<filter-class>
org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter
</filter-class>
<init-param>
<param-name>casServerUrlPrefix</param-name>
<!-- CAS服务端的地址,注意不需要有login -->
<param-value>http://localhost:8080/cas/</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<!-- 客户端的serverName -->
<param-value>http://localhost:8088</param-value>
</init-param>
</filter>
<!-- 该过滤器对HttpServletRequest请求包装, 可通过HttpServletRequest的getRemoteUser()方法获得登录用户的登录名,可选 -->
<filter>
<filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
<filter-class>
org.jasig.cas.client.util.HttpServletRequestWrapperFilter
</filter-class>
</filter>
<!-- 该过滤器使得可以通过org.jasig.cas.client.util.AssertionHolder来获取用户的登录名。
比如AssertionHolder.getAssertion().getPrincipal().getName()。
这个类把Assertion信息放在ThreadLocal变量中,这样应用程序不在web层也能够获取到当前登录信息 -->
<filter>
<filter-name>CAS Assertion Thread Local Filter</filter-name>
<filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class>
</filter>
<!-- ****************** 单点登录结束 ********************-->
那么我们总结一下上面的这5个过滤器的具体作用吧
序号 Filter 类 功能
1 org.jasig.cas.client.session.SingleSignOutFilter 单点登出
2 org.jasig.cas.client.authentication.AuthenticationFilter 用户认证
3 org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter (超类为AbstractTicketValidationFilter) 对Ticket的校验
4 org.jasig.cas.client.util.HttpServletRequestWrapperFilter 可选,对HttpServletRequest请求包装,HttpServletRequest的getRemoteUser()方法获得登录用户的登录名
5 org.jasig.cas.client.util.AssertionThreadLocalFilter 可以通过org.jasig.cas.client.util.AssertionHolder来获取用户的登录名,如AssertionHolder.getAssertion().getPrincipal().getName()
为便于描述,后面在解析相关过滤器时会采用编号描述filter。
2. 进入第一个filter:SingleSignOutFilter
此类在client中的org.jasig.cas.client.session包下。
*SingleSignOutFilter.java*
public final class SingleSignOutFilter extends AbstractConfigurationFilter { public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
final FilterChain filterChain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) servletRequest;
final HttpServletResponse response = (HttpServletResponse) servletResponse;
/**
* <p>Workaround for now for the fact that Spring Security will fail since it doesn't call {@link #init(javax.servlet.FilterConfig)}.</p>
* <p>Ultimately we need to allow deployers to actually inject their fully-initialized {@link org.jasig.cas.client.session.SingleSignOutHandler}.</p>
*/
if (!this.handlerInitialized.getAndSet(true)) {
HANDLER.init();
}
//判断登出的处理
if (HANDLER.process(request, response)) {
filterChain.doFilter(servletRequest, servletResponse);
}
}
}
处理登出是在HANDLER.process()里。因为没有收到登出请求,所以这里什么都没做,进入下一个filter。后续我们再来讲解是如何登出的。
3. 进入第二个filter:AuthenticationFilter
AuthenticationFilter类在org.jasig.cas.client.authentication包下,主要用于CAS单点登录客户端的用户信息获取和判断。
*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;
}
final HttpSession session = request.getSession(false);
final Assertion assertion = session != null ? (Assertion) session.getAttribute(CONST_CAS_ASSERTION) : null;
//判断session中是否能够获取到客户端的用户信息,如果不为空,跳出本filter,//进入下一个filter
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);
//判断ticket是否为空或者是否要求发送网关请求,如果是,跳出本filter,进入下//一个filter
if (CommonUtils.isNotBlank(ticket) || wasGatewayed) {
filterChain.doFilter(request, response);
return;
}
final String modifiedServiceUrl;
logger.debug("no ticket and no assertion found");
if (this.gateway) {
logger.debug("setting gateway attribute in session");
modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl);
} else {
modifiedServiceUrl = serviceUrl;
}
logger.debug("Constructed service url: {}", modifiedServiceUrl);
//如果ticket和assertion都为空,那么就计算重定向到CAS单点登录服务端的地址
final String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl,
getProtocol().getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway);
logger.debug("redirecting to \"{}\"", urlToRedirectTo);
//重定向到CAS单点登录服务端,并将客户端地址作为service参数传入
this.authenticationRedirectStrategy.redirect(request, response, urlToRedirectTo);
}
…………
}
大致三步:
a>判断session是否为空,同时session中是否能获取到assertion,session如果不为空同时可以获取到assertion,跳出本filter,进入下一个filter(在未登录CAS服务端的本例中session为空)。
b>判断ticket是否为空或者是否发送网关请求,如果是,跳出本filter,进入下一个filter(本例在web.xml中的配置都不是)。
c>重定向到服务端,并将客户端地址作为service参数传入。
重定向到服务端地址:
http://localhost:8080/cas/login?service=http%3A%2F%2Flocalhost%3A8088%2Fcas-client%2F
重定向后,后面3个filter就不执行了。
至此,完成了流程图的第2步:

本文解析了CAS单点登录客户端的跳转过程,重点介绍了客户端包cas-client-core中的5个filter及其作用,包括单点登出、用户认证、Ticket校验等关键步骤。
--CAS单点登录客户端认证之客户端过滤拦截&spm=1001.2101.3001.5002&articleId=88966924&d=1&t=3&u=82459830d299406a9706b540f4922a8c)
4540

被折叠的 条评论
为什么被折叠?



