Through our last articles we could discover that Spring Security can be configured in two ways: the first one using standard Spring beans mechanism. The second one is specific for Spring Security because it uses its own XML schema definition (XSD). In this article we'll focus on the second method.
Data Engineering Design Patterns

Looking for a book that defines and solves most common data engineering problems? I wrote
one on that topic! You can read it online
on the O'Reilly platform,
or get a print copy on Amazon.
I also help solve your data engineering problems 👉 contact@waitingforcode.com 📩
At the begin we'll present a sample configuration based on Spring Security's XSD. We'll describe it shortly. After, we'll analyze each part separately, in more detailed way. Thanks to it we'll understand better how Spring knows which beans must be used for each of <http /> configuration or how it resolves the name of <user-service />.
Spring Security configuration based on XSD
To illustrate Spring Security configuration based on XML schema, we'll use the same sample application as in previous articles. Its configuration looks like:
<security:http authentication-manager-ref="frontend" auto-config="true" use-expressions="true" access-denied-page="/access-denied"> <security:intercept-url pattern="/logout" access="isAuthenticated()" /> <security:csrf /> <security:logout logout-url="/logout" logout-success-url="/login" invalidate-session="true" delete-cookies="JSESSIONID" /> <security:form-login login-page="/login" default-target-url="/secret/data" authentication-failure-url="/login?error=true" password-parameter="password" username-parameter="login" login-processing-url="/do-login" /> <security:remember-me data-source-ref="dataSource" key="secret_remember_me" user-service-ref="inMemoryUserService" /> <security:session-management invalid-session-url="/invalid-session" session-fixation-protection="migrateSession"> <security:concurrency-control expired-url="/expired-session" max-sessions="1" error-if-maximum-exceeded="true" /> </security:session-management> <security:custom-filter ref="oneShootAuthFilter" after="CONCURRENT_SESSION_FILTER"/> </security:http> <security:authentication-manager id="frontend"> <security:authentication-provider user-service-ref="inMemoryUserService" /> </security:authentication-manager> <security:user-service id="inMemoryUserService"> <security:user name="bartosz" password="bartosz" authorities="ROLE_ADMIN,ROLE_USER" /> <security:user name="admin" password="admin" authorities="ROLE_ADMIN,ROLE_USER" /> <security:user name="mod" password="mod" authorities="ROLE_USER" /> </security:user-service> <bean id="oneShootAuthFilter" class="com.waitingforcode.security.filter.OneShotActionFilter"> <property name="authenticationManager" ref="frontend" /> <property name="userDetailsService" ref="inMemoryUserService" /> </bean>
You can observe there 3 main parts: http, authentication-manager and user-service. To define them quickly, we can consider the http element as the security context for one application area (for example: frontend, backend, site parts reserved to connected users). As to authentication-manager, it defines the way of handling users authentication. It uses user-service, which is the last component. As you can deduce, it provides the layer thanks to which we can check if one user can be authentified or not. The last bean, oneShootAuthFilter, is defined here only to understand better one of http's components, <custom-filter />.
Spring Security configuration builders
-
<security:http authentication-manager-ref="frontend" auto-config="true" use-expressions="true" access-denied-page="/access-denied"> <security:intercept-url pattern="/logout" access="isAuthenticated()" /> <security:csrf /> <security:logout logout-url="/logout" logout-success-url="/login" invalidate-session="true" delete-cookies="JSESSIONID" /> <security:form-login login-page="/login" default-target-url="/secret/data" authentication-failure-url="/login?error=true" password-parameter="password" username-parameter="login" login-processing-url="/do-login" /> <security:remember-me data-source-ref="dataSource" key="secret_remember_me" user-service-ref="inMemoryUserService" /> <security:session-management invalid-session-url="/invalid-session" session-fixation-protection="migrateSession"> <security:concurrency-control expired-url="/expired-session" max-sessions="1" error-if-maximum-exceeded="true" /> </security:session-management> <security:custom-filter ref="oneShootAuthFilter" after="CONCURRENT_SESSION_FILTER"/> </security:http>
Unlike two next definitions, parser for <http /> tag is more complex. It not only resolves configuration into beans but also configures all Spring Security filter chain. All operations are made by org.springframework.security.config.HttpSecurityBeanDefinitionParser which is helped by HttpConfigurationBuilder. The job of making filter chain is handled by private BeanReference createFilterChain(Element element, ParserContext pc) method.
At the begin of this method, we construct new instance of HttpConfigurationBuilder using some of previously generated instances (authentication manager, port mapper and port resolver bean references). Once constructed, HttpConfigurationBuilder is used further to parse XML configuration and create beans definition understable by Spring Framework. As you can imagine now, it'll produce plenty of BeanDefinition objects, each one representing separate filter chain component. Almost all of its private methods are in charge of constructing Spring Security filters bean definitions.
Another member of <http /> configuration is AuthenticationConfigBuilder. It constructs all filters needed for user authentication (as AnonymousFilter, RememberMeFilter...). HttpConfigurationBuilder is in charge of handling creation of filters destined to HTTP requests (session management, CSRF protection or authorization).
Ok, both builders return a list of filters. But how Spring Security does order them ? It does it by wrapping all created filters with inner OrderDecorator class which contains a field with filter's position in Spring Security filters chain. This position is computed thanks to position of entry representing given filter in SecurityFilters enum. Spring adds all authentication and http filters. It also creates some of custom filter (<custom-filter /> tag). Thanks to it, we receive an unordered list of all configured filters. Spring Security uses after org.springframework.core.OrderComparator to decide the final order in filter chain. The fragment making that looks like:
List<OrderDecorator> unorderedFilterChain = new ArrayList<OrderDecorator>(); 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<BeanMetadataElement>(); for (OrderDecorator od : unorderedFilterChain) { filterChain.add(od.bean); }
-
<security:user-service id="inMemoryUserService"> <security:user name="bartosz" password="bartosz" authorities="ROLE_ADMIN,ROLE_USER" /> <security:user name="admin" password="admin" authorities="ROLE_ADMIN,ROLE_USER" /> <security:user name="mod" password="mod" authorities="ROLE_USER" /> </security:user-service>
This fragment is treated as a standard bean parsers because it's analyzed with an implementation of org.springframework.beans.factory.xml.BeanDefinitionParser. This class is in charge of analyze XML configuration entry and translate it into an instance of org.springframework.beans.factory.config.BeanDefinition. This object is used further to create new instance of real object defined in the configuration.
The package org.springframework.security.config.authentication contains an abstract class, AbstractUserDetailsServiceBeanDefinitionParser, which is extended by all real user services. So, in our configuration case, class extending this abstract parser is UserServiceBeanDefinitionParser. If we examine this class, we'll see that it allows to define users recognized by application in .properties file or directly inside XML configuration. In the second case, the parser will read all child elements of <user-service /> tags which name is <user />. After collecting all definitions and translating them into BeanDefinition objects, user-service bean will be constructed. But how does Spring know which class must be implemented for given user-service ? It knows that thanks to protected abstract String getBeanClassName(Element element). Thanks to that we know that implementation for in-memory user service is org.springframework.security.provisioning.InMemoryUserDetailsManager. If you look well in its constructor, you'll see that it accepts an collection of UserDetails instances, so exactly what the parser produces.
However, in-memory authentication is not ideal for big and dynamic systems, where we can register new users every day. For this case a better solution is JDBC user-service. Unlike in-memory user service, it contains some supplementary attributes which are used to detect underlying data source (data-source-ref), retrieve user by his login (users-by-username-query) and list of authorities (authorities-by-username-query). These attributes are used further by JdbcUserDetailsManager. This user details manager is mainly constructed by SQL query templates which are executed to check if user can be authenticated or to add/remove him.
-
<security:authentication-manager id="frontend"> <security:authentication-provider user-service-ref="inMemoryUserService" /> </security:authentication-manager>
Authentication manager will handle authentication process thanks to defined authentication provider. This last object is used to provide authentication details and so, attempt to create valid Authentication object. As user-service, it also constructed with one BeanDefinitionParser. But because there is only one possible configuration entry, no abstraction is needed. This parser mostly interprets all known attributes, as id or erase-credentials. But it also must resolve authentication-providers. As we can read in the parser file, one or more providers can be defined:
NodeList children = element.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { Node node = children.item(i); if (node instanceof Element) { Element providerElt = (Element)node; if (StringUtils.hasText( providerElt.getAttribute(ATT_REF))) { if (providerElt.getAttributes().getLength() > 1) { pc.getReaderContext().error("authentication-provider element cannot be used with other attributes " + "when using 'ref' attribute", pc.extractSource(element)); } NodeList providerChildren = providerElt.getChildNodes(); for (int j = 0; j < providerChildren.getLength(); j++) { if (providerChildren.item(j) instanceof Element) { pc.getReaderContext().error("authentication-provider element cannot have child elements when used " + "with 'ref' attribute", pc.extractSource(element)); } } providers.add(new RuntimeBeanReference(providerElt.getAttribute(ATT_REF))); } else { BeanDefinition provider = resolver.resolve(providerElt.getNamespaceURI()).parse(providerElt, pc); Assert.notNull(provider, "Parser for " + providerElt.getNodeName() + " returned a null bean definition"); String providerId = pc.getReaderContext().generateBeanName(provider); pc.registerBeanComponent(new BeanComponentDefinition(provider, providerId)); providers.add(new RuntimeBeanReference(providerId)); } } }
As we could see in this article, underlying process for Spring Security configuration based on namespace is based on BeanDefinitionParser instances. They analyze provided XML configuration and construct appropriate BeanDefinition objects. This process is made by configuration builders which are sometimes coupled, as user-service or authentication and http filters in <http /> tag. In additionally they can take different options which can trigger a lot of different actions. It makes the code more verbose to read and a little bit more complicated to understand. Even, if the main method is still the same as in the rest of Spring projects.
Consulting

With nearly 16 years of experience, including 8 as data engineer, I offer expert consulting to design and optimize scalable data solutions.
As an O’Reilly author, Data+AI Summit speaker, and blogger, I bring cutting-edge insights to modernize infrastructure, build robust pipelines, and
drive data-driven decision-making. Let's transform your data challenges into opportunities—reach out to elevate your data engineering game today!
👉 contact@waitingforcode.com
đź”— past projects