CAS源码追踪系列一:Filter的初始化
最近研究了一下SSO(Single Sign On:单点登录)原理。
于是想借助CAS(基于SSO原理的实现框架)加深一下理解同时参考一下具体代码实现,因此有了此系列文章。
先从CAS-CLIENT说起。
假设你已经掌握了如何在你的web项目中引入CAS。我们以AuthenticationFilter为例,说一说它是如何从初始化的。
代码跟踪
Spring-web:DelegatingFilterProxy
在web项目中的web.xml文件中我们通常通过如下方式进行spring和cas的整合:
<bean id="authenticationFilter" class="org.jasig.cas.client.authentication.AuthenticationFilter">
<property name="casServlerLoginUrl">xxx</property>
<property name="serverName">xxx</property>
</bean>
<filter>
<filter-name>casAuthenticationFilter</filter-name>
<filter-class>
org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
<init-param>
<param-name>targetBeanName</param-name>
<param-value>authenticationFilter</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>casAuthenticationFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
可以看到引入了一个名为DelegatingFilterProxy的Filter。那我们来看一下该类的源码:
public class DelegatingFilterProxy extends GenericFilterBean {
private String contextAttribute;//上下文属性,寻找WebApplicationContext
private WebApplicationContext webApplicationContext;//web上下文
private String targetBeanName;//被委托的Filter的名字,如果没有指定,则使用DelegatingFilterProxy对应的Filter的name,即上面的<filter-name>标签的内容。
private boolean targetFilterLifecycle;//是否是目标Filter的生命周期,默认为false,即由Spring来管理Filter的生命周期,否则由Servlet来管理。
private Filter delegate;//被委托的过滤器
private final Object delegateMonitor;//监视器
...
该类是实现了Filter接口并交由spring管理的servlet过滤器的代理类。
因为这个类也是实现了Filter接口,所以在tomcat容器初始化是会执行init(FilterConfig)方法。该方法来自其父类GenericFilterBean,来看代码:
public final void init(FilterConfig filterConfig) throws ServletException {
...
this.filterConfig = filterConfig;
...
this.initFilterBean();
}
注意到this.initFilterBean(),该方法来自DelegatingFilterProxy,看源码:
protected void initFilterBean() throws ServletException {
...
WebApplicationContext wac = this.findWebApplicationContext();//获取应用上下文
if (wac != null) {
this.delegate = this.initDelegate(wac);//初始化委托
...
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
Filter delegate = (Filter)wac.getBean(this.getTargetBeanName(), Filter.class);//从应用上下文中获取名为targetBeanName的bean,也就是被委托的Filter
if (this.isTargetFilterLifecycle()) {
delegate.init(this.getFilterConfig());//调用Filter的初始化方法
}
return delegate;
}
CAS:AuthenticationFilter
AuthenticationFilter继承了AbstractCasFilter。
上面说到调用Filter自己的初始化方法。对于AuthenticationFilter,因为自己没有重写init(FilterConfig),则会调用从其父类AbstractCasFilter继承来的init(FilterConfig)方法:
public final void init(FilterConfig filterConfig) throws ServletException {
if (!this.isIgnoreInitConfiguration()) {
...
this.initInternal(filterConfig);//内部初始化
}
this.init();//自定义初始化逻辑
}
注意:你会发现AuthenticationFilter和其父类都有initInternal(filterConfig)和init()方法,这里进入的是AuthenticationFilter,所以回去调用AuthenticationFilter中对应的方法:
protected void initInternal(FilterConfig filterConfig) throws ServletException {//将filterConfig设置到WebXmlConfigurationStrategyImpl以及设置一些自己的属性值
...
super.initInternal(filterConfig);//此时才去调用其父类的initInternal(filterConfig)
...
}
public void init() {
super.init();//此时才去调用其父类的init()
...
}
直观的图示(非专业,手动滑稽~):
总结
本文从web项目和spring的整合入手,以cas中的AuthenticationFilter为例(其他类型的Filter类似)跟踪代码分析如何走到他自己的初始化逻辑。后续会有初始化之后对请求的拦截、cas服务端的处理等分析。
码字整理不易,如何你觉得写的还能看的话请赏一个赞或者推荐吧,如果写的不对请直接评论纠正,毕竟我还是一个在路上的小鲁班呢~
欢迎关注我的公众号,不定期更新学习笔记和视频文档学习资料哦~