2.Spring Security 实现原理和基本的过程,为了理解起来方便:先写我们较熟悉的场景——用户登陆-> 用户授权。
1)首先配置一个过滤器,很容易理解,我们web容器在开始操作前都会经过filter(实际上spring以DelegatingFilterProxy为入口,这个类里面存储了FilterChainProxy实例,至于说这些是什么意思我们向下看)
<!-- Spring-Security -->
<filter>
<filter-name>mySpringSecurityFilterChain</filter-name>
<filter-class>cn.com.nuskin.agelocme.config.DelegatingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
public class DelegatingFilter extends DelegatingFilterProxy{
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
HttpServletRequest httpRequest=(HttpServletRequest)request;
String url = httpRequest.getRequestURI();
if (url.indexOf("admin") != -1){
super.doFilter(request, response, filterChain);
return;
}else{
super.doFilter(request, response, filterChain);
}
}
我们可以看到 这样我们是做了准备工作.关注一下DelegatingFilterProxy这个类,我们可以在配置文件里直接使用该类,不用重新继承,这样做就是为了拓展.
DelegatingFilterProxy类继承于抽象类GenericFilterBean,间接地implement 了javax.servlet.Filter接口,Servlet容器在启动时,首先会调用Filter的i nit方法。GenericFilterBean实现了filter那么我们看init方法里做了什么。
@Override
public final void init(FilterConfig filterConfig) throws ServletException {
Assert.notNull(filterConfig, "FilterConfig must not be null");
if (logger.isDebugEnabled()) {
logger.debug("Initializing filter '" + filterConfig.getFilterName() + "'");
}
this.filterConfig = filterConfig;
// Set bean properties from init parameters.
try {
PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
String msg = "Failed to set bean properties on filter '" +
filterConfig.getFilterName() + "': " + ex.getMessage();
logger.error(msg, ex);
throw new NestedServletException(msg, ex);
}
// Let subclasses do whatever initialization they like.
<span style="color:#cc0000;">initFilterBean();</span>
if (logger.isDebugEnabled()) {
logger.debug("Filter '" + filterConfig.getFilterName() + "' configured successfully");
}
}
看着红色部分,init方法设置了一些参数信息后调用initFilterBean来初始化filterBean该方法其实留给了子类来实现。这里其实就是我们的DelegatingFilterProxy initFilterBean方法。
@Override
protected void initFilterBean() throws ServletException {
synchronized (this.delegateMonitor) {
if (this.delegate == null) {
// If no target bean name specified, use filter name.
if (this.targetBeanName == null) {
this.targetBeanName = getFilterName();
}
// Fetch Spring root application context and initialize the delegate early,
// if possible. If the root application context will be started after this
// filter proxy, we'll have to resort to lazy initialization.
WebApplicationContext wac = findWebApplicationContext();
if (wac != null) {
this.delegate = initDelegate(wac);
}
}
}
}
意思很好理解如果我们配置了targetBeanName那么我们就会去加载这个配置的filter,否则就是我们的targetBeanName就是springSecurityFilterChain(该名称spring里已经默认好spring org.springframework.security.FilterChainProxy实例的名称)。此处我们并没有设置这个参数的值那么就会取到默认的值。
然后最终通过initDelegate方法获取了我们的filter实例同时设置到了DelegatingFilterProxy类的delegate属性里。实例是org.springframework.security.FilterChainProxy(该类也是继承了GenericFilterBean)。最后通过DelegatingFilterProxy invokeDelegate(delegateToUse, request, response, filterChain)方法响应处理请求。
总结:这里主要有俩个filter一个代理类DelegatingFilterProxy 一个实际起作用spring org.springframework.security.FilterChainProxy该过滤器。这样
使得FilterChainProxy 被代理了在DelegatingFilterProxy 下。这样做的好处是FilterChainProxy 独立出来可以动态配置,可以是默认的spring提供,也可以根据
根据targetBeanName来自定义过滤器,一般情况下我们就默认。下面就是来说们为什么 FilterChainProxy 如何配置,怎么就通过initDelegate方法获取到了实例了?
2)FilterChainProxy配置和原理
首先看一下initDelegate方法:
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
if (isTargetFilterLifecycle()) {
delegate.init(getFilterConfig());
}
return delegate;
}
关键点就在于getTargetBeanName方法。要想搞明白我们就得来说一下上文说到的security.xml,看个简单的
<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.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<http auto-config='true'>
<intercept-url pattern="/**" access="ROLE_USER" />
<http-basic />
</http>
</beans:beans>
auto-config="true"属性,spring security就会自动配置好了过滤器链。其中原理可以参考:http://dead-knight.iteye.com/blog/1511389 博客。
那么我们说到的过滤连是什么意思呢,那么如何自定义?
<bean id="securityFilterChainProxy"
class="org.springframework.security.web.FilterChainProxy">
<constructor-arg>
<list>
<security:filter-chain pattern="/services/**"
filters="none" />
<security:filter-chain pattern="/test*" filters="none" />
<security:filter-chain pattern="/**"
filters="person1Filter,person2Filter," />
</list>
</constructor-arg>
</bean>
spring在加载配置文件 加载securityFilterChainProxy就会初始化该过滤链注意bean id 必须是securityFilterChainProxy因为我的web.xml DelegatingFilter代理过滤器没有配置自定的filter默认的spring securityFilterChainProxy的过滤器,这里我们只是给他设置了一些自定义过滤连参数。这么多都是我们的前置配置。当然自己定义了实际的过滤器
例如:
<init-param>
<param-name>targetBeanName</param-name>
<param-value>myFilter</param-value> //自己过滤器的名字
</init-param>
然后自己可以配置bean id =myFilter的bean即可。下面就是实际的认证授权了待续