springSecurity源码分析-springSecurityFilterChain
为什么在使用SpringSecurity时,web.xml文件里面的springSecurityFilterChain定义的过滤器的名字不可以改变?
问题:为什么DelegatingFilterProxy的filter-name必须是springSecurityFilterChain?
(1) 在web.xml文件中配置
<!-- 配置springSecurity的过滤器-->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
DelegatingFilterProxy并不是真正的Filter,在其initFilterBean方法中会从WebApplicationContext根据delegate来获取到
protected void initFilterBean() throws ServletException {
synchronized(this.delegateMonitor) {
if (this.delegate == null) {
if (this.targetBeanName == null) {
this.targetBeanName = this.getFilterName();
}
WebApplicationContext wac = this.findWebApplicationContext();
if (wac != null) {
this.delegate = this.initDelegate(wac);
}
}
}
}
在上这代码中this.targetBeanName=getFilterName()就是获取名称叫做springSecurityFilterChain
通过在doFilter就去中我们会发现真正干活的其实是delegate这个Filter,而delegate其实就是FilterChainProxy
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
Filter delegateToUse = this.delegate;
if (delegateToUse == null) {
synchronized(this.delegateMonitor) {
delegateToUse = this.delegate;
if (delegateToUse == null) {
WebApplicationContext wac = this.findWebApplicationContext();
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener or DispatcherServlet registered?");
}
delegateToUse = this.initDelegate(wac);
}
this.delegate = delegateToUse;
}
}
this.invokeDelegate(delegateToUse, request, response, filterChain);
}
FilterChainProxy是spring在解析配置文件时装配到上下文中,并且beanName为springSecurityFilterChain,因此在web.xml中需要配置filter-name为springSecurityFilterChain
(2) 在spring-security.xml文件中配置
在配置文件中我们主要使用标签来过多成配置
<!-- 配置不拦截的资源 -->
<security:http pattern="/login.jsp" security="none"/>
<security:http pattern="/failer.jsp" security="none"/>
<security:http pattern="/css/**" security="none"/>
<security:http pattern="/img/**" security="none"/>
<security:http pattern="/plugins/**" security="none"/>
<!--
配置具体的规则
auto-config="true" 不用自己编写登录的页面,框架提供默认登录页面
use-expressions="false" 是否使用SPEL表达式(没学习过)
-->
<security:http auto-config="true" use-expressions="false">
<!-- 配置具体的拦截的规则 pattern="请求路径的规则" access="访问系统的人,必须有ROLE_USER的角色" -->
<security:intercept-url pattern="/**" access="ROLE_USER,ROLE_ADMIN"/>
<!-- 定义跳转的具体的页面 -->
<security:form-login
login-page="/login.jsp"
login-processing-url="/login.do"
default-target-url="/index.jsp"
authentication-failure-url="/failer.jsp"
authentication-success-forward-url="/pages/main.jsp"
/>
<!-- 关闭跨域请求 -->
<security:csrf disabled="true"/>
<!-- 退出
只要访问logout.do,就可以帮助我们完成注销功能(security:logout ),并且能够帮助我们把session杀死(invalidate-session)
成功后跳转login.jsp页面
-->
<security:logout invalidate-session="true" logout-url="/logout.do" logout-success-url="/login.jsp" />
</security:http>
http标签是自定义标签,我们可以在spring-security-config包中查看
http\://www.springframework.org/schema/security=org.springframework.security.config.SecurityName
spaceHandler
继续查看SecurityNamespaceHandler类,在其init方法
public void init() {
loadParsers();
}
在loadParsers()方法中,指定由HttpSecurityBeanDefinitionParser进行解析
parsers.put(Elements.HTTP, new HttpSecurityBeanDefinitionParser());
在HttpSecurityBeanDefinitionParser完成具体解析的parse方法中
registerFilterChainProxyIfNecessary(pc, pc.extractSource(element));
这里就是注册了名为springSecurityFilterChain的filterChainProxy类
接下我们在看一下注册一系列Filter的地方createFilterChain,在这个方法中我们重点关注
AuthenticationConfigBuilder authBldr = new AuthenticationConfigBuilder(element,
forceAutoConfig, pc, httpBldr.getSessionCreationPolicy(),
httpBldr.getRequestCache(), authenticationManager,
httpBldr.getSessionStrategy(), portMapper, portResolver,
httpBldr.getCsrfLogoutHandler());
我们可以查看AuthenticationConfigBuilder创建代码
public AuthenticationConfigBuilder(Element element, boolean forceAutoConfig,
ParserContext pc, SessionCreationPolicy sessionPolicy,
BeanReference requestCache, BeanReference authenticationManager,
BeanReference sessionStrategy, BeanReference portMapper,
BeanReference portResolver, BeanMetadataElement csrfLogoutHandler) {
this.httpElt = element;
this.pc = pc;
this.requestCache = requestCache;
autoConfig = forceAutoConfig
| "true".equals(element.getAttribute(ATT_AUTO_CONFIG));
this.allowSessionCreation = sessionPolicy != SessionCreationPolicy.NEVER
&& sessionPolicy != SessionCreationPolicy.STATELESS;
this.portMapper = portMapper;
this.portResolver = portResolver;
this.csrfLogoutHandler = csrfLogoutHandler;
createAnonymousFilter();
createRememberMeFilter(authenticationManager);
createBasicFilter(authenticationManager);
createFormLoginFilter(sessionStrategy, authenticationManager);
createOpenIDLoginFilter(sessionStrategy, authenticationManager);
createX509Filter(authenticationManager);
createJeeFilter(authenticationManager);
createLogoutFilter();
createLoginPageFilterIfNeeded();
createUserDetailsServiceFactory();
createExceptionTranslationFilter();
}