spring-security/src/docbkx/namespace-config.xml

538 lines
29 KiB
XML
Raw Normal View History

<?xml version="1.0" encoding="UTF-8"?>
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="namespace-config" xmlns:xlink="http://www.w3.org/1999/xlink">
<info>
<title>Security Namespace Configuration</title>
</info>
<section>
<title>Introduction</title>
<para>
Namespace configuration has been available since version 2.0 of the Spring framework. It
allows you to supplement the traditional Spring beans application context syntax with elements
from additional XML schema. You can find more information in the Spring
<link xlink:href="http://static.springframework.org/spring/docs/2.5.x/reference/xsd-config.html">
Reference Documentation</link>. A namespace element can be used simply to allow a more
concise way of configuring an individual bean or, more powerfully, to define an alternative
configuration syntax which more closely matches the problem domain and hides the underlying
2008-04-08 20:51:30 +00:00
complexity from the user. A simple element may conceal the fact that multiple beans and
processing steps are being added to the application context. For example, adding the following
2008-04-08 20:51:30 +00:00
element from the security namespace to an application context will start up an embedded LDAP
server for testing use within the application:
<programlisting><![CDATA[
2008-04-08 20:51:30 +00:00
<security:ldap-server />
]]></programlisting>
2008-04-08 20:51:30 +00:00
This is much simpler than wiring up the equivalent Apache Directory Server beans. The most
common alternative configuration requirements are supported by attributes on the
<literal>ldap-server</literal>
element and the user is isolated from worrying about which beans they need to be set on and
what the bean property names are.
<footnote>
<para>You can find out more about the use of the
<literal>ldap-server</literal>
element in the chapter on
<link xlink:href="ldap">LDAP</link>.</para>
</footnote>. Use of a good XML editor while editing the application context file should
provide information on the attributes and elements that are available. We would recommend that
you try out the
2008-04-08 20:51:30 +00:00
<link xlink:href="http://www.springsource.com/products/sts">SpringSource Tool Suite</link>
as it has special features for working with the Spring portfolio namespaces.
</para>
<para>
To start using the security namespace in your application context, all you need to do is add
the schema declaration to your application context file:
<programlisting>
<![CDATA[
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd">
...
</beans>
]]></programlisting>
In many of the examples you will see (and in the sample) applications, we will often use
"security" as the default namespace rather than "beans", which means we can omit the prefix on
all the security namespace elements, making the context easier to read. You may also want to
do this if you have your application context divided up into separate files and have most of
your security configuration in one of them. Your security application context file would then
start like this
<programlisting><![CDATA[
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans">
...
</beans:beans>
]]></programlisting>
2008-04-09 11:27:55 +00:00
We'll assume this syntax is being used from now on in this chapter.
</para>
2008-04-08 20:51:30 +00:00
<section>
<title>Design of the Namespace</title>
2008-04-08 20:51:30 +00:00
<para>
The namespace is designed to capture the most common uses of the framework and provide a
simplified and concise syntax for enabling them within an application. The design is largely
based around the large-scale dependencies within the framework, and can be divided up into
the following areas:
2008-04-08 20:51:30 +00:00
<itemizedlist>
<listitem>
<para>
<emphasis>Web/HTTP Security</emphasis>
- the most complex part. Sets up the filters and related service beans used to apply
the framework authentication mechanisms, to secure URLs, render login and error pages
and much more.</para>
</listitem>
<listitem>
<para>
<emphasis>Business Object (Method) Security</emphasis>
- options for securing the service layer.</para>
</listitem>
<listitem>
<para>
<emphasis>AuthenticationManager</emphasis>
- handles authentication requests from other parts of the framework.</para>
</listitem>
<listitem>
<para>
<emphasis>AccessDecisionManager</emphasis>
- provides access decisions for web and method security. A default one will be
registered, but you can also choose to use a custom one, declared using normal Spring
bean syntax.</para>
</listitem>
<listitem>
<para>
<emphasis>AuthenticationProvider</emphasis>s - mechanisms against which the
authentication manager authenticates users. The namespace provides supports for
several standard options and also a means of adding custom beans declared using a
traditional syntax.
</para>
</listitem>
<listitem>
<para>
<emphasis>UserDetailsService</emphasis>
- closely related to authentication providers, but often also required by other beans.</para>
</listitem>
2008-04-08 20:51:30 +00:00
<!-- todo: diagram and link to other sections which describe the interfaces -->
</itemizedlist>
2008-04-08 20:51:30 +00:00
</para>
<para>We'll see how these work together in the following sections.</para>
2008-04-08 20:51:30 +00:00
</section>
</section>
<section xml:id="ns-getting-started">
<title>Getting Started with Security Namespace Cofiguration</title>
2008-04-09 11:27:55 +00:00
<para>
In this section, we'll look at how you can build up a namespace configuration to use some of the main
features of the framework. Let's assume you initially want to get up and running as quickly as possible
and add authentication support and access control to an existing web application, with a few
test logins. The we'll look at how to change over to authenticating against a database or other
security information repository. In later sections we'll introduce more advanced namespace configuration
options.
</para>
<section xml:id="ns-web-xml">
<title><literal>web.xml</literal> Configuration</title>
<para>
The first thing you need to do is add the following filter declaration to your
<literal>web.xml</literal>
2008-04-09 11:27:55 +00:00
file:
<programlisting>
<![CDATA[
2008-04-09 11:27:55 +00:00
<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>]]>
</programlisting>
This provides a hook into the Spring Security web infrastructure. <!-- You can find more details
of how this works in
<link xlink:href="#todo">TODO</link>-->. You're then ready to start editing your application
context file. Web security services are configured using the <literal>&lt;http&gt;</literal>
element.
</para>
</section>
<section xml:id="ns-minimal">
<title>A Minimal <literal>&lt;http&gt;</literal>Configuration</title>
<para>
All you need to enable web security to begin with is
<programlisting><![CDATA[
2008-04-09 11:27:55 +00:00
<http auto-config='true'>
<intercept-url pattern="/**" access="ROLE_USER" />
</http>
]]>
</programlisting>
Which says that we want all URLs within our application to be secured, requiring the role
<literal>ROLE_USER</literal> to access them.</para>
<note><para>You can use multiple <literal>&lt;intercept-url&gt;</literal> elements to define
different access requirements for different sets of URLs, but they will be evaluated in the
order listed and the first match will be used. So you must put the most specific matches at the top.</para></note>
<para>
To add some users, you can define a set of test data directly in the
namespace:
<programlisting><![CDATA[
2008-04-09 11:27:55 +00:00
<authentication-provider>
<user-service>
<user name="jimi" password="jimispassword" authorities="ROLE_USER, ROLE_ADMIN" />
<user name="bob" password="bobspassword" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
]]>
</programlisting>
This defines two users, their passwords and their roles within the application (which will
be used for access control). It is also possible to load user information from
a standard properties file using the <literal>properties</literal> attribute on
<literal>user-service</literal>.
The <literal>&lt;authentication-provider&gt;</literal>
element specifies that the user information will be registered with the authentication
manager and used to process authentication requests.
</para>
<sidebar>
<para>If you are familiar with previous versions of the framework, you can probably
already guess roughly what's going on here. The &lt;http&gt; element is
responsible for creating a <classname>FilterChainProxy</classname> and the
filter beans which it uses. Common issues like incorrect filter ordering are no
longer an issue as the filter positions are predefined.</para>
<para>The <literal>&lt;authentication-provider&gt;</literal>
element creates a <classname>DaoAuthenticationProvider</classname>
bean and the <literal>&lt;user-service&gt;</literal> element creates an
<classname>InMemoryDaoImpl</classname>. A <literal>ProviderManager</literal>
bean is always created by the namespace processing system and the
<classname>AuthenticationProvider</classname>
is automatically registered with it.</para>
</sidebar>
<para>
At this point you should be able to start up your application and you will be required to
log in to proceed. Try it out, or try experimenting with the "tutorial" sample application
that comes with the project. The above configuration actually adds quite a few services to
the application because we have used the
<literal>auto-config</literal>
attribute. For example, form login processing and "remember-me" services are automatically
enabled.
</para>
<section xml:id="ns-auto-config">
<title>What does <literal>auto-config</literal> Include?</title>
<para>
The <literal>auto-config</literal> attribute, as we have used it above, is just a
shorthand syntax for:
<programlisting><![CDATA[
<http>
<intercept-url pattern="/**" access="ROLE_USER" />
<form-login />
<anonymous />
<http-basic />
<logout />
<remember-me />
</http>
]]>
</programlisting>
These other elements are responsible for setting up form-login,
<link xlink:href="#anonymous">anonymous authentication</link>, basic authentication, logout handling and remember-me services
respectively. They each have attributes which can be used to alter their behaviour.
</para>
</section>
<section xml:id="ns-form-and-basic">
<title>Form and Basic Login Options</title>
<para>
You might be wondering where the login form came from when you were prompted
to log in, since we made no mention of any HTML files or JSPs. In fact, since we didn't explicitly
set a URL for the login page, Spring Security generates one automatically, based on the features
that are enabled and using standard values for the URL which processes the submitted login,
the default target URL the user will be sent to ad so on. However, the namespace offers plenty of
suppport to allow you to customize these options.
For example, if you want to supply your own login page, you could use:
<programlisting><![CDATA[
<http auto-config='true'>
<intercept-url pattern="/login.jsp*" filters="none"/>
<intercept-url pattern="/**" access="ROLE_USER" />
<form-login login-page='/login.jsp'/>
</http>
]]>
</programlisting>
Note that you can still use <literal>auto-config</literal>. The <literal>form-login</literal> element just overrides the
default settings. Also note that we've added an extra <literal>intercept-url</literal> element to say that any requests
for the login page should be excluded from processing by the security filters. Otherwise the request would be matched by
the pattern <literal>/**</literal> and it wouldn't be possible to access the login page itself!
If you want to use basic authentication instead of form login, then change the configuration to
<programlisting><![CDATA[
<http auto-config='true'>
<intercept-url pattern="/**" access="ROLE_USER" />
<http-basic />
</http>
]]>
</programlisting>
Basic authentication will then take precedence and will be used to prompt for a login when a user attempts to access
a protected resource. Form login is still available in this configuration if you wish to use it, for example
through a login form embedded in another web page.
</para>
</section>
</section>
<section xml:id="ns-auth-providers">
<title>Using other Authentication Providers</title>
<para>
In practice you will need a more scalable source of user information than a few names added to the application context file.
Most likely you will want to store your user information in something like a database or an LDAP server. LDAP namespace
configuration is dealt with in the <link xlink:href="#ldap">LDAP chapter</link>, so we won't cover it here. If you have a
custom implementation of Spring Security's <classname>UserDetailsService</classname>, called "myUserDetailsService" in your
application context, then you can authenticate against this using
<programlisting><![CDATA[
<authentication-provider user-service-ref='myUserDetailsService'/>
]]>
</programlisting>
If you want to use a database, then you can use
<programlisting><![CDATA[
<authentication-provider>
<jdbc-user-service data-source-ref="securityDataSource"/>
</authentication-provider>
]]>
</programlisting>
Where "securityDataSource" is the name of a <classname>DataSource</classname> bean in the application context,
pointing at a database containing the standard Spring Security user data tables. Alternatively, you could configure
a Spring Security <classname>JdbcDaoImpl</classname> bean and point at that using the <literal>user-service-ref</literal>
attribute.
</para>
<section><title>Adding a Password Encoder</title>
<para>
Often your password data will be encoded using a hashing algorithm. This is supported by the <literal>&lt;password-encoder&gt;</literal>
element. With SHA encoded passwords, the original authentication provider configuration would look like this:
<programlisting><![CDATA[
<authentication-provider>
<password-encoder hash="sha"/>
<user-service>
<user name="jimi" password="d7e6351eaa13189a5a3641bab846c8e8c69ba39f" authorities="ROLE_USER, ROLE_ADMIN" />
<user name="bob" password="4e7421b1b8765d8f9406d87e7cc6aa784c4ab97f" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
]]>
</programlisting>
</para>
<para>
When using hashed passwords, it's also a good idea to use a salt value to protect against dictionary attacks and Spring Security supports this too.
Ideally you would want to use a randomly generated salt value for each user, but you can use any property of the <classname>UserDetails</classname>
object which is loaded by your <classname>UserDetailsService</classname>. For example, to use the <literal>username</literal> property, you would use
<programlisting><![CDATA[
<password-encoder hash="sha">
<salt-source user-property="username"/>
</password-encoder>
]]></programlisting>
You can use a custom password encoder bean by using the <literal>ref</literal> attribute of <literal>password-encoder</literal>. This should
contain the name of a bean in the application context which is an instance of Spring Security's <interfacename>PasswordEncoder</interfacename>
interface.
</para>
</section>
2008-04-09 11:27:55 +00:00
</section>
</section>
<section xml:id="ns-web-advanced">
<title>Advanced Web Features</title>
<section xml:id="ns-requires-channel">
<title>Adding HTTP/HTTPS Channel Security</title>
<para>If your application supports both HTTP and HTTPS, and you require that particular URLs can only be accessed over HTTPS, then this is
directly supported using the <literal>requires-channel</literal> attribute on <literal>&lt;intercept-url&gt;</literal>:
<programlisting><![CDATA[
<http>
<intercept-url pattern="/secure/**" access="ROLE_USER" requires-channel="https"/>
<intercept-url pattern="/**" access="ROLE_USER" requires-channel="any"/>
...
</http>]]>
</programlisting>
With this configuration in place, if a user attempts to access anything matching the "/secure/**"
pattern using HTTP, they will first be redirected to an HTTPS URL.
The available options are "http", "https" or "any". Using the value "any" means that either HTTP or HTTPS
can be used.
</para>
<para>
If your application uses non-standard ports for HTTP and/or HTTPS, you can specify a list of port mappings as follows:
<programlisting>
<![CDATA[
<http>
...
<port-mappings>
<port-mapping http="9080" https="9443"/>
</port-mappings>
</http>]]>
</programlisting>
You can find a more in-depth discussion of channel security in <xref xlink:href="#channel-security"/>.
</para>
</section>
<section xml:id="ns-concurrent-session">
<title>Concurrent Session Control</title>
<para>
If you wish to place constraints on a single user's ability to log in to your application,
Spring Security supports this out of the box with the following simple additions. First you need to add the
following listener to your <filename>web.xml</filename> file to keep Spring Security updated about
session lifecycle events:
<programlisting>
<![CDATA[
<listener>
<listener-class>org.springframework.security.ui.session.HttpSessionEventPublisher</listener-class>
</listener>
]]></programlisting>
Then add the following line to your application context:
<programlisting><![CDATA[
<http>
...
<concurrent-session-control max-sessions="1" />
</http>]]>
</programlisting>
This will prevent a user from logging in multiple times - a second login will cause the first to
be invalidated. Often you would prefer to prevent a second login, in which case you can use
<programlisting><![CDATA[
<http>
...
<concurrent-session-control max-sessions="1" exception-if-maximum-exceeded="true"/>
</http>]]>
</programlisting>
The second login will then be rejected. <!-- TODO: Link to main section in docs -->
</para>
</section>
<section xml:id="ns-openid">
<title>OpenID Login</title>
<para>The namespace supports <link xlink:href="http://openid.net/">OpenID</link> login eiter instead of, or in addition to
normal form-based login, with a simple change:
<programlisting><![CDATA[
<http auto-config='true'>
<intercept-url pattern="/**" access="ROLE_USER" />
<openid-login />
</http>
]]></programlisting>
You should then register yourself with an OpenID provider (such as myopenid.com), and
add the user information to your in-memory <literal>&lt;user-service&gt;</literal>:
<programlisting><![CDATA[
<user name="http://jimi.hendrix.myopenid.com/" password="notused" authorities="ROLE_USER" />
]]></programlisting>
You should be able to login using the <literal>myopenid.com</literal> site to authenticate.
</para>
</section>
<section xml:id="ns-custom-filters">
<title>Adding in Your Own Filters</title>
<para>If you've used Spring Security before, you'll know that the framework maintains a chain
of filters in order to apply its services. You may want to add your own filters to the stack at
particular locations, or use a customized version of an existing filter. How can you do this with
namespace configuration, since the filter chain is not directly exposed?
</para>
<para>The order of the filters is always strictly enforced when using the namespace. Each Spring Security
filter implements the Spring <interfacename>Ordered</interfacename> interface and the filters are sorted
during initialization. The standard filters each have an alias in the namespace:
<table>
<title>Standard Filter Aliases and Ordering</title>
<tgroup cols="2" align="left">
<thead><row>
<entry align="center">Alias</entry><entry align="center">Filter Class</entry>
</row></thead>
<tbody>
<row><entry> CHANNEL_FILTER</entry><entry>ChannelProcessingFilter</entry></row>
<row><entry> CONCURRENT_SESSION_FILTER</entry><entry>ConcurrentSessionFilter</entry></row>
<row><entry> SESSION_CONTEXT_INTEGRATION_FILTER</entry><entry>HttpSessionContextIntegrationFilter</entry></row>
<row><entry> LOGOUT_FILTER </entry><entry>LogoutFilter</entry></row>
<row><entry> X509_FILTER </entry><entry>X509PreAuthenticatedProcessigFilter</entry></row>
<row><entry> PRE_AUTH_FILTER </entry><entry>Subclass of AstractPreAuthenticatedProcessingFilter</entry></row>
<row><entry> CAS_PROCESSING_FILTER </entry><entry>CasProcessingFilter</entry></row>
<row><entry> AUTHENTICATION_PROCESSING_FILTER </entry><entry>AuthenticationProcessingFilter</entry></row>
<row><entry> BASIC_PROCESSING_FILTER </entry><entry>BasicProcessingFilter</entry></row>
<row><entry> SERVLET_API_SUPPORT_FILTER</entry><entry>classname</entry></row>
<row><entry> REMEMBER_ME_FILTER </entry><entry>RememberMeProcessingFilter</entry></row>
<row><entry> ANONYMOUS_FILTER </entry><entry>AnonymousProcessingFilter</entry></row>
<row><entry> EXCEPTION_TRANSLATION_FILTER </entry><entry>ExceptionTranslationFilter</entry></row>
<row><entry> NTLM_FILTER </entry><entry>NtlmProcessingFilter</entry></row>
<row><entry> FILTER_SECURITY_INTERCEPTOR </entry><entry>FilterSecurityInterceptor</entry></row>
<row><entry> SWITCH_USER_FILTER </entry><entry>SwitchUserProcessingFilter</entry></row>
</tbody>
</tgroup>
</table>
You can add your own filter to the stack, using the <literal>custom-filter</literal> element and one of these
names to specify the position your filter should appear at:
<programlisting><![CDATA[
<beans:bean id="myFilter" class="com.mycompany.MySpecialAuthenticationFilter">
<custom-filter position="AUTHENTICATION_PROCESSING_FILTER"/>
</beans:bean>
]]></programlisting>
You can also use the <literal>after</literal> or <literal>before</literal> attribtues if you want your filter
to be inserted before or after another filter in the stack. The names "FIRST" and "LAST" can be used to indicate
that you want your filter to appear before or after the entire stack, respectively.
</para>
</section>
<section xml:id="ns-session-fixation">
<title>Session Fixation Attack Protection</title>
<para>
<link xlink:href="http://en.wikipedia.org/wiki/Session_fixation">Session fixation</link>
attacks are a potential risk where it is possible for a malicious attacker to create
a session by accessing a site, then persuade another user to log in with the same session
(by sending them a link containing the session identifier as a parameter, for example). Spring Security
protects against this automatically by creating a new session when a user logs in. If you don't require
this protection, or it conflicts with some other requirement, you can control the behaviour using the
<literal>session-fixation-protection</literal> attribute on <literal>&lt;http&gt;</literal>, which
has three options
<itemizedlist>
<listitem><para><literal>migrateSession</literal> - creates a new session and copies the existing
session attributes to the new session. This is the default.</para></listitem>
<listitem><para><literal>none</literal> - Don't do anything. The original session will be retained.</para></listitem>
<listitem><para><literal>newSession</literal> - Create a new "clean" session, without copying the existing session data.</para></listitem>
</itemizedlist>
</para>
</section>
2008-04-09 11:27:55 +00:00
</section>
<section xml:id="ns-method-security">
<title>Method Security</title>
<para>
Spring Security 2.0 has improved support substantially for adding security to your service layer methods. If you are
using Java 5 or greater, then support for JSR-250 security annotations is provided, as well as the framework's native
<literal>@secured</literal> annotation. You can apply security to a single bean, using the <literal>intercept-methods</literal>
element to decorate the bean declaration, or you can secure multiple beans across the entire service layer using the
AspectJ style pointcuts.
</para>
<section xml:id="ns-global-method">
<title>The <literal>&lt;global-method-security&gt;</literal> Element</title>
<para>
This element is used to enable annotation based security in your application (by setting the appropriate
attributes on the element), and also to group together security pointcut declarations which will be applied across your
entire application context. You should only declare one <literal>&lt;global-method-security&gt;</literal> element.
The following declaration would enable support for both types of annotations:
<programlisting><![CDATA[
<global-method-security secured-annotations="enabled" jsr250-annotations="true"/>
]]>
</programlisting>
</para>
<section xml:id="ns-protect-pointcut">
<title>Adding Security Pointcuts using <literal>protect-pointcut</literal></title>
<para>
The use of <literal>protect-pointcut</literal> is particularly powerful, as it allows you to
apply security to many beans with only a simple declaration. Consider the following example:
<programlisting><![CDATA[
<global-method-security>
<protect-pointcut expression="execution(* com.mycompany.*Service.*(..))" access="ROLE_USER"/>
</global-method-security>
]]>
</programlisting>
This will protect all methods on beans declared in the application context whose classes
are in the <literal>com.mycompany</literal> package and whose class names end in "Service".
Only users with the <literal>ROLE_USER</literal> role will be able to invoke these methods.
As with URL matching, the most specific matches must come first in the list of pointcuts, as the
first matching expression will be used.
</para>
</section>
<section xml:id="ns-global-method-access-mgr">
<title>Customizing the AccessDecisionManager</title>
<para>
The default namespace-registered AccessDecisionManager will be used automatically to
control method access. For more complex requirements you can specify another instance
using the <literal>access-decision-manager-ref</literal> attribute:
<programlisting><![CDATA[
<global-method-security access-decision-manager-ref="myAccessDecisionManagerBean">
...
</global-method-security>
]]></programlisting>
</para>
</section>
</section>
</section>
</chapter>