Spring Security3学习-过滤器链

Spring Security3是目前使用非常广泛的java web安全框架,我经历的项目中有很多在使用它。尽管有shiro等使用更方便、更容易理解、应用范围更广的安全框架开始流行,但Spring Security3在Java web领域无疑是更强大、更容易扩展的。

对Spring Security3配置、应用的blog很多,我就不再介绍这些了。此系列文章主要根据我学习Spring Security3的过程,沿着Filter链介绍Spring Security3的原理。

启用Spring Security3需要做如下三件事

    1、在web.xml配置配置过滤器DelegatingFilterProxy

 
  springSecurityFilterChain 
   
    org.springframework.web.filter.DelegatingFilterProxy 
   
 
 
  springSecurityFilterChain 
  /* 

    这里filter的名字必须是springSecurityFilterChain,原因稍后解释。

  2、新建spring-sucirity.xml

 
 
   
     
   
   
     
       
         
       
     
    
    3、添加spring-security.xml到web.xml 

	contextConfigLocation
	classpath*:/spring-security.xml

下面开始从入DelegatingFilterProxy手

从名字上可以看出,这个过滤器其实没做什么,只是委派给了其他过滤器。它并不在spring-security包中,而是在spring-web包
Spring Security3学习-过滤器链_第1张图片
 看它的doFilter方法

public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		// Lazily initialize the delegate if necessary.
		Filter delegateToUse = null;
		synchronized (this.delegateMonitor) {
			if (this.delegate == null) {
				WebApplicationContext wac = findWebApplicationContext();
				if (wac == null) {
					throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener registered?");
				}
                                //初始化FilterChain代理
				this.delegate = initDelegate(wac);
			}
			delegateToUse = this.delegate;
		}

		// Let the delegate perform the actual doFilter operation.
                //调用doFilter方法
		invokeDelegate(delegateToUse, request, response, filterChain);
	}

 分析initDelegate方法 

protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
		Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
		if (isTargetFilterLifecycle()) {
			delegate.init(getFilterConfig());
		}
		return delegate;
	}
 getTargetBeanName()其实获取到的是Filter名字springSecurityFilterChain:在initFilterBean()方法中对targetBeanName做了赋值
protected void initFilterBean() throws ServletException {
		// If no target bean name specified, use filter name.
		if (this.targetBeanName == null) {
                        //targetBeanName被设置为filterName
			this.targetBeanName = getFilterName();
		}
                ......
	}
 initFilterBean方法在父类GenericFilterBean的init方法中被执行。
这里根据我们在web.xml设置filter的名字去获取Spring Seucurity3的代理过滤器。后面回看到这个名字是springSecurityFilterChain,所以在web.xml的filter名字也必须是这个。
 再分析spring-scurity.xml的解析
org.springframework.security.config.SecurityNamespaceHandler负责解析namespace为security的xml文档,即我们的spring-security.xml文件,它在spring-security-config包:
Spring Security3学习-过滤器链_第2张图片
SecurityNamespaceHandler继承了NamespaceHandler接口,有三个方法:init初始化,parse解析,decorate装饰,在SecurityNamespaceHandler的init方法进行了每个解析类的注册
private void loadParsers() {
        // Parsers
        parsers.put(Elements.LDAP_PROVIDER, new LdapProviderBeanDefinitionParser());
        parsers.put(Elements.LDAP_SERVER, new LdapServerBeanDefinitionParser());
        parsers.put(Elements.LDAP_USER_SERVICE, new LdapUserServiceBeanDefinitionParser());
        parsers.put(Elements.USER_SERVICE, new UserServiceBeanDefinitionParser());
        parsers.put(Elements.JDBC_USER_SERVICE, new JdbcUserServiceBeanDefinitionParser());
        parsers.put(Elements.AUTHENTICATION_PROVIDER, new AuthenticationProviderBeanDefinitionParser());
        parsers.put(Elements.GLOBAL_METHOD_SECURITY, new GlobalMethodSecurityBeanDefinitionParser());
        parsers.put(Elements.AUTHENTICATION_MANAGER, new AuthenticationManagerBeanDefinitionParser());

        // Only load the web-namespace parsers if the web classes are available
        if (ClassUtils.isPresent("org.springframework.security.web.FilterChainProxy", getClass().getClassLoader())) {
            parsers.put(Elements.HTTP, new HttpSecurityBeanDefinitionParser());
            parsers.put(Elements.HTTP_FIREWALL, new HttpFirewallBeanDefinitionParser());
            parsers.put(Elements.FILTER_INVOCATION_DEFINITION_SOURCE, new FilterInvocationSecurityMetadataSourceParser());
            parsers.put(Elements.FILTER_SECURITY_METADATA_SOURCE, new FilterInvocationSecurityMetadataSourceParser());
            filterChainMapBDD = new FilterChainMapBeanDefinitionDecorator();
        }
    }
 HttpSecurityBeanDefinitionParser时解析http节点的解析类
parsers.put(Elements.HTTP, new HttpSecurityBeanDefinitionParser());
 看一下HttpSecurityBeanDefinitionParser是如何解析http节点的,上parse
    public BeanDefinition parse(Element element, ParserContext pc) {
        CompositeComponentDefinition compositeDef =
            new CompositeComponentDefinition(element.getTagName(), pc.extractSource(element));
        pc.pushContainingComponent(compositeDef);
        final Object source = pc.extractSource(element);

        final String portMapperName = createPortMapper(element, pc);
        final UrlMatcher matcher = createUrlMatcher(element);

        HttpConfigurationBuilder httpBldr = new HttpConfigurationBuilder(element, pc, matcher, portMapperName);

        httpBldr.parseInterceptUrlsForEmptyFilterChains();
        httpBldr.createSecurityContextPersistenceFilter();
        httpBldr.createSessionManagementFilters();

        ManagedList authenticationProviders = new ManagedList();
        BeanReference authenticationManager = createAuthenticationManager(element, pc, authenticationProviders, null);

        httpBldr.createServletApiFilter();
        httpBldr.createChannelProcessingFilter();
        httpBldr.createFilterSecurityInterceptor(authenticationManager);

        AuthenticationConfigBuilder authBldr = new AuthenticationConfigBuilder(element, pc,
                httpBldr.isAllowSessionCreation(), portMapperName);
        //http节点的默认的11个过滤器
        authBldr.createAnonymousFilter();
        authBldr.createRememberMeFilter(authenticationManager);
        authBldr.createRequestCache();
        authBldr.createBasicFilter(authenticationManager);
        authBldr.createFormLoginFilter(httpBldr.getSessionStrategy(), authenticationManager);
        authBldr.createOpenIDLoginFilter(httpBldr.getSessionStrategy(), authenticationManager);
        authBldr.createX509Filter(authenticationManager);
        authBldr.createLogoutFilter();
        authBldr.createLoginPageFilterIfNeeded();
        authBldr.createUserServiceInjector();
        authBldr.createExceptionTranslationFilter();

        List unorderedFilterChain = new ArrayList();
        //将过滤器加入过滤器链
        unorderedFilterChain.addAll(httpBldr.getFilters());
        unorderedFilterChain.addAll(authBldr.getFilters());//这里加入的过滤器和我们设置auto-config="true"有关

        authenticationProviders.addAll(authBldr.getProviders());

        BeanDefinition requestCacheAwareFilter = new RootBeanDefinition(RequestCacheAwareFilter.class);
        requestCacheAwareFilter.getPropertyValues().addPropertyValue("requestCache", authBldr.getRequestCache());
        unorderedFilterChain.add(new OrderDecorator(requestCacheAwareFilter, REQUEST_CACHE_FILTER));

        unorderedFilterChain.addAll(buildCustomFilterList(element, pc));
        //对过滤器进行排序
        Collections.sort(unorderedFilterChain, new OrderComparator());
        checkFilterChainOrder(unorderedFilterChain, pc, source);
        //排好序的过滤器链
        List filterChain = new ManagedList();

        for (OrderDecorator od : unorderedFilterChain) {
            filterChain.add(od.bean);
        }

        ManagedMap> filterChainMap = httpBldr.getFilterChainMap();
        filterChainMap.put(matcher.getUniversalMatchPattern(), filterChain);
        //注册过滤器链proxy
        registerFilterChainProxy(pc, filterChainMap, matcher, source);

        pc.popAndRegisterContainingComponent();
        return null;
    }
    http节点的11个过滤器不是都要加入过滤器链的,这个和我们的auto-config有关。使用auto-config="true"会自动提供以下三个认证相关的功能:
    HTTP基本认证
    Form登录认证
    退出
这一点从源码中可以找到:AuthenticationConfigBuilder类的createLogoutFilter、createBasicFilter和createFormLoginFilter三个方法
void createLogoutFilter() {
        Element logoutElt = DomUtils.getChildElementByTagName(httpElt, Elements.LOGOUT);
        if (logoutElt != null || autoConfig) {
            logoutFilter = new LogoutBeanDefinitionParser(rememberMeServicesId).parse(logoutElt, pc);
        }
    }
 
void createBasicFilter(BeanReference authManager) {
        ......
        if (basicAuthElt != null || autoConfig) {
            ......
        }
      ......
    }
 
void createFormLoginFilter(BeanReference sessionStrategy, BeanReference authManager) {
      ......
        if (formLoginElt != null || autoConfig) {
          ......
        }
       ......
    }
 可见只有autoConfig=true 时才会加入这仨过滤器。
在对unorderedFilterChain进行排序时,这11个过滤器怎么排序的呢?org.springframework.core.OrderComparator的排序逻辑是根据org.springframework.core.Ordered的getOrder方法的比较。在AuthenticationConfigBuilder的getFilters方法返回的事List集合,OrderDecorator的构造方法:
public OrderDecorator(BeanMetadataElement bean, SecurityFilters filterOrder) {
        this.bean = bean;
        this.order = filterOrder.getOrder();
    }
 继续看SecurityFilters枚举类
enum SecurityFilters {
    FIRST (Integer.MIN_VALUE),
    CHANNEL_FILTER,
    CONCURRENT_SESSION_FILTER,
    SECURITY_CONTEXT_FILTER,
    LOGOUT_FILTER,
    X509_FILTER,
    PRE_AUTH_FILTER,
    CAS_FILTER,
    FORM_LOGIN_FILTER,
    OPENID_FILTER,
    LOGIN_PAGE_FILTER,
    DIGEST_AUTH_FILTER,
    BASIC_AUTH_FILTER,
    REQUEST_CACHE_FILTER,
    SERVLET_API_SUPPORT_FILTER,
    REMEMBER_ME_FILTER,
    ANONYMOUS_FILTER,
    SESSION_MANAGEMENT_FILTER,
    EXCEPTION_TRANSLATION_FILTER,
    FILTER_SECURITY_INTERCEPTOR,
    SWITCH_USER_FILTER,
    LAST (Integer.MAX_VALUE);

    private static final int INTERVAL = 100;
    private final int order;

    private SecurityFilters() {
        order = ordinal() * INTERVAL;
    }

    private SecurityFilters(int order) {
        this.order = order;
    }

    public int getOrder() {
       return order;
    }
}
 可见是根据枚举的定义进行排序的,在http节点的配置中,我们将自定一个的过滤器用before、after、position等关键字插入到这11个过滤器前后。
再看注册过滤器链:
private void registerFilterChainProxy(ParserContext pc, Map> filterChainMap, UrlMatcher matcher, Object source) {
        if (pc.getRegistry().containsBeanDefinition(BeanIds.FILTER_CHAIN_PROXY)) {
            pc.getReaderContext().error("Duplicate  element detected", source);
        }
        //定义一个FilterChainProxy
        BeanDefinitionBuilder fcpBldr = BeanDefinitionBuilder.rootBeanDefinition(FilterChainProxy.class);
        fcpBldr.getRawBeanDefinition().setSource(source);
        fcpBldr.addPropertyValue("matcher", matcher);
        fcpBldr.addPropertyValue("stripQueryStringFromUrls", Boolean.valueOf(matcher instanceof AntUrlPathMatcher));
        fcpBldr.addPropertyValue("filterChainMap", filterChainMap);//设置过滤器链集合
        BeanDefinition fcpBean = fcpBldr.getBeanDefinition();
        //注册bean
        pc.registerBeanComponent(new BeanComponentDefinition(fcpBean, BeanIds.FILTER_CHAIN_PROXY));
        //注册的别名为springSecurityFilterChain,在web.xml配置的filter名也要是这个
        pc.getRegistry().registerAlias(BeanIds.FILTER_CHAIN_PROXY, BeanIds.SPRING_SECURITY_FILTER_CHAIN);
    }
 那么在DelegatingFilterProxy的initDelegate方法就可以获取到这个过滤器链了,然后就是doFilter了。
 
好了,囫囵吞枣的列出这么多,也不是很连贯,但大概应该能说明白原理了。下一篇开始分析过滤器,所以再把过滤器列出来:

过滤器名称

描述

o.s.s.web.context.SecurityContextPersistenceFilter

负责从SecurityContextRepository获取或存储SecurityContext。SecurityContext代表了用户安全和认证过的session。

o.s.s.web.authentication.logout.LogoutFilter

监控一个实际为退出功能的URL(默认为/j_spring_security_logout),并且在匹配的时候完成用户的退出功能。

o.s.s.web.authentication.UsernamePasswordAuthenticationFilter

监控一个使用用户名和密码基于form认证的URL(默认为/j_spring_security_check),并在URL匹配的情况下尝试认证该用户。

o.s.s.web.authentication.ui.DefaultLoginPageGeneratingFilter

监控一个要进行基于forn或OpenID认证的URL(默认为/spring_security_login),并生成展现登录form的HTML

o.s.s.web.authentication.www.BasicAuthenticationFilter

监控HTTP 基础认证的头信息并进行处理

o.s.s.web.savedrequest.

RequestCacheAwareFilter

用于用户登录成功后,重新恢复因为登录被打断的请求。

o.s.s.web.servletapi.

SecurityContextHolderAwareRequest

Filter

用一个扩展了HttpServletRequestWrapper的子类(o.s.s.web. servletapi.SecurityContextHolderAwareRequestWrapper)包装HttpServletRequest。它为请求处理器提供了额外的上下文信息。

o.s.s.web.authentication.

AnonymousAuthenticationFilter

如果用户到这一步还没有经过认证,将会为这个请求关联一个认证的token,标识此用户是匿名的。

o.s.s.web.session.

SessionManagementFilter

根据认证的安全实体信息跟踪session,保证所有关联一个安全实体的session都能被跟踪到。

o.s.s.web.access.

ExceptionTranslationFilter

解决在处理一个请求时产生的指定异常

o.s.s.web.access.intercept.

FilterSecurityInterceptor

简化授权和访问控制决定,委托一个AccessDecisionManager完成授权的判断

 

你可能感兴趣的:(Spring,Security3)