1.在pom.xml中添加maven坐标
<dependency>
<groupId>org.springframework.securitygroupId>
<artifactId>spring-security-configartifactId>
<version>4.0.3.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.securitygroupId>
<artifactId>spring-security-coreartifactId>
<version>4.0.3.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.securitygroupId>
<artifactId>spring-security-webartifactId>
<version>4.0.3.RELEASEversion>
dependency>
2.在web.xml中添加如下配置
<filter>
<filter-name>springSecurityFilterChainfilter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxyfilter-class>
filter>
<filter-mapping>
<filter-name>springSecurityFilterChainfilter-name>
/*
filter-mapping>
注意filter-name一定要写成springSecurityFilterChain。在DelegatingFilterProxy类init时,会获取filter-name,然后通过filter-name去spring中获取代理的bean。
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
if (isTargetFilterLifecycle()) {
delegate.init(getFilterConfig());
}
return delegate;
}
而spring-security的配置是由HttpSecurityBeanDefinitionParser解析器解析,每一个http都会被解析成一个SecurityFilterChain都添加到FilterChainProxy中的filterChains中。而且该FilterChainProxy会以springSecurityFilterChain注册到spring的bean中。
public BeanDefinition parse(Element element, ParserContext pc) {
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), pc.extractSource(element));
pc.pushContainingComponent(compositeDef);
registerFilterChainProxyIfNecessary(pc, pc.extractSource(element));
BeanDefinition listFactoryBean = pc.getRegistry().getBeanDefinition("org.springframework.security.filterChains");
List filterChains = (List)listFactoryBean.getPropertyValues().getPropertyValue("sourceList").getValue();
filterChains.add(this.createFilterChain(element, pc));
pc.popAndRegisterContainingComponent();
return null;
}
static void registerFilterChainProxyIfNecessary(ParserContext pc, Object source) {
if(!pc.getRegistry().containsBeanDefinition("org.springframework.security.filterChainProxy")) {
RootBeanDefinition listFactoryBean = new RootBeanDefinition(ListFactoryBean.class);
listFactoryBean.getPropertyValues().add("sourceList", new ManagedList());
pc.registerBeanComponent(new BeanComponentDefinition(listFactoryBean, "org.springframework.security.filterChains"));
BeanDefinitionBuilder fcpBldr = BeanDefinitionBuilder.rootBeanDefinition(FilterChainProxy.class);
fcpBldr.getRawBeanDefinition().setSource(source);
fcpBldr.addConstructorArgReference("org.springframework.security.filterChains");
fcpBldr.addPropertyValue("filterChainValidator", new RootBeanDefinition(DefaultFilterChainValidator.class));
AbstractBeanDefinition fcpBean = fcpBldr.getBeanDefinition();
pc.registerBeanComponent(new BeanComponentDefinition(fcpBean, "org.springframework.security.filterChainProxy"));
pc.getRegistry().registerAlias("org.springframework.security.filterChainProxy", "springSecurityFilterChain");
}
}
3.spring-security配置文件讲解
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-4.0.xsd ">
<http pattern="/resources/**" security="none"/>
<http pattern="/verify/**" security="none"/>
<http pattern="/user/login.htm" security="none"/>
<http pattern="/user/register.*" security="none"/>
<http pattern="/favicon.ico" security="none"/>
<http use-expressions="true" auto-config="false" entry-point-ref="authenticationProcessingFilterEntryPoint">
<intercept-url pattern="/**" access="authenticated"/>
<logout invalidate-session="true" logout-url="/logout" logout-success-url="/"/>
<csrf disabled="true"/>
<custom-filter ref="loginFilter" position="FORM_LOGIN_FILTER"/>
http>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="daoAuthenticationProvider" />
authentication-manager>
<beans:bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="hideUserNotFoundExceptions" value="false"/>
<beans:property name="userDetailsService" ref="userDetailsService"/>
<beans:property name="passwordEncoder" ref="md5Encoder"/>
<beans:property name="saltSource" ref="saltSource"/>
beans:bean>
<beans:bean id="md5Encoder" class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" />
<beans:bean id="authenticationProcessingFilterEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<beans:constructor-arg name="loginFormUrl" value="/user/login.htm" />
beans:bean>
<beans:bean id="loginFilter" class="com.test.security.LoginFilter">
<beans:property name="filterProcessesUrl" value="/login.json"/>
<beans:property name="usernameParameter" value="userName"/>
<beans:property name="authenticationManager" ref="authenticationManager"/>
<beans:property name="authenticationSuccessHandler" ref="authenticationSuccessHandler"/>
<beans:property name="authenticationFailureHandler" ref="authenticationFailureHandler"/>
beans:bean>
beans:beans>
程序在启动的时候会遍历解析spring-security配置文件,当命名空间是
的时候就使用HttpSecurityBeanDefinitionParser类来解析。
private BeanReference createFilterChain(Element element, ParserContext pc) {
boolean secured = !"none".equals(element.getAttribute("security"));
if(!secured) {
if(!StringUtils.hasText(element.getAttribute("pattern")) && !StringUtils.hasText("request-matcher-ref")) {
pc.getReaderContext().error("The \'security\' attribute must be used in combination with the \'pattern\' or \'request-matcher-ref\' attributes.", pc.extractSource(element));
}
for(int var15 = 0; var15 < element.getChildNodes().getLength(); ++var15) {
if(element.getChildNodes().item(var15) instanceof Element) {
pc.getReaderContext().error("If you are using to define an unsecured pattern, it cannot contain child elements." , pc.extractSource(element));
}
}
return this.createSecurityFilterChainBean(element, pc, Collections.emptyList());
} else {
BeanReference portMapper = this.createPortMapper(element, pc);
RuntimeBeanReference portResolver = this.createPortResolver(portMapper, pc);
ManagedList authenticationProviders = new ManagedList();
BeanReference authenticationManager = this.createAuthenticationManager(element, pc, authenticationProviders);
boolean forceAutoConfig = isDefaultHttpConfig(element);
HttpConfigurationBuilder httpBldr = new HttpConfigurationBuilder(element, forceAutoConfig, pc, portMapper, portResolver, authenticationManager);
AuthenticationConfigBuilder authBldr = new AuthenticationConfigBuilder(element, forceAutoConfig, pc, httpBldr.getSessionCreationPolicy(), httpBldr.getRequestCache(), authenticationManager, httpBldr.getSessionStrategy(), portMapper, portResolver, httpBldr.getCsrfLogoutHandler());
httpBldr.setLogoutHandlers(authBldr.getLogoutHandlers());
httpBldr.setEntryPoint(authBldr.getEntryPointBean());
httpBldr.setAccessDeniedHandler(authBldr.getAccessDeniedHandlerBean());
authenticationProviders.addAll(authBldr.getProviders());
ArrayList unorderedFilterChain = new ArrayList();
unorderedFilterChain.addAll(httpBldr.getFilters());
unorderedFilterChain.addAll(authBldr.getFilters());
unorderedFilterChain.addAll(this.buildCustomFilterList(element, pc));
Collections.sort(unorderedFilterChain, new OrderComparator());
this.checkFilterChainOrder(unorderedFilterChain, pc, pc.extractSource(element));
ManagedList filterChain = new ManagedList();
Iterator var13 = unorderedFilterChain.iterator();
while(var13.hasNext()) {
OrderDecorator od = (OrderDecorator)var13.next();
filterChain.add(od.bean);
}
return this.createSecurityFilterChainBean(element, pc, filterChain);
}
}
上面是解析源代码。当发现security="none"
的时候,则创建一个DefaultFilterChain添加到FilterChainProxy的filterChains属性中。当没有security="none"
则使用else中的代码。特别注意HttpConfigurationBuilder httpBldr = new HttpConfigurationBuilder(element, forceAutoConfig, pc, portMapper, portResolver, authenticationManager);
最后添加到filterChains中。
spring-security执行则是依据请求的URL获得过滤器链。然后依次执行。
private void doFilterInternal(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
FirewalledRequest fwRequest = firewall
.getFirewalledRequest((HttpServletRequest) request);
HttpServletResponse fwResponse = firewall
.getFirewalledResponse((HttpServletResponse) response);
List filters = getFilters(fwRequest);
if (filters == null || filters.size() == 0) {
if (logger.isDebugEnabled()) {
logger.debug(UrlUtils.buildRequestUrl(fwRequest)
+ (filters == null ? " has no matching filters"
: " has an empty filter list"));
}
fwRequest.reset();
chain.doFilter(fwRequest, fwResponse);
return;
}
VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
vfc.doFilter(fwRequest, fwResponse);
}
/**
* Returns the first filter chain matching the supplied URL.
*
* @param request the request to match
* @return an ordered array of Filters defining the filter chain
*/
private List getFilters(HttpServletRequest request) {
for (SecurityFilterChain chain : filterChains) {
if (chain.matches(request)) {
return chain.getFilters();
}
}
return null;
}
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if (currentPosition == size) {
if (logger.isDebugEnabled()) {
logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
+ " reached end of additional filter chain; proceeding with original chain");
}
// Deactivate path stripping as we exit the security filter chain
this.firewalledRequest.reset();
originalChain.doFilter(request, response);
}
else {
currentPosition++;
Filter nextFilter = additionalFilters.get(currentPosition - 1);
if (logger.isDebugEnabled()) {
logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
+ " at position " + currentPosition + " of " + size
+ " in additional filter chain; firing Filter: '"
+ nextFilter.getClass().getSimpleName() + "'");
}
nextFilter.doFilter(request, response, this);
}
}