I realize that Spring security build on chain of filters, which will intercept the request, detect (absence of) authentication, redirect to authentication entry point or pass the request to authorization service, and eventually let the request either hit the servlet or throw security exception (unauthenticated or unauthorized). DelegatingFitlerProxy glues these filters together. To perform their tasks, these filter access services such as UserDetailsService and AuthenticationManager.
Key filters in the chain are (in the order)
- SecurityContextPersistenceFilter (restores Authentication from JSESSIONID)
- UsernamePasswordAuthenticationFilter (performs authentication)
- ExceptionTranslationFilter (catch security exceptions from FilterSecurityInterceptor)
- FilterSecurityInterceptor (may throw authentication and authorization exceptions)
I'm confused how these filters are used. Is it that for the spring provided form-login, UsernamePasswordAuthenticationFilter is only used for /login, and latter filters are not? Does the form-login namespace element auto-configure these filters? Does every request (authenticated or not) reach FilterSecurityInterceptor for non-login url?
What if I want to secure my REST API with JWT-token, which is retrieved from login? I must configure two namespace configuration http
tags, rights? Other one for /login with UsernamePasswordAuthenticationFilter
, and another one for REST url's, with custom JwtAuthenticationFilter
.
Does configuring two http
elements create two springSecurityFitlerChains
? Is UsernamePasswordAuthenticationFilter
turned off by default, until I declare form-login
? How do I replace SecurityContextPersistenceFilter
with one, which will obtain Authentication
from existing JWT-token
rather than JSESSIONID
?
No,
UsernamePasswordAuthenticationFilter
extendsAbstractAuthenticationProcessingFilter
, and this contains aRequestMatcher
, that means you can define your own processing url, this filter only handle theRequestMatcher
matches the request url, the default processing url is/login
.Later filters can still handle the request, if the
UsernamePasswordAuthenticationFilter
executeschain.doFilter(request, response);
.More details about core fitlers
UsernamePasswordAuthenticationFilter
is created by<form-login>
, these are Standard Filter Aliases and OrderingIt depends on whether the before fitlers are successful, but
FilterSecurityInterceptor
is the last fitler normally.Yes, every fitlerChain has a
RequestMatcher
, if theRequestMatcher
matches the request, the request will be handled by the fitlers in the fitler chain.The default
RequestMatcher
matches all request if you don't config the pattern, or you can config the specific url (<http pattern="/rest/**"
).If you want to konw more about the fitlers, I think you can check source code in spring security.
doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
The Spring security filter chain is a very complex and flexible engine.
Looking at the current stable release 4.2.1 documentation, section 13.3 Filter Ordering you could see the whole filter chain's filter organization:
Now, I'll try to go on by your questions one by one:
Once you are configuring a
<security-http>
section, for each one you must at least provide one authentication mechanism. This must be one of the filters which match group 4 in the 13.3 Filter Ordering section from the Spring Security documentation I've just referenced.This is the minimum valid security:http element which can be configured:
Just doing it, these filters are configured in the filter chain proxy:
Note: I get them by creating a simple RestController which @Autowires the FilterChainProxy and returns it's contents:
Here we could see that just by declaring the
<security:http>
element with one minimum configuration, all the default filters are included, but none of them is of a Authentication type (4th group in 13.3 Filter Ordering section). So it actually means that just by declaring thesecurity:http
element, the SecurityContextPersistenceFilter, the ExceptionTranslationFilter and the FilterSecurityInterceptor are auto-configured.In fact, one authentication processing mechanism should be configured, and even security namespace beans processing claims for that, throwing an error during startup, but it can be bypassed adding an entry-point-ref attribute in
<http:security>
If I add a basic
<form-login>
to the configuration, this way:Now, the filterChain will be like this:
Now, this two filters org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter and org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter are created and configured in the FilterChainProxy.
So, now, the questions:
Yes, it is used to try to complete a login processing mechanism in case the request matches the UsernamePasswordAuthenticationFilter url. This url can be configured or even changed it's behaviour to match every request.
You could too have more than one Authentication processing mechanisms configured in the same FilterchainProxy (such as HttpBasic, CAS, etc).
No, the form-login element configures the UsernamePasswordAUthenticationFilter, and in case you don't provide a login-page url, it also configures the org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter, which ends in a simple autogenerated login page.
The other filters are auto-configured by default just by creating a
<security:http>
element with nosecurity:"none"
attribute.Every request should reach it, as it is the element which takes care of whether the request has the rights to reach the requested url. But some of the filters processed before might stop the filter chain processing just not calling
FilterChain.doFilter(request, response);
. For example, a CSRF filter might stop the filter chain processing if the request has not the csrf parameter.No, you are not forced to do this way. You could declare both
UsernamePasswordAuthenticationFilter
and theJwtAuthenticationFilter
in the same http element, but it depends on the concrete behaviour of each of this filters. Both approaches are possible, and which one to choose finnally depends on own preferences.Yes, that's true
Yes, you could see it in the filters raised in each one of the configs I posted
You could avoid SecurityContextPersistenceFilter, just configuring session strategy in
<http:element>
. Just configure like this:<security:http create-session="stateless" >
Or, In this case you could overwrite it with another filter, this way inside the
<security:http>
element:EDIT:
This finally depends on the implementation of each filter itself, but it's true the fact that the latter authentication filters at least are able to overwrite any prior authentication eventually made by preceding filters.
But this won't necesarily happen. I have some production cases in secured REST services where I use a kind of authorization token which can be provided both as a Http header or inside the request body. So I configure two filters which recover that token, in one case from the Http Header and the other from the request body of the own rest request. It's true the fact that if one http request provides that authentication token both as Http header and inside the request body, both filters will try to execute the authentication mechanism delegating it to the manager, but it could be easily avoided simply checking if the request is already authenticated just at the begining of the
doFilter()
method of each filter.Having more than one authentication filter is related to having more than one authentication providers, but don't force it. In the case I exposed before, I have two authentication filter but I only have one authentication provider, as both of the filters create the same type of Authentication object so in both cases the authentication manager delegates it to the same provider.
And opposite to this, I too have a scenario where I publish just one UsernamePasswordAuthenticationFilter but the user credentials both can be contained in DB or LDAP, so I have two UsernamePasswordAuthenticationToken supporting providers, and the AuthenticationManager delegates any authentication attempt from the filter to the providers secuentially to validate the credentials.
So, I think it's clear that neither the amount of authentication filters determine the amount of authentication providers nor the amount of provider determine the amount of filters.
I did not look carefully into this filter before, but after your last question I've been checking it's implementation, and as usually in Spring, nearly everything could be configured, extended or overwrited.
The SecurityContextPersistenceFilter delegates in a SecurityContextRepository implementation the search for the SecurityContext. By default, a HttpSessionSecurityContextRepository is used, but this could be changed using one of the constructors of the filter. So it may be better to write an SecurityContextRepository which fits your needs and just configure it in the SecurityContextPersistenceFilter, trusting in it's proved behaviour rather than start making all from scratch.