如果在web项目中增加springsecurity作为你的登录认证授权框架,那么第一步就需要在web.xml 增加如下配置
<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。先看一下org.springframework.web.filter.DelegatingFilterProxy.java的源码
@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); } } } } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException { // Lazily initialize the delegate if necessary. Filter delegateToUse = this.delegate; if (delegateToUse == null) { synchronized (this.delegateMonitor) { if (this.delegate == null) { WebApplicationContext wac = findWebApplicationContext(); if (wac == null) { throw new IllegalStateException("No WebApplicationContext found: " + "no ContextLoaderListener or DispatcherServlet registered?"); } this.delegate = initDelegate(wac); } delegateToUse = this.delegate; } } // Let the delegate perform the actual doFilter operation. invokeDelegate(delegateToUse, request, response, filterChain); }
将filtername('springSecurityFilterChain')作为targetBeanName,从WebApplicationContext通过getBean()获取FilterChainProxy的实例。
最终FilterChainProxy还是把工作交给了FilterList来处理,其中保存的就是Security系统中的一系列Filter。那么这些Filter也是在解析配置文件的时候注册到上下文中的。下面具体看下Security配置文件的解析,主要是<http>标签。
二、这是一段springsecurity的xml配置文件的一段内容
<http pattern="/static/**" security="none" /> <http pattern="/WEB-INF/views/jsp/common/403.jsp" security="none" /> <http pattern="/WEB-INF/views/jsp/common/login.jsp" security="none" /> <http pattern="/403" security="none" /> <http pattern="/login" security="none" /> <http use-expressions="true" > <intercept-url pattern="/login" access="IS_AUTHENTICATED_ANONYMOUSLY"/> <intercept-url pattern="/**" access="isAuthenticated()"/> <access-denied-handler ref="accessDeniedHandler"/> <form-login login-page="/login" default-target-url="/login" authentication-failure-url="/login" always-use-default-target="true" username-parameter="userName" password-parameter="password" login-processing-url="/userLogin" authentication-success-handler-ref="authenticationDispatcher" authentication-failure-handler-ref="ylpaAuthenticationFailureHandler" /> <custom-filter ref="customFilter" before="FILTER_SECURITY_INTERCEPTOR"/> <custom-filter ref="customLogout" position="LOGOUT_FILTER" /> <session-management invalid-session-url="/login"> <concurrency-control max-sessions="1" expired-url="/login" /> </session-management> <csrf disabled="true" /> </http>
先找到是哪一个自定义的 NamespaceHandler来解析该内容。
SecurityNamespaceHandler.java对各个标签使用的解析标签类
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()); parsers.put(Elements.METHOD_SECURITY_METADATA_SOURCE, new MethodSecurityMetadataSourceBeanDefinitionParser()); // Only load the web-namespace parsers if the web classes are available if (ClassUtils.isPresent(FILTER_CHAIN_PROXY_CLASSNAME, getClass() .getClassLoader())) { parsers.put(Elements.DEBUG, new DebugBeanDefinitionParser()); parsers.put(Elements.HTTP, new HttpSecurityBeanDefinitionParser()); parsers.put(Elements.HTTP_FIREWALL, new HttpFirewallBeanDefinitionParser()); parsers.put(Elements.FILTER_SECURITY_METADATA_SOURCE, new FilterInvocationSecurityMetadataSourceParser()); parsers.put(Elements.FILTER_CHAIN, new FilterChainBeanDefinitionParser()); filterChainMapBDD = new FilterChainMapBeanDefinitionDecorator(); } if (ClassUtils.isPresent(MESSAGE_CLASSNAME, getClass().getClassLoader())) { parsers.put(Elements.WEBSOCKET_MESSAGE_BROKER, new WebSocketMessageBrokerSecurityBeanDefinitionParser()); } }
HTTP 标签使用的是
HttpSecurityBeanDefinitionParser
@SuppressWarnings({ "unchecked" }) @Override public BeanDefinition parse(Element element, ParserContext pc) { CompositeComponentDefinition compositeDef = new CompositeComponentDefinition( element.getTagName(), pc.extractSource(element)); pc.pushContainingComponent(compositeDef); registerFilterChainProxyIfNecessary(pc, pc.extractSource(element)); // Obtain the filter chains and add the new chain to it BeanDefinition listFactoryBean = pc.getRegistry().getBeanDefinition( BeanIds.FILTER_CHAINS); List<BeanReference> filterChains = (List<BeanReference>) listFactoryBean .getPropertyValues().getPropertyValue("sourceList").getValue(); filterChains.add(createFilterChain(element, pc)); pc.popAndRegisterContainingComponent(); return null; }
static void registerFilterChainProxyIfNecessary(ParserContext pc, Object source) { if (pc.getRegistry().containsBeanDefinition(BeanIds.FILTER_CHAIN_PROXY)) { return; } // Not already registered, so register the list of filter chains and the // FilterChainProxy BeanDefinition listFactoryBean = new RootBeanDefinition(ListFactoryBean.class); listFactoryBean.getPropertyValues().add("sourceList", new ManagedList()); pc.registerBeanComponent(new BeanComponentDefinition(listFactoryBean, BeanIds.FILTER_CHAINS)); BeanDefinitionBuilder fcpBldr = BeanDefinitionBuilder .rootBeanDefinition(FilterChainProxy.class); fcpBldr.getRawBeanDefinition().setSource(source); fcpBldr.addConstructorArgReference(BeanIds.FILTER_CHAINS); fcpBldr.addPropertyValue("filterChainValidator", new RootBeanDefinition( DefaultFilterChainValidator.class)); BeanDefinition fcpBean = fcpBldr.getBeanDefinition(); pc.registerBeanComponent(new BeanComponentDefinition(fcpBean, BeanIds.FILTER_CHAIN_PROXY)); //将FilterChainProxy实例别名注册为springSecurityFilterChain pc.getRegistry().registerAlias(BeanIds.FILTER_CHAIN_PROXY, BeanIds.SPRING_SECURITY_FILTER_CHAIN); }
//一系列的filter 注册。还有下一节要讲的ProviderManager的创建就是从这个方法createAuthenticationManager
private BeanReference createFilterChain(Element element, ParserContext pc) { boolean secured = !OPT_SECURITY_NONE.equals(element.getAttribute(ATT_SECURED)); if (!secured) { if (!StringUtils.hasText(element.getAttribute(ATT_PATH_PATTERN)) && !StringUtils.hasText(ATT_REQUEST_MATCHER_REF)) { pc.getReaderContext().error( "The '" + ATT_SECURED + "' attribute must be used in combination with" + " the '" + ATT_PATH_PATTERN + "' or '" + ATT_REQUEST_MATCHER_REF + "' attributes.", pc.extractSource(element)); } for (int n = 0; n < element.getChildNodes().getLength(); n++) { if (element.getChildNodes().item(n) instanceof Element) { pc.getReaderContext().error( "If you are using <http> to define an unsecured pattern, " + "it cannot contain child elements.", pc.extractSource(element)); } } return createSecurityFilterChainBean(element, pc, Collections.emptyList()); } final BeanReference portMapper = createPortMapper(element, pc); final BeanReference portResolver = createPortResolver(portMapper, pc); ManagedList<BeanReference> authenticationProviders = new ManagedList<>(); BeanReference authenticationManager = 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()); List<OrderDecorator> unorderedFilterChain = new ArrayList<>(); unorderedFilterChain.addAll(httpBldr.getFilters()); unorderedFilterChain.addAll(authBldr.getFilters()); unorderedFilterChain.addAll(buildCustomFilterList(element, pc)); Collections.sort(unorderedFilterChain, new OrderComparator()); checkFilterChainOrder(unorderedFilterChain, pc, pc.extractSource(element)); // The list of filter beans List<BeanMetadataElement> filterChain = new ManagedList<>(); for (OrderDecorator od : unorderedFilterChain) { filterChain.add(od.bean); } return createSecurityFilterChainBean(element, pc, filterChain); }
public abstract class BeanIds { private static final String PREFIX = "org.springframework.security."; /** * The "global" AuthenticationManager instance, registered by the * <authentication-manager> element */ public static final String AUTHENTICATION_MANAGER = PREFIX + "authenticationManager"; /** External alias for FilterChainProxy bean, for use in web.xml files */ public static final String SPRING_SECURITY_FILTER_CHAIN = "springSecurityFilterChain"; public static final String CONTEXT_SOURCE_SETTING_POST_PROCESSOR = PREFIX + "contextSettingPostProcessor"; public static final String USER_DETAILS_SERVICE = PREFIX + "userDetailsService"; public static final String USER_DETAILS_SERVICE_FACTORY = PREFIX + "userDetailsServiceFactory"; public static final String METHOD_ACCESS_MANAGER = PREFIX + "defaultMethodAccessManager"; public static final String FILTER_CHAIN_PROXY = PREFIX + "filterChainProxy"; public static final String FILTER_CHAINS = PREFIX + "filterChains"; public static final String METHOD_SECURITY_METADATA_SOURCE_ADVISOR = PREFIX + "methodSecurityMetadataSourceAdvisor"; public static final String EMBEDDED_APACHE_DS = PREFIX + "apacheDirectoryServerContainer"; public static final String CONTEXT_SOURCE = PREFIX + "securityContextSource"; public static final String DEBUG_FILTER = PREFIX + "debugFilter"; }}