CAS单点登录开源框架解读(七)--CAS单点登录客户端认证之客户端过滤拦截

本文解析了CAS单点登录客户端的跳转过程,重点介绍了客户端包cas-client-core中的5个filter及其作用,包括单点登出、用户认证、Ticket校验等关键步骤。

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步:
单点登录客户端重定向到服务端

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

行走的一只鞋

你的肯定是对我最大赞赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值