There are situations where you want to use Spring Security for authorization, but the user has already been reliably authenticated by some external system prior to accessing the application. We refer to these situations as “pre-authenticated” scenarios. Examples include X.509, Siteminder and authentication by the J2EE container in which the application is running. When using pre-authentication, Spring Security has to
Identify the user making the request.
Obtain the authorities for the user.
The details will depend on the external authentication
mechanism. A user might be identified by their certificate information in the case of X.509, or
by an HTTP request header in the case of Siteminder. If relying on container authentication, the
user will be identified by calling the getUserPrincipal()
method on the
incoming HTTP request. In some cases, the external mechanism may supply role/authority
information for the user but in others the authorities must be obtained from a separate source,
such as a UserDetailsService
.
Because most pre-authentication mechanisms follow the same pattern, Spring Security has a set of classes which provide an internal framework for implementing pre-authenticated authentication providers. This removes duplication and allows new implementations to be added in a structured fashion, without having to write everything from scratch. You don't need to know about these classes if you want to use something like X.509 authentication, as it already has a namespace configuration option which is simpler to use and get started with. If you need to use explicit bean configuration or are planning on writing your own implementation then an understanding of how the provided implementations work will be useful. You will find classes under the org.springframework.security.web.authentication.preauth. We just provide an outline here so you should consult the Javadoc and source where appropriate.
This class will check the current contents of the security context and, if empty, it
will attempt to extract user information from the HTTP request and submit it to the
AuthenticationManager
. Subclasses override the following
methods to obtain this information:
protected abstract Object getPreAuthenticatedPrincipal(HttpServletRequest request); protected abstract Object getPreAuthenticatedCredentials(HttpServletRequest request);
After calling these, the filter will create a
PreAuthenticatedAuthenticationToken
containing the returned data
and submit it for authentication. By “authentication” here, we really just mean
further processing to perhaps load the user's authorities, but the standard Spring Security
authentication architecture is followed.
Like other Spring Security authentication filters, the pre-authentication filter has an
authenticationDetailsSource
property which by default will create a
WebAuthenticationDetails
object to store additional information
such as the session-identifier and originating IP address in the details
property of the Authentication
object. In cases where user
role information can be obtained from the pre-authentication mechanism, the data is also
stored in this property. Subclasses of
AbstractPreAuthenticatedAuthenticationDetailsSource
use an extended
details object which implements the
GrantedAuthoritiesContainer
interface, thus enabling the
authentication provider to read the authorities which were externally allocated to the user.
We'll look at a concrete example next.
If the filter is configured with an authenticationDetailsSource
which is an instance of this class, the authority information is obtained by calling the
isUserInRole(String role)
method for each of a pre-determined
set of “mappable roles”. The class gets these from a configured
MappableAttributesRetriever
. Possible implementations
include hard-coding a list in the application context and reading the role information
from the <security-role>
information in a
web.xml
file. The pre-authentication sample application uses the
latter approach.
There is an additional stage where the roles (or attributes) are mapped to Spring
Security GrantedAuthority
objects using a configured
Attributes2GrantedAuthoritiesMapper
. The default will
just add the usual ROLE_
prefix to the names, but it gives you full
control over the behaviour.
The pre-authenticated provider has little more to do than load the
UserDetails
object for the user. It does this by delegating
to a AuthenticationUserDetailsService
. The latter is similar
to the standard UserDetailsService
but takes an
Authentication
object rather than just user name:
public interface AuthenticationUserDetailsService { UserDetails loadUserDetails(Authentication token) throws UsernameNotFoundException; }
This interface may have also other uses but with pre-authentication it allows access to the
authorities which were packaged in the Authentication
object,
as we saw in the previous section. The
PreAuthenticatedGrantedAuthoritiesUserDetailsService
class does
this. Alternatively, it may delegate to a standard
UserDetailsService
via the
UserDetailsByNameServiceWrapper
implementation.
The AuthenticationEntryPoint
was discussed in the technical overview chapter. Normally it
is responsible for kick-starting the authentication process for an unauthenticated user
(when they try to access a protected resource), but in the pre-authenticated case this
doesn't apply. You would only configure the
ExceptionTranslationFilter
with an instance of this class if you
aren't using pre-authentication in combination with other authentication mechanisms. It will
be called if the user is rejected by the
AbstractPreAuthenticatedProcessingFilter
resulting in a null
authentication. It always returns a 403
-forbidden response code if
called.
X.509 authentication is covered in its own chapter. Here we'll look at some classes which provide support for other pre-authenticated scenarios.
An external authentication system may supply information to the application by setting
specific headers on the HTTP request. A well known example of this is Siteminder, which
passes the username in a header called SM_USER
. This mechanism is
supported by the class RequestHeaderAuthenticationFilter
which simply
extracts the username from the header. It defaults to using the name
SM_USER
as the header name. See the Javadoc for more details.
Tip | |
---|---|
Note that when using a system like this, the framework performs no authentication checks at all and it is extremely important that the external system is configured properly and protects all access to the application. If an attacker is able to forge the headers in their original request without this being detected then they could potentially choose any username they wished. |
A typical configuration using this filter would look like this:
<security:http> <!-- Additional http configuration omitted --> <security:custom-filter position="PRE_AUTH_FILTER" ref="siteminderFilter" /> </security:http> <bean id="siteminderFilter" class= "org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter"> <property name="principalRequestHeader" value="SM_USER"/> <property name="authenticationManager" ref="authenticationManager" /> </bean> <bean id="preauthAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider"> <property name="preAuthenticatedUserDetailsService"> <bean id="userDetailsServiceWrapper" class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper"> <property name="userDetailsService" ref="userDetailsService"/> </bean> </property> </bean> <security:authentication-manager alias="authenticationManager"> <security:authentication-provider ref="preauthAuthProvider" /> </security:authentication-manager>
We've assumed here that the security namespace
is being used for configuration. It's also assumed that you have added a
UserDetailsService
(called
“userDetailsService”) to your configuration to load the user's
roles.
The class J2eePreAuthenticatedProcessingFilter
will extract the
username from the userPrincipal
property of the
HttpServletRequest
. Use of this filter would usually be
combined with the use of J2EE roles as described above in the section called “J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource”.
There is a sample application in the codebase which uses this approach, so get hold of
the code from subversion and have a look at the application context file if you are
interested. The code is in the samples/preauth
directory.