SEC-1168: Added filter-security-metadat-source to namespace.

This commit is contained in:
Luke Taylor 2009-06-08 12:59:13 +00:00
parent b50d37fa66
commit 66f7e8bcc8
14 changed files with 2001 additions and 2056 deletions

View File

@ -44,6 +44,8 @@ public abstract class Elements {
public static final String CUSTOM_AUTH_PROVIDER = "custom-authentication-provider"; public static final String CUSTOM_AUTH_PROVIDER = "custom-authentication-provider";
public static final String CUSTOM_AFTER_INVOCATION_PROVIDER = "custom-after-invocation-provider"; public static final String CUSTOM_AFTER_INVOCATION_PROVIDER = "custom-after-invocation-provider";
public static final String X509 = "x509"; public static final String X509 = "x509";
public static final String FILTER_SECURITY_METADATA_SOURCE = "filter-security-metadata-source";
@Deprecated
public static final String FILTER_INVOCATION_DEFINITION_SOURCE = "filter-invocation-definition-source"; public static final String FILTER_INVOCATION_DEFINITION_SOURCE = "filter-invocation-definition-source";
public static final String LDAP_PASSWORD_COMPARE = "password-compare"; public static final String LDAP_PASSWORD_COMPARE = "password-compare";
} }

View File

@ -409,8 +409,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
new DefaultFilterInvocationSecurityMetadataSource(matcher, channelRequestMap); new DefaultFilterInvocationSecurityMetadataSource(matcher, channelRequestMap);
channelFilterInvDefSource.setStripQueryStringFromUrls(matcher instanceof AntUrlPathMatcher); channelFilterInvDefSource.setStripQueryStringFromUrls(matcher instanceof AntUrlPathMatcher);
channelFilter.getPropertyValues().addPropertyValue("filterInvocationSecurityMetadataSource", channelFilter.getPropertyValues().addPropertyValue("securityMetadataSource", channelFilterInvDefSource);
channelFilterInvDefSource);
RootBeanDefinition channelDecisionManager = new RootBeanDefinition(ChannelDecisionManagerImpl.class); RootBeanDefinition channelDecisionManager = new RootBeanDefinition(ChannelDecisionManagerImpl.class);
ManagedList channelProcessors = new ManagedList(3); ManagedList channelProcessors = new ManagedList(3);
RootBeanDefinition secureChannelProcessor = new RootBeanDefinition(SecureChannelProcessor.class); RootBeanDefinition secureChannelProcessor = new RootBeanDefinition(SecureChannelProcessor.class);

View File

@ -12,6 +12,7 @@ import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
*/ */
public class SecurityNamespaceHandler extends NamespaceHandlerSupport { public class SecurityNamespaceHandler extends NamespaceHandlerSupport {
@SuppressWarnings("deprecation")
public void init() { public void init() {
// Parsers // Parsers
registerBeanDefinitionParser(Elements.LDAP_PROVIDER, new LdapProviderBeanDefinitionParser()); registerBeanDefinitionParser(Elements.LDAP_PROVIDER, new LdapProviderBeanDefinitionParser());
@ -24,12 +25,13 @@ public class SecurityNamespaceHandler extends NamespaceHandlerSupport {
registerBeanDefinitionParser(Elements.GLOBAL_METHOD_SECURITY, new GlobalMethodSecurityBeanDefinitionParser()); registerBeanDefinitionParser(Elements.GLOBAL_METHOD_SECURITY, new GlobalMethodSecurityBeanDefinitionParser());
registerBeanDefinitionParser(Elements.AUTHENTICATION_MANAGER, new AuthenticationManagerBeanDefinitionParser()); registerBeanDefinitionParser(Elements.AUTHENTICATION_MANAGER, new AuthenticationManagerBeanDefinitionParser());
registerBeanDefinitionParser(Elements.FILTER_INVOCATION_DEFINITION_SOURCE, new FilterInvocationSecurityMetadataSourceBeanDefinitionParser()); registerBeanDefinitionParser(Elements.FILTER_INVOCATION_DEFINITION_SOURCE, new FilterInvocationSecurityMetadataSourceBeanDefinitionParser());
registerBeanDefinitionParser(Elements.FILTER_SECURITY_METADATA_SOURCE, new FilterInvocationSecurityMetadataSourceBeanDefinitionParser());
// Decorators // Decorators
registerBeanDefinitionDecorator(Elements.INTERCEPT_METHODS, new InterceptMethodsBeanDefinitionDecorator()); registerBeanDefinitionDecorator(Elements.INTERCEPT_METHODS, new InterceptMethodsBeanDefinitionDecorator());
registerBeanDefinitionDecorator(Elements.FILTER_CHAIN_MAP, new FilterChainMapBeanDefinitionDecorator()); registerBeanDefinitionDecorator(Elements.FILTER_CHAIN_MAP, new FilterChainMapBeanDefinitionDecorator());
registerBeanDefinitionDecorator(Elements.CUSTOM_FILTER, new OrderedFilterBeanDefinitionDecorator()); registerBeanDefinitionDecorator(Elements.CUSTOM_FILTER, new OrderedFilterBeanDefinitionDecorator());
registerBeanDefinitionDecorator(Elements.CUSTOM_AUTH_PROVIDER, new CustomAuthenticationProviderBeanDefinitionDecorator()); registerBeanDefinitionDecorator(Elements.CUSTOM_AUTH_PROVIDER, new CustomAuthenticationProviderBeanDefinitionDecorator());
registerBeanDefinitionDecorator(Elements.CUSTOM_AFTER_INVOCATION_PROVIDER, new CustomAfterInvocationProviderBeanDefinitionDecorator()); registerBeanDefinitionDecorator(Elements.CUSTOM_AFTER_INVOCATION_PROVIDER, new CustomAfterInvocationProviderBeanDefinitionDecorator());
} }
} }

View File

@ -367,20 +367,24 @@ filter-chain.attlist &=
filter-chain.attlist &= filter-chain.attlist &=
attribute filters {xsd:token} attribute filters {xsd:token}
filter-invocation-definition-source = filter-security-metadata-source =
## Used to explicitly configure a FilterInvocationDefinitionSource bean for use with a FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy explicitly, rather than using the <http> element. The intercept-url elements used should only contain pattern, method and access attributes. Any others will result in a configuration error. ## Used to explicitly configure a FilterSecurityMetadataSource bean for use with a FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy explicitly, rather than using the <http> element. The intercept-url elements used should only contain pattern, method and access attributes. Any others will result in a configuration error.
element filter-invocation-definition-source {fids.attlist, intercept-url+} element filter-security-metadata-source {fsmds.attlist, intercept-url+}
fids.attlist &= fsmds.attlist &=
use-expressions? use-expressions?
fids.attlist &= fsmds.attlist &=
id? id?
fids.attlist &= fsmds.attlist &=
## as for http element ## as for http element
attribute lowercase-comparisons {boolean}? attribute lowercase-comparisons {boolean}?
fids.attlist &= fsmds.attlist &=
## as for http element ## as for http element
path-type? path-type?
filter-invocation-definition-source =
## Deprecated synonym for filter-security-metadata-source
element filter-invocation-definition-source {fsmds.attlist, intercept-url+}
http-basic = http-basic =
## Adds support for basic authentication (this is an element to permit future expansion, such as supporting an "ignoreFailure" attribute) ## Adds support for basic authentication (this is an element to permit future expansion, such as supporting an "ignoreFailure" attribute)
element http-basic {empty} element http-basic {empty}

View File

@ -10,7 +10,7 @@
<xsl:output method="xml" indent="yes"/> <xsl:output method="xml" indent="yes"/>
<xsl:variable name="elts-to-inline"> <xsl:variable name="elts-to-inline">
<xsl:text>,access-denied-handler,anonymous,concurrent-session-control,filter-chain,form-login,http-basic,intercept-url,logout,password-encoder,port-mappings,port-mapper,password-compare,protect,protect-pointcut,pre-post-annotation-handling,pre-invocation-advice,post-invocation-advice,invocation-attribute-factory,remember-me,salt-source,x509,</xsl:text> <xsl:text>,access-denied-handler,anonymous,concurrent-session-control,user,port-mapping,openid-login,expression-handler,filter-chain,form-login,http-basic,intercept-url,logout,password-encoder,port-mappings,port-mapper,password-compare,protect,protect-pointcut,pre-post-annotation-handling,pre-invocation-advice,post-invocation-advice,invocation-attribute-factory,remember-me,salt-source,x509,</xsl:text>
</xsl:variable> </xsl:variable>
<xsl:template match="xs:element"> <xsl:template match="xs:element">

View File

@ -23,7 +23,7 @@ import org.w3c.dom.Element;
* @author Luke Taylor * @author Luke Taylor
* @version $Id$ * @version $Id$
*/ */
public class FilterInvocationSecurityMetadataSourceBeanDefinitionParserTests { public class FilterSecurityMetadataSourceBeanDefinitionParserTests {
private AbstractXmlApplicationContext appContext; private AbstractXmlApplicationContext appContext;
@After @After
@ -46,9 +46,9 @@ public class FilterInvocationSecurityMetadataSourceBeanDefinitionParserTests {
@Test @Test
public void parsingMinimalConfigurationIsSuccessful() { public void parsingMinimalConfigurationIsSuccessful() {
setContext( setContext(
"<filter-invocation-definition-source id='fids'>" + "<filter-security-metadata-source id='fids'>" +
" <intercept-url pattern='/**' access='ROLE_A'/>" + " <intercept-url pattern='/**' access='ROLE_A'/>" +
"</filter-invocation-definition-source>"); "</filter-security-metadata-source>");
DefaultFilterInvocationSecurityMetadataSource fids = (DefaultFilterInvocationSecurityMetadataSource) appContext.getBean("fids"); DefaultFilterInvocationSecurityMetadataSource fids = (DefaultFilterInvocationSecurityMetadataSource) appContext.getBean("fids");
List<? extends ConfigAttribute> cad = fids.getAttributes(createFilterInvocation("/anything", "GET")); List<? extends ConfigAttribute> cad = fids.getAttributes(createFilterInvocation("/anything", "GET"));
assertNotNull(cad); assertNotNull(cad);
@ -61,11 +61,11 @@ public class FilterInvocationSecurityMetadataSourceBeanDefinitionParserTests {
"<http auto-config='true'/>" + "<http auto-config='true'/>" +
"<b:bean id='fsi' class='org.springframework.security.web.access.intercept.FilterSecurityInterceptor' autowire='byType'>" + "<b:bean id='fsi' class='org.springframework.security.web.access.intercept.FilterSecurityInterceptor' autowire='byType'>" +
" <b:property name='securityMetadataSource'>" + " <b:property name='securityMetadataSource'>" +
" <filter-invocation-definition-source>" + " <filter-security-metadata-source>" +
" <intercept-url pattern='/secure/extreme/**' access='ROLE_SUPERVISOR'/>" + " <intercept-url pattern='/secure/extreme/**' access='ROLE_SUPERVISOR'/>" +
" <intercept-url pattern='/secure/**' access='ROLE_USER'/>" + " <intercept-url pattern='/secure/**' access='ROLE_USER'/>" +
" <intercept-url pattern='/**' access='ROLE_USER'/>" + " <intercept-url pattern='/**' access='ROLE_USER'/>" +
" </filter-invocation-definition-source>" + " </filter-security-metadata-source>" +
" </b:property>" + " </b:property>" +
"</b:bean>" + ConfigTestUtils.AUTH_PROVIDER_XML); "</b:bean>" + ConfigTestUtils.AUTH_PROVIDER_XML);

View File

@ -16,10 +16,17 @@
<global-method-security run-as-manager-ref="myRunAsManager"> <global-method-security run-as-manager-ref="myRunAsManager">
<pre-post-annotation-handling> <pre-post-annotation-handling>
< <invocation-attribute-factory ref=""/>
<pre-invocation-advice ref=""/>
<post-invocation-advice ref=""/>
</pre-post-annotation-handling> </pre-post-annotation-handling>
</global-method-security> </global-method-security>
<filter-security-metadata-source>
<intercept-url pattern=""/>
</filter-security-metadata-source>
<http> <http>
<access-denied-handler error-page="/go-away.html"/> <access-denied-handler error-page="/go-away.html"/>
<intercept-url pattern="/**" access="ROLE_USER" /> <intercept-url pattern="/**" access="ROLE_USER" />

View File

@ -1,47 +1,41 @@
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="anonymous"> <chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="anonymous">
<info>
<info><title>Anonymous Authentication</title></info> <title>Anonymous Authentication</title>
</info>
<section xml:id="anonymous-overview">
<section xml:id="anonymous-overview"> <info>
<info><title>Overview</title></info> <title>Overview</title>
</info>
<para>Particularly in the case of web request URI security, sometimes <para>Particularly in the case of web request URI security, sometimes it is more convenient
it is more convenient to assign configuration attributes against every to assign configuration attributes against every possible secure object invocation. Put
possible secure object invocation. Put differently, sometimes it is differently, sometimes it is nice to say <literal>ROLE_SOMETHING</literal> is required
nice to say <literal>ROLE_SOMETHING</literal> is required by default by default and only allow certain exceptions to this rule, such as for login, logout and
and only allow certain exceptions to this rule, such as for login, home pages of an application. There are also other situations where anonymous
logout and home pages of an application. There are also other authentication would be desired, such as when an auditing interceptor queries the
situations where anonymous authentication would be desired, such as <classname>SecurityContextHolder</classname> to identify which principal was
when an auditing interceptor queries the responsible for a given operation. Such classes can be authored with more robustness if
<classname>SecurityContextHolder</classname> to identify which principal they know the <classname>SecurityContextHolder</classname> always contains an
was responsible for a given operation. Such classes can be authored <interfacename>Authentication</interfacename> object, and never
with more robustness if they know the <literal>null</literal>.</para>
<classname>SecurityContextHolder</classname> always contains an </section>
<interfacename>Authentication</interfacename> object, and never <section xml:id="anonymous-config">
<literal>null</literal>.</para> <info>
</section> <title>Configuration</title>
</info>
<section xml:id="anonymous-config"> <para>Spring Security provides three classes that together provide an anonymous
<info><title>Configuration</title></info> authentication feature. <literal>AnonymousAuthenticationToken</literal> is an
implementation of <interfacename>Authentication</interfacename>, and stores the
<para>Spring Security provides three classes that together provide an <interfacename>GrantedAuthority</interfacename>[]s which apply to the anonymous
anonymous authentication feature. principal. There is a corresponding <literal>AnonymousAuthenticationProvider</literal>,
<literal>AnonymousAuthenticationToken</literal> is an implementation which is chained into the <literal>ProviderManager</literal> so that
of <interfacename>Authentication</interfacename>, and stores the <literal>AnonymousAuthenticationTokens</literal> are accepted. Finally, there is an
<interfacename>GrantedAuthority</interfacename>[]s which apply to the anonymous AnonymousProcessingFilter, which is chained after the normal authentication mechanisms
principal. There is a corresponding and automatically add an <literal>AnonymousAuthenticationToken</literal> to the
<literal>AnonymousAuthenticationProvider</literal>, which is chained <classname>SecurityContextHolder</classname> if there is no existing
into the <literal>ProviderManager</literal> so that <interfacename>Authentication</interfacename> held there. The definition of the
<literal>AnonymousAuthenticationTokens</literal> are accepted. filter and authentication provider appears as follows:</para>
Finally, there is an AnonymousProcessingFilter, which is chained after <para>
the normal authentication mechanisms and automatically add an <programlisting>
<literal>AnonymousAuthenticationToken</literal> to the
<classname>SecurityContextHolder</classname> if there is no existing
<interfacename>Authentication</interfacename> held there. The definition of the
filter and authentication provider appears as follows:</para>
<para><programlisting>
<![CDATA[ <![CDATA[
<bean id="anonymousProcessingFilter" <bean id="anonymousProcessingFilter"
class="org.springframework.security.web.authentication.AnonymousProcessingFilter"> class="org.springframework.security.web.authentication.AnonymousProcessingFilter">
@ -53,53 +47,44 @@
class="org.springframework.security.authentication.AnonymousAuthenticationProvider"> class="org.springframework.security.authentication.AnonymousAuthenticationProvider">
<property name="key" value="foobar"/> <property name="key" value="foobar"/>
</bean>]]> </bean>]]>
</programlisting></para> </programlisting>
</para>
<para>The <literal>key</literal> is shared between the filter and <para>The <literal>key</literal> is shared between the filter and authentication provider,
authentication provider, so that tokens created by the former are so that tokens created by the former are accepted by the latter. The
accepted by the latter. The <literal>userAttribute</literal> is <literal>userAttribute</literal> is expressed in the form of
expressed in the form of <literal>usernameInTheAuthenticationToken,grantedAuthority[,grantedAuthority]</literal>.
<literal>usernameInTheAuthenticationToken,grantedAuthority[,grantedAuthority]</literal>. This is the same syntax as used after the equals sign for
This is the same syntax as used after the equals sign for <literal>InMemoryDaoImpl</literal>'s <literal>userMap</literal> property.</para>
<literal>InMemoryDaoImpl</literal>'s <literal>userMap</literal> <para>As explained earlier, the benefit of anonymous authentication is that all URI patterns
property.</para> can have security applied to them. For example:</para>
<para><programlisting>
<para>As explained earlier, the benefit of anonymous authentication is
that all URI patterns can have security applied to them. For
example:</para>
<para><programlisting>
<![CDATA[ <![CDATA[
<bean id="filterInvocationInterceptor" <bean id="filterInvocationInterceptor"
class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor"> class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/> <property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/> <property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/>
<property name="objectDefinitionSource"> <property name="securityMetadata">
<security:filter-invocation-definition-source> <security:filter-security-metadata-source>
<security:intercept-url pattern='/index.jsp' access='ROLE_ANONYMOUS,ROLE_USER'/> <security:intercept-url pattern='/index.jsp' access='ROLE_ANONYMOUS,ROLE_USER'/>
<security:intercept-url pattern='/hello.htm' access='ROLE_ANONYMOUS,ROLE_USER'/> <security:intercept-url pattern='/hello.htm' access='ROLE_ANONYMOUS,ROLE_USER'/>
<security:intercept-url pattern='/logoff.jsp' access='ROLE_ANONYMOUS,ROLE_USER'/> <security:intercept-url pattern='/logoff.jsp' access='ROLE_ANONYMOUS,ROLE_USER'/>
<security:intercept-url pattern='/login.jsp' access='ROLE_ANONYMOUS,ROLE_USER'/> <security:intercept-url pattern='/login.jsp' access='ROLE_ANONYMOUS,ROLE_USER'/>
<security:intercept-url pattern='/**' access='ROLE_USER'/> <security:intercept-url pattern='/**' access='ROLE_USER'/>
</security:filter-invocation-definition-source>" + </security:filter-security-metadata-source>" +
</property> </property>
</bean>]]> </bean>]]>
</programlisting>Rounding out the anonymous authentication discussion </programlisting>Rounding out the anonymous authentication discussion is the
is the <literal>AuthenticationTrustResolver</literal> interface, with <literal>AuthenticationTrustResolver</literal> interface, with its corresponding
its corresponding <literal>AuthenticationTrustResolverImpl</literal> <literal>AuthenticationTrustResolverImpl</literal> implementation. This interface
implementation. This interface provides an provides an <literal>isAnonymous(Authentication)</literal> method, which allows
<literal>isAnonymous(Authentication)</literal> method, which allows interested classes to take into account this special type of authentication status. The
interested classes to take into account this special type of <classname>ExceptionTranslationFilter</classname> uses this interface in processing
authentication status. The <literal>AccessDeniedException</literal>s. If an
<classname>ExceptionTranslationFilter</classname> uses this interface in <literal>AccessDeniedException</literal> is thrown, and the authentication is of an
processing <literal>AccessDeniedException</literal>s. If an anonymous type, instead of throwing a 403 (forbidden) response, the filter will instead
<literal>AccessDeniedException</literal> is thrown, and the commence the <interfacename>AuthenticationEntryPoint</interfacename> so the principal
authentication is of an anonymous type, instead of throwing a 403 can authenticate properly. This is a necessary distinction, otherwise principals would
(forbidden) response, the filter will instead commence the always be deemed "authenticated" and never be given an opportunity to login via form,
<interfacename>AuthenticationEntryPoint</interfacename> so the principal can basic, digest or some other normal authentication mechanism</para>
authenticate properly. This is a necessary distinction, otherwise </section>
principals would always be deemed "authenticated" and never be given
an opportunity to login via form, basic, digest or some other normal
authentication mechanism</para>
</section>
</chapter> </chapter>

View File

@ -1,52 +1,49 @@
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="channel-security" xmlns:xlink="http://www.w3.org/1999/xlink"> <chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="channel-security"
xmlns:xlink="http://www.w3.org/1999/xlink">
<info><title>Channel Security</title></info> <info>
<title>Channel Security</title>
<section xml:id="channel-security-overview"> </info>
<info><title>Overview</title></info> <section xml:id="channel-security-overview">
<info>
<para>In addition to coordinating the authentication and authorization <title>Overview</title>
requirements of your application, Spring Security is also able to </info>
ensure unauthenticated web requests have certain properties. These <para>In addition to coordinating the authentication and authorization requirements of your
properties may include being of a particular transport type, having a application, Spring Security is also able to ensure unauthenticated web requests have
particular <literal>HttpSession</literal> attribute set and so on. The certain properties. These properties may include being of a particular transport type,
most common requirement is for your web requests to be received using having a particular <literal>HttpSession</literal> attribute set and so on. The most
a particular transport protocol, such as HTTPS.</para> common requirement is for your web requests to be received using a particular transport
protocol, such as HTTPS.</para>
<para>An important issue in considering transport security is that of <para>An important issue in considering transport security is that of session hijacking.
session hijacking. Your web container manages a Your web container manages a <literal>HttpSession</literal> by reference to a
<literal>HttpSession</literal> by reference to a <literal>jsessionid</literal> that is sent to user agents either via a cookie or URL
<literal>jsessionid</literal> that is sent to user agents either via a rewriting. If the <literal>jsessionid</literal> is ever sent over HTTP, there is a
cookie or URL rewriting. If the <literal>jsessionid</literal> is ever possibility that session identifier can be intercepted and used to impersonate the user
sent over HTTP, there is a possibility that session identifier can be after they complete the authentication process. This is because most web containers
intercepted and used to impersonate the user after they complete the maintain the same session identifier for a given user, even after they switch from HTTP
authentication process. This is because most web containers maintain to HTTPS pages.</para>
the same session identifier for a given user, even after they switch <para>If session hijacking is considered too significant a risk for your particular
from HTTP to HTTPS pages.</para> application, the only option is to use HTTPS for every request. This means the
<literal>jsessionid</literal> is never sent across an insecure channel. You will
<para>If session hijacking is considered too significant a risk for need to ensure your <literal>web.xml</literal>-defined
your particular application, the only option is to use HTTPS for every <literal>&lt;welcome-file&gt;</literal> points to an HTTPS location, and the
request. This means the <literal>jsessionid</literal> is never sent application never directs the user to an HTTP location. Spring Security provides a
across an insecure channel. You will need to ensure your solution to assist with the latter.</para>
<literal>web.xml</literal>-defined </section>
<literal>&lt;welcome-file&gt;</literal> points to an HTTPS location, <section xml:id="channel-security-config">
and the application never directs the user to an HTTP location. Spring <info>
Security provides a solution to assist with the latter.</para> <title>Configuration</title>
</section> </info>
<para>Channel security is supported by the <link xlink:href="#ns-requires-channel">security
<section xml:id="channel-security-config"> namespace</link> by means of the <literal>requires-channel</literal> attribute on
<info><title>Configuration</title></info> the <literal>&lt;intercept-url&gt;</literal> element and this is the simplest (and
<para>Channel security is supported by the <link xlink:href="#ns-requires-channel">security namespace</link> recommended approach).</para>
by means of the <literal>requires-channel</literal> attribute on the <literal>&lt;intercept-url&gt;</literal> <para>To confiure channel security explicitly, you would define the following the filter in
element and this is the simplest (and recommended approach).</para> your application context: <programlisting><![CDATA[
<para>To confiure channel security explicitly, you would define the following the filter in your application
context:
<programlisting><![CDATA[
<bean id="channelProcessingFilter" <bean id="channelProcessingFilter"
class="org.springframework.security.web.access.channel.ChannelProcessingFilter"> class="org.springframework.security.web.access.channel.ChannelProcessingFilter">
<property name="channelDecisionManager" ref="channelDecisionManager"/> <property name="channelDecisionManager" ref="channelDecisionManager"/>
<property name="filterInvocationSecurityMetadataSource"> <property name="securityMetadataSource">
<security:filter-invocation-definition-source path-type="regex"> <security:filter-security-metadata-source path-type="regex">
<security:intercept-url pattern="\A/secure/.*\Z" <security:intercept-url pattern="\A/secure/.*\Z"
access="REQUIRES_SECURE_CHANNEL"/> access="REQUIRES_SECURE_CHANNEL"/>
<security:intercept-url pattern="\A/acegilogin.jsp.*\Z" <security:intercept-url pattern="\A/acegilogin.jsp.*\Z"
@ -54,7 +51,7 @@
<security:intercept-url pattern="\A/j_spring_security_check.*\Z" <security:intercept-url pattern="\A/j_spring_security_check.*\Z"
access="REQUIRES_SECURE_CHANNEL"/> access="REQUIRES_SECURE_CHANNEL"/>
<security:intercept-url pattern="\A/.*\Z" access="ANY_CHANNEL"/> <security:intercept-url pattern="\A/.*\Z" access="ANY_CHANNEL"/>
</security:filter-invocation-definition-source> </security:filter-security-metadata-source>
</property> </property>
</bean> </bean>
@ -73,95 +70,73 @@
<bean id="insecureChannelProcessor" <bean id="insecureChannelProcessor"
class="org.springframework.security.access.channel.InsecureChannelProcessor"/>]]> class="org.springframework.security.access.channel.InsecureChannelProcessor"/>]]>
</programlisting> </programlisting>
Like <classname>FilterSecurityInterceptor</classname>, Apache Ant Like <classname>FilterSecurityInterceptor</classname>, Apache Ant style paths are also
style paths are also supported by the supported by the <literal>ChannelProcessingFilter</literal>.</para>
<literal>ChannelProcessingFilter</literal>.</para> <para>The <literal>ChannelProcessingFilter</literal> operates by filtering all web requests
and determining the configuration attributes that apply. It then delegates to the
<para>The <literal>ChannelProcessingFilter</literal> operates by <literal>ChannelDecisionManager</literal>. The default implementation,
filtering all web requests and determining the configuration <literal>ChannelDecisionManagerImpl</literal>, should suffice in most cases. It
attributes that apply. It then delegates to the simply delegates to the list of configured <literal>ChannelProcessor</literal>
<literal>ChannelDecisionManager</literal>. The default implementation, instances. The attribute <literal>ANY_CHANNEL</literal> can be used to override this
<literal>ChannelDecisionManagerImpl</literal>, should suffice in most behaviour and skip a particular URL. Otherwise, a <literal>ChannelProcessor</literal>
cases. It simply delegates to the list of configured will review the request, and if it is unhappy with the request (e.g. if it was received
<literal>ChannelProcessor</literal> instances. The attribute <literal>ANY_CHANNEL</literal> across the incorrect transport protocol), it will perform a redirect, throw an exception
can be used to override this behaviour and skip a particular URL. Otherwise, a or take whatever other action is appropriate.</para>
<literal>ChannelProcessor</literal> will review the request, and if it <para>Included with Spring Security are two concrete <literal>ChannelProcessor</literal>
is unhappy with the request (e.g. if it was received across the incorrect implementations: <literal>SecureChannelProcessor</literal> ensures requests with a
transport protocol), it will perform a redirect, throw an exception or configuration attribute of <literal>REQUIRES_SECURE_CHANNEL</literal> are received over
take whatever other action is appropriate.</para> HTTPS, whilst <literal>InsecureChannelProcessor</literal> ensures requests with a
configuration attribute of <literal>REQUIRES_INSECURE_CHANNEL</literal> are received
<para>Included with Spring Security are two concrete over HTTP. Both implementations delegate to a <literal>ChannelEntryPoint</literal> if
<literal>ChannelProcessor</literal> implementations: the required transport protocol is not used. The two
<literal>SecureChannelProcessor</literal> ensures requests with a <literal>ChannelEntryPoint</literal> implementations included with Spring Security
configuration attribute of <literal>REQUIRES_SECURE_CHANNEL</literal> simply redirect the request to HTTP and HTTPS as appropriate. Appropriate defaults are
are received over HTTPS, whilst assigned to the <literal>ChannelProcessor</literal> implementations for the
<literal>InsecureChannelProcessor</literal> ensures requests with a configuration attribute keywords they respond to and the
configuration attribute of <interfacename>ChannelEntryPoint</interfacename> they delegate to, although you have
<literal>REQUIRES_INSECURE_CHANNEL</literal> are received over HTTP. the ability to override these using the application context.</para>
Both implementations delegate to a <para>Note that the redirections are absolute (eg
<literal>ChannelEntryPoint</literal> if the required transport <literal>http://www.company.com:8080/app/page</literal>), not relative (eg
protocol is not used. The two <literal>ChannelEntryPoint</literal> <literal>/app/page</literal>). During testing it was discovered that Internet
implementations included with Spring Security simply redirect the Explorer 6 Service Pack 1 has a bug whereby it does not respond correctly to a
request to HTTP and HTTPS as appropriate. Appropriate defaults are redirection instruction which also changes the port to use. Accordingly, absolute URLs
assigned to the <literal>ChannelProcessor</literal> implementations are used in conjunction with bug detection logic in the
for the configuration attribute keywords they respond to and the <classname>PortResolverImpl</classname> that is wired up by default to many Spring
<interfacename>ChannelEntryPoint</interfacename> they delegate to, although you Security beans. Please refer to the JavaDocs for <classname>PortResolverImpl</classname>
have the ability to override these using the application for further details.</para>
context.</para> <para>You should note that using a secure channel is recommended if usernames and passwords
are to be kept secure during the login process. If you do decide to use
<para>Note that the redirections are absolute (eg <classname>ChannelProcessingFilter</classname> with form-based login, please ensure
<literal>http://www.company.com:8080/app/page</literal>), not relative that your login page is set to <literal>REQUIRES_SECURE_CHANNEL</literal>, and that the
(eg <literal>/app/page</literal>). During testing it was discovered <literal>LoginUrlAuthenticationEntryPoint.forceHttps</literal> property is
that Internet Explorer 6 Service Pack 1 has a bug whereby it does not <literal>true</literal>.</para>
respond correctly to a redirection instruction which also changes the </section>
port to use. Accordingly, absolute URLs are used in conjunction with <section xml:id="channel-security-conclusion">
bug detection logic in the <classname>PortResolverImpl</classname> that is <info>
wired up by default to many Spring Security beans. Please refer to the <title>Conclusion</title>
JavaDocs for <classname>PortResolverImpl</classname> for further </info>
details.</para> <para>Once configured, using the channel security filter is very easy. Simply request pages
without regard to the protocol (ie HTTP or HTTPS) or port (eg 80, 8080, 443, 8443 etc).
<para>You should note that using a secure channel is recommended if Obviously you'll still need a way of making the initial request (probably via the
usernames and passwords are to be kept secure during the login <literal>web.xml</literal>
process. If you do decide to use <literal>&lt;welcome-file&gt;</literal> or a well-known home page URL), but once this is
<classname>ChannelProcessingFilter</classname> with form-based login, done the filter will perform redirects as defined by your application context.</para>
please ensure that your login page is set to <para>You can also add your own <literal>ChannelProcessor</literal> implementations to the
<literal>REQUIRES_SECURE_CHANNEL</literal>, and that the <literal>ChannelDecisionManagerImpl</literal>. For example, you might set a
<literal>LoginUrlAuthenticationEntryPoint.forceHttps</literal> <literal>HttpSession</literal> attribute when a human user is detected via a "enter
property is <literal>true</literal>.</para> the contents of this graphic" procedure. Your <literal>ChannelProcessor</literal> would
</section> respond to say <literal>REQUIRES_HUMAN_USER</literal> configuration attributes and
redirect to an appropriate entry point to start the human user validation process if the
<section xml:id="channel-security-conclusion"> <literal>HttpSession</literal> attribute is not currently set.</para>
<info><title>Conclusion</title></info> <para>To decide whether a security check belongs in a <literal>ChannelProcessor</literal> or
an <interfacename>AccessDecisionVoter</interfacename>, remember that the former is
<para>Once configured, using the channel security filter is very easy. designed to handle unauthenticated requests, whilst the latter is designed to handle
Simply request pages without regard to the protocol (ie HTTP or HTTPS) authenticated requests. The latter therefore has access to the granted authorities of
or port (eg 80, 8080, 443, 8443 etc). Obviously you'll still need a the authenticated principal. In addition, problems detected by a
way of making the initial request (probably via the <literal>ChannelProcessor</literal> will generally cause an HTTP/HTTPS redirection
<literal>web.xml</literal> <literal>&lt;welcome-file&gt;</literal> or so its requirements can be met, whilst problems detected by an
a well-known home page URL), but once this is done the filter will <interfacename>AccessDecisionVoter</interfacename> will ultimately result in an
perform redirects as defined by your application context.</para> <literal>AccessDeniedException</literal> (depending on the governing
<interfacename>AccessDecisionManager</interfacename>).</para>
<para>You can also add your own <literal>ChannelProcessor</literal> </section>
implementations to the <literal>ChannelDecisionManagerImpl</literal>. </chapter>
For example, you might set a <literal>HttpSession</literal> attribute
when a human user is detected via a "enter the contents of this
graphic" procedure. Your <literal>ChannelProcessor</literal> would
respond to say <literal>REQUIRES_HUMAN_USER</literal> configuration
attributes and redirect to an appropriate entry point to start the
human user validation process if the <literal>HttpSession</literal>
attribute is not currently set.</para>
<para>To decide whether a security check belongs in a
<literal>ChannelProcessor</literal> or an
<interfacename>AccessDecisionVoter</interfacename>, remember that the former is
designed to handle unauthenticated requests, whilst the latter is
designed to handle authenticated requests. The latter therefore has
access to the granted authorities of the authenticated principal. In
addition, problems detected by a <literal>ChannelProcessor</literal>
will generally cause an HTTP/HTTPS redirection so its requirements can
be met, whilst problems detected by an
<interfacename>AccessDecisionVoter</interfacename> will ultimately result in an
<literal>AccessDeniedException</literal> (depending on the governing
<interfacename>AccessDecisionManager</interfacename>).</para>
</section>
</chapter>

View File

@ -1,36 +1,32 @@
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="secure-object-impls" xmlns:xlink="http://www.w3.org/1999/xlink"> <chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="secure-object-impls"
<info><title>Secure Object Implementations</title></info> xmlns:xlink="http://www.w3.org/1999/xlink">
<info>
<title>Secure Object Implementations</title>
</info>
<section xml:id="aop-alliance"> <section xml:id="aop-alliance">
<info> <info>
<title>AOP Alliance (MethodInvocation) Security Interceptor</title> <title>AOP Alliance (MethodInvocation) Security Interceptor</title>
</info> </info>
<para> Prior to Spring Security 2.0, securing <classname>MethodInvocation</classname>s needed
<para> quite a lot of boiler plate configuration. Now the recommended approach for method security is
Prior to Spring Security 2.0, securing <classname>MethodInvocation</classname>s needed quite a to use <link xlink:href="#ns-method-security">namespace configuration</link>. This way the
lot of boiler plate configuration. Now the recommended approach for method security method security infrastructure beans are configured automatically for you so you don't really
is to use <link xlink:href="#ns-method-security">namespace configuration</link>. need to know about the implementation classes. We'll just provide a quick overview of the
This way the method security infrastructure beans are configured automatically for you so you don't really need to classes that are involved here. </para>
know about the implementation classes. We'll just provide a quick overview of the classes that are involved here. <para> Method security in enforced using a <classname>MethodSecurityInterceptor</classname>,
</para> which secures <classname>MethodInvocation</classname>s. Depending on the configuration
approach, an interceptor may be specific to a single bean or shared between multiple beans.
<para> The interceptor uses a <interfacename>MethodDefinitionSource</interfacename> instance to
Method security in enforced using a <classname>MethodSecurityInterceptor</classname>, which secures obtain the configuration attributes that apply to a particular method invocation.
<classname>MethodInvocation</classname>s. Depending on the configuration approach, an interceptor may be specific to a single <classname>MapBasedMethodDefinitionSource</classname> is used to store configuration
bean or shared between multiple beans. The interceptor uses a <interfacename>MethodDefinitionSource</interfacename> attributes keyed by method names (which can be wildcarded) and will be used internally when
instance to obtain the configuration attributes that apply to a particular method invocation. the attributes are defined in the application context using the
<classname>MapBasedMethodDefinitionSource</classname> is used to store configuration attributes keyed by method names <literal>&lt;intercept-methods&gt;</literal> or <literal>&lt;protect-point&gt;</literal>
(which can be wildcarded) and will be used internally when the attributes are defined in the application context using elements. Other implementations will be used to handle annotation-based configuration. </para>
the <literal>&lt;intercept-methods&gt;</literal> or <literal>&lt;protect-point&gt;</literal> elements. Other implementations
will be used to handle annotation-based configuration.
</para>
<section> <section>
<title>Explicit MethodSecurityIterceptor Configuration</title> <title>Explicit MethodSecurityIterceptor Configuration</title>
<para> <para> You can of course configure a <classname>MethodSecurityIterceptor</classname> directly
You can of course configure a <classname>MethodSecurityIterceptor</classname> directly in your application context in your application context for use with one of Spring AOP's proxying mechanisms: <programlisting><![CDATA[
for use with one of Spring AOP's proxying mechanisms:
<programlisting><![CDATA[
<bean id="bankManagerSecurity" <bean id="bankManagerSecurity"
class="org.springframework.security.intercept.aopalliance.MethodSecurityInterceptor"> class="org.springframework.security.intercept.aopalliance.MethodSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/> <property name="authenticationManager" ref="authenticationManager"/>
@ -43,37 +39,26 @@
</value> </value>
</property> </property>
</bean> ]]> </bean> ]]>
</programlisting> </programlisting></para>
</para>
</section> </section>
</section> </section>
<section xml:id="aspectj"> <section xml:id="aspectj">
<info> <info>
<title>AspectJ (JoinPoint) Security Interceptor</title> <title>AspectJ (JoinPoint) Security Interceptor</title>
</info> </info>
<para>The AspectJ security interceptor is very similar to the AOP Alliance security interceptor
<para>The AspectJ security interceptor is very similar to the AOP discussed in the previous section. Indeed we will only discuss the differences in this
Alliance security interceptor discussed in the previous section. section.</para>
Indeed we will only discuss the differences in this section.</para> <para>The AspectJ interceptor is named <literal>AspectJSecurityInterceptor</literal>. Unlike the
AOP Alliance security interceptor, which relies on the Spring application context to weave in
<para>The AspectJ interceptor is named the security interceptor via proxying, the <literal>AspectJSecurityInterceptor</literal> is
<literal>AspectJSecurityInterceptor</literal>. Unlike the AOP Alliance weaved in via the AspectJ compiler. It would not be uncommon to use both types of security
security interceptor, which relies on the Spring application context interceptors in the same application, with <literal>AspectJSecurityInterceptor</literal> being
to weave in the security interceptor via proxying, the used for domain object instance security and the AOP Alliance
<literal>AspectJSecurityInterceptor</literal> is weaved in via the <classname>MethodSecurityInterceptor</classname> being used for services layer
AspectJ compiler. It would not be uncommon to use both types of security.</para>
security interceptors in the same application, with <para>Let's first consider how the <literal>AspectJSecurityInterceptor</literal> is configured
<literal>AspectJSecurityInterceptor</literal> being used for domain in the Spring application context:</para>
object instance security and the AOP Alliance
<classname>MethodSecurityInterceptor</classname> being used for services
layer security.</para>
<para>Let's first consider how the
<literal>AspectJSecurityInterceptor</literal> is configured in the
Spring application context:</para>
<programlisting><![CDATA[ <programlisting><![CDATA[
<bean id="bankManagerSecurity" <bean id="bankManagerSecurity"
class="org.springframework.security.intercept.aspectj.AspectJSecurityInterceptor"> class="org.springframework.security.intercept.aspectj.AspectJSecurityInterceptor">
@ -87,23 +72,18 @@
</value> </value>
</property> </property>
</bean>]]> </programlisting> </bean>]]> </programlisting>
<para>As you can see, aside from the class name, the <para>As you can see, aside from the class name, the
<literal>AspectJSecurityInterceptor</literal> is exactly the same as <literal>AspectJSecurityInterceptor</literal> is exactly the same as the AOP Alliance
the AOP Alliance security interceptor. Indeed the two interceptors can security interceptor. Indeed the two interceptors can share the same
share the same <literal>securityMetadataSource</literal>, as the <literal>securityMetadataSource</literal>, as the
<interfacename>SecurityMetadataSource</interfacename> works with <interfacename>SecurityMetadataSource</interfacename> works with
<literal>java.lang.reflect.Method</literal>s rather than an AOP <literal>java.lang.reflect.Method</literal>s rather than an AOP library-specific class. Of
library-specific class. Of course, your access decisions have access course, your access decisions have access to the relevant AOP library-specific invocation (ie
to the relevant AOP library-specific invocation (ie <classname>MethodInvocation</classname> or <literal>JoinPoint</literal>) and as such can
<classname>MethodInvocation</classname> or <literal>JoinPoint</literal>) consider a range of addition criteria when making access decisions (such as method
and as such can consider a range of addition criteria when making arguments).</para>
access decisions (such as method arguments).</para> <para>Next you'll need to define an AspectJ <literal>aspect</literal>. For example:</para>
<programlisting language="java">
<para>Next you'll need to define an AspectJ <literal>aspect</literal>.
For example:</para>
<programlisting>
package org.springframework.security.samples.aspectj; package org.springframework.security.samples.aspectj;
import org.springframework.security.intercept.aspectj.AspectJSecurityInterceptor; import org.springframework.security.intercept.aspectj.AspectJSecurityInterceptor;
@ -144,22 +124,16 @@ public void afterPropertiesSet() throws Exception {
throw new IllegalArgumentException("securityInterceptor required"); throw new IllegalArgumentException("securityInterceptor required");
} }
}</programlisting> }</programlisting>
<para>In the above example, the security interceptor will be applied to every instance of
<para>In the above example, the security interceptor will be applied <literal>PersistableEntity</literal>, which is an abstract class not shown (you can use any
to every instance of <literal>PersistableEntity</literal>, which is an other class or <literal>pointcut</literal> expression you like). For those curious,
abstract class not shown (you can use any other class or <literal>AspectJCallback</literal> is needed because the <literal>proceed();</literal>
<literal>pointcut</literal> expression you like). For those curious, statement has special meaning only within an <literal>around()</literal> body. The
<literal>AspectJCallback</literal> is needed because the <literal>AspectJSecurityInterceptor</literal> calls this anonymous
<literal>proceed();</literal> statement has special meaning only <literal>AspectJCallback</literal> class when it wants the target object to continue.</para>
within an <literal>around()</literal> body. The <para>You will need to configure Spring to load the aspect and wire it with the
<literal>AspectJSecurityInterceptor</literal> calls this anonymous <literal>AspectJSecurityInterceptor</literal>. A bean declaration which achieves this is
<literal>AspectJCallback</literal> class when it wants the target shown below:</para>
object to continue.</para>
<para>You will need to configure Spring to load the aspect and wire it
with the <literal>AspectJSecurityInterceptor</literal>. A bean
declaration which achieves this is shown below:</para>
<programlisting><![CDATA[ <programlisting><![CDATA[
<bean id="domainObjectInstanceSecurityAspect" <bean id="domainObjectInstanceSecurityAspect"
class="org.springframework.security.samples.aspectj.DomainObjectInstanceSecurityAspect" class="org.springframework.security.samples.aspectj.DomainObjectInstanceSecurityAspect"
@ -167,23 +141,18 @@ public void afterPropertiesSet() throws Exception {
<property name="securityInterceptor" ref="aspectJSecurityInterceptor"/> <property name="securityInterceptor" ref="aspectJSecurityInterceptor"/>
</bean>]]> </bean>]]>
</programlisting> </programlisting>
<para>That's it! Now you can create your beans from anywhere within your application, using
<para>That's it! Now you can create your beans from anywhere within whatever means you think fit (eg <literal>new Person();</literal>) and they will have the
your application, using whatever means you think fit (eg <literal>new security interceptor applied.</para>
Person();</literal>) and they will have the security interceptor
applied.</para>
</section> </section>
<section xml:id="filter-invocation-authorization"> <section xml:id="filter-invocation-authorization">
<info><title>FilterInvocation Security Interceptor</title></info> <info>
<title>FilterInvocation Security Interceptor</title>
<para>To secure <classname>FilterInvocation</classname>s, developers need </info>
to add a <classname>FilterSecurityInterceptor</classname> to their filter chain. <para>To secure <classname>FilterInvocation</classname>s, developers need to add a
A typical configuration example is provided below:</para> <classname>FilterSecurityInterceptor</classname> to their filter chain. A typical
configuration example is provided below:</para>
<para>In the application context you will need to configure three <para>In the application context you will need to configure three beans:</para>
beans:</para>
<programlisting> <programlisting>
<![CDATA[ <![CDATA[
<bean id="exceptionTranslationFilter" <bean id="exceptionTranslationFilter"
@ -202,93 +171,69 @@ public void afterPropertiesSet() throws Exception {
<property name="authenticationManager" ref="authenticationManager"/> <property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/> <property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="securityMetadataSource"> <property name="securityMetadataSource">
<security:filter-invocation-definition-source> <security:filter-security-metadata-source>
<security:intercept-url pattern="/secure/super/**" access="ROLE_WE_DONT_HAVE"/> <security:intercept-url pattern="/secure/super/**" access="ROLE_WE_DONT_HAVE"/>
<security:intercept-url pattern="/secure/**" access="ROLE_SUPERVISOR,ROLE_TELLER"/> <security:intercept-url pattern="/secure/**" access="ROLE_SUPERVISOR,ROLE_TELLER"/>
</security:filter-invocation-definition-source> </security:filter-security-metadata-source>
</property> </property>
</bean>]]> </programlisting> </bean>]]> </programlisting>
<para>The <classname>ExceptionTranslationFilter</classname> provides the bridge between Java
<para>The <classname>ExceptionTranslationFilter</classname> provides exceptions and HTTP responses. It is solely concerned with maintaining the user interface.
the bridge between Java exceptions and HTTP responses. It is solely This filter does not do any actual security enforcement. If an
concerned with maintaining the user interface. This filter does not do <exceptionname>AuthenticationException</exceptionname> is detected, the filter will call the
any actual security enforcement. If an AuthenticationEntryPoint to commence the authentication process (e.g. a user login).</para>
<exceptionname>AuthenticationException</exceptionname> is detected, <para>The <interfacename>AuthenticationEntryPoint</interfacename> will be called if the user
the filter will call the AuthenticationEntryPoint to commence the requests a secure HTTP resource but they are not authenticated. The class handles presenting
authentication process (e.g. a user login).</para> the appropriate response to the user so that authentication can begin. Three concrete
implementations are provided with Spring Security:
<para>The <interfacename>AuthenticationEntryPoint</interfacename> will be called <classname>LoginUrlAuthenticationEntryPoint</classname> for commencing a form-based
if the user requests a secure HTTP resource but they are not authentication, <literal>BasicProcessingFilterEntryPoint</literal> for commencing a HTTP Basic
authenticated. The class handles presenting the appropriate response authentication process, and <literal>CasProcessingFilterEntryPoint</literal> for commencing a
to the user so that authentication can begin. Three concrete JA-SIG Central Authentication Service (CAS) login. The
implementations are provided with Spring Security: <classname>LoginUrlAuthenticationEntryPoint</classname> and
<classname>LoginUrlAuthenticationEntryPoint</classname> for <literal>CasProcessingFilterEntryPoint</literal> have optional properties related to forcing
commencing a form-based authentication, the use of HTTPS, so please refer to the JavaDocs if you require this.</para>
<literal>BasicProcessingFilterEntryPoint</literal> for commencing a <para><classname>FilterSecurityInterceptor</classname> is responsible for handling the security
HTTP Basic authentication process, and of HTTP resources. Like any other security interceptor, it requires a reference to an
<literal>CasProcessingFilterEntryPoint</literal> for commencing a <interfacename>AuthenticationManager</interfacename> and an
JA-SIG Central Authentication Service (CAS) login. The <interfacename>AccessDecisionManager</interfacename>, which are both discussed in separate
<classname>LoginUrlAuthenticationEntryPoint</classname> and sections below. The <classname>FilterSecurityInterceptor</classname> is also configured with
<literal>CasProcessingFilterEntryPoint</literal> have optional configuration attributes that apply to different HTTP URL requests. A full discussion of
properties related to forcing the use of HTTPS, so please refer to the configuration attributes is provided in the High Level Design section of this document.</para>
JavaDocs if you require this.</para> <para>The <classname>FilterSecurityInterceptor</classname> can be configured with configuration
attributes in two ways. The first, which is shown above, is using the
<para><classname>FilterSecurityInterceptor</classname> is responsible for <literal>&lt;filter-security-metadata-source&gt;</literal> namespace element. This is
handling the security of HTTP resources. Like any other security similar to the <literal>&lt;filter-chain-map&gt;</literal> used to configure a
interceptor, it requires a reference to an <classname>FilterChainProxy</classname> but the <literal>&lt;intercept-url&gt;</literal>
<interfacename>AuthenticationManager</interfacename> and an child elements only use the <literal>pattern</literal> and <literal>access</literal>
<interfacename>AccessDecisionManager</interfacename>, which are both discussed in attributes. The second is by writing your own
separate sections below. The <interfacename>SecurityMetadataSource</interfacename>, although this is beyond the scope of
<classname>FilterSecurityInterceptor</classname> is also configured with this document. Irrespective of the approach used, the
configuration attributes that apply to different HTTP URL requests. A <interfacename>SecurityMetadataSource</interfacename> is responsible for returning a
full discussion of configuration attributes is provided in the High <literal>List&lt;ConfigAttribute&gt;</literal> containing all of the configuration
Level Design section of this document.</para> attributes associated with a single secure HTTP URL.</para>
<para>The <classname>FilterSecurityInterceptor</classname> can be
configured with configuration attributes in two ways. The first,
which is shown above, is using the <literal>&lt;filter-invocation-definition-source&gt;</literal>
namespace element. This is similar to the <literal>&lt;filter-chain-map&gt;</literal>
used to configure a <classname>FilterChainProxy</classname> but the <literal>&lt;intercept-url&gt;</literal>
child elements only use the <literal>pattern</literal> and <literal>access</literal> attributes.
The second is by writing your own
<interfacename>SecurityMetadataSource</interfacename>, although this is beyond the
scope of this document. Irrespective of the approach used, the
<interfacename>SecurityMetadataSource</interfacename> is responsible for returning
a <literal>List&lt;ConfigAttribute&gt;</literal> containing
all of the configuration attributes associated with a single secure
HTTP URL.</para>
<para>It should be noted that the <para>It should be noted that the
<literal>FilterSecurityInterceptor.setSecurityMetadataSource()</literal> <literal>FilterSecurityInterceptor.setSecurityMetadataSource()</literal> method actually
method actually expects an instance of expects an instance of <interfacename>FilterInvocationDefinitionSource</interfacename>. This
<interfacename>FilterInvocationDefinitionSource</interfacename>. This is a marker is a marker interface which subclasses <interfacename>SecurityMetadataSource</interfacename>.
interface which subclasses <interfacename>SecurityMetadataSource</interfacename>. It simply denotes the <interfacename>SecurityMetadataSource</interfacename> understands
It simply denotes the <interfacename>SecurityMetadataSource</interfacename> <classname>FilterInvocation</classname>s. In the interests of simplicity we'll continue to
understands <classname>FilterInvocation</classname>s. In the interests of refer to the <interfacename>FilterInvocationDefinitionSource</interfacename> as an
simplicity we'll continue to refer to the <interfacename>SecurityMetadataSource</interfacename>, as the distinction is of little
<interfacename>FilterInvocationDefinitionSource</interfacename> as an relevance to most users of the <classname>FilterSecurityInterceptor</classname>.</para>
<interfacename>SecurityMetadataSource</interfacename>, as the distinction is of <para>When using the namespace option to configure the interceptor, commas are used to delimit
little relevance to most users of the the different configuration attributes that apply to each HTTP URL. Each configuration
<classname>FilterSecurityInterceptor</classname>.</para> attribute is assigned into its own <literal>SecurityConfig</literal> object. The
<literal>SecurityConfig</literal> object is discussed in the High Level Design section. The
<para>When using the namespace option to configure the interceptor, <interfacename>SecurityMetadataSource</interfacename> created by the property editor,
commas are used to delimit the different configuration <interfacename>FilterInvocationDefinitionSource</interfacename>, matches configuration
attributes that apply to each HTTP URL. Each configuration attribute attributes against <literal>FilterInvocations</literal> based on expression evaluation of the
is assigned into its own <literal>SecurityConfig</literal> object. The request URL. Two standard expression syntaxes are supported. The default is to treat all
<literal>SecurityConfig</literal> object is discussed in the High expressions as Apache Ant paths and regular expressions are also supported for ore complex
Level Design section. The <interfacename>SecurityMetadataSource</interfacename> cases. The <literal>path-type</literal> attribute is used to specify the type of pattern being
created by the property editor, used. It is not possible to mix expression syntaxes within the same definition. For example,
<interfacename>FilterInvocationDefinitionSource</interfacename>, matches the previous configuration using regular expressions instead of Ant paths would be written as
configuration attributes against <literal>FilterInvocations</literal> follows:</para>
based on expression evaluation of the request URL. Two standard
expression syntaxes are supported. The default is to treat all
expressions as Apache Ant paths and regular expressions are also supported
for ore complex cases. The <literal>path-type</literal> attribute is used
to specify the type of pattern being used. It is not possible to
mix expression syntaxes within the same definition. For example, the
previous configuration using regular expressions instead of Ant paths would be
written as follows:</para>
<programlisting><![CDATA[ <programlisting><![CDATA[
<bean id="filterInvocationInterceptor" <bean id="filterInvocationInterceptor"
class="org.springframework.security.intercept.web.FilterSecurityInterceptor"> class="org.springframework.security.intercept.web.FilterSecurityInterceptor">
@ -296,32 +241,25 @@ public void afterPropertiesSet() throws Exception {
<property name="accessDecisionManager" ref="accessDecisionManager"/> <property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="runAsManager" ref="runAsManager"/> <property name="runAsManager" ref="runAsManager"/>
<property name="securityMetadataSource"> <property name="securityMetadataSource">
<security:filter-invocation-definition-source path-type="regex"> <security:filter-security-metadata-source path-type="regex">
<security:intercept-url pattern="\A/secure/super/.*\Z" access="ROLE_WE_DONT_HAVE"/> <security:intercept-url pattern="\A/secure/super/.*\Z" access="ROLE_WE_DONT_HAVE"/>
<security:intercept-url pattern="\A/secure/.*\" access="ROLE_SUPERVISOR,ROLE_TELLER"/> <security:intercept-url pattern="\A/secure/.*\" access="ROLE_SUPERVISOR,ROLE_TELLER"/>
</security:filter-invocation-definition-source> </security:filter-security-metadata-source>
</property> </property>
</bean>]]> </programlisting> </bean>]]> </programlisting>
<para>Irrespective of the type of expression syntax used, expressions are always evaluated in
<para>Irrespective of the type of expression syntax used, expressions the order they are defined. Thus it is important that more specific expressions are defined
are always evaluated in the order they are defined. Thus it is higher in the list than less specific expressions. This is reflected in our example above,
important that more specific expressions are defined higher in the where the more specific <literal>/secure/super/</literal> pattern appears higher than the less
list than less specific expressions. This is reflected in our example specific <literal>/secure/</literal> pattern. If they were reversed, the
above, where the more specific <literal>/secure/super/</literal> <literal>/secure/</literal> pattern would always match and the
pattern appears higher than the less specific <literal>/secure/super/</literal> pattern would never be evaluated.</para>
<literal>/secure/</literal> pattern. If they were reversed, the <para>As with other security interceptors, the <literal>validateConfigAttributes</literal>
<literal>/secure/</literal> pattern would always match and the property is observed. When set to <literal>true</literal> (the default), at startup time the
<literal>/secure/super/</literal> pattern would never be <classname>FilterSecurityInterceptor</classname> will evaluate if the provided configuration
evaluated.</para> attributes are valid. It does this by checking each configuration attribute can be processed
by either the <interfacename>AccessDecisionManager</interfacename> or the
<para>As with other security interceptors, the <literal>RunAsManager</literal>. If neither of these can process a given configuration
<literal>validateConfigAttributes</literal> property is observed. When attribute, an exception is thrown.</para>
set to <literal>true</literal> (the default), at startup time the
<classname>FilterSecurityInterceptor</classname> will evaluate if the
provided configuration attributes are valid. It does this by checking
each configuration attribute can be processed by either the
<interfacename>AccessDecisionManager</interfacename> or the
<literal>RunAsManager</literal>. If neither of these can process a
given configuration attribute, an exception is thrown.</para>
</section> </section>
</chapter> </chapter>

View File

@ -92,11 +92,11 @@
<property name="authenticationManager" ref="authenticationManager"/> <property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/> <property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="securityMetadataSource"> <property name="securityMetadataSource">
<sec:filter-invocation-definition-source> <sec:filter-security-metadata-source>
<sec:intercept-url pattern="/secure/extreme/**" access="ROLE_2"/> <sec:intercept-url pattern="/secure/extreme/**" access="ROLE_2"/>
<sec:intercept-url pattern="/secure/**" access="ROLE_1"/> <sec:intercept-url pattern="/secure/**" access="ROLE_1"/>
<sec:intercept-url pattern="/**" access="ROLE_0"/> <sec:intercept-url pattern="/**" access="ROLE_0"/>
</sec:filter-invocation-definition-source> </sec:filter-security-metadata-source>
</property> </property>
</bean> </bean>

View File

@ -51,15 +51,15 @@ public class ChannelProcessingFilter extends SpringSecurityFilter implements Ini
//~ Instance fields ================================================================================================ //~ Instance fields ================================================================================================
private ChannelDecisionManager channelDecisionManager; private ChannelDecisionManager channelDecisionManager;
private FilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource; private FilterInvocationSecurityMetadataSource securityMetadataSource;
//~ Methods ======================================================================================================== //~ Methods ========================================================================================================
public void afterPropertiesSet() throws Exception { public void afterPropertiesSet() throws Exception {
Assert.notNull(filterInvocationSecurityMetadataSource, "filterInvocationSecurityMetadataSource must be specified"); Assert.notNull(securityMetadataSource, "securityMetadataSource must be specified");
Assert.notNull(channelDecisionManager, "channelDecisionManager must be specified"); Assert.notNull(channelDecisionManager, "channelDecisionManager must be specified");
Collection<ConfigAttribute> attrDefs = this.filterInvocationSecurityMetadataSource.getAllConfigAttributes(); Collection<ConfigAttribute> attrDefs = this.securityMetadataSource.getAllConfigAttributes();
if (attrDefs == null) { if (attrDefs == null) {
if (logger.isWarnEnabled()) { if (logger.isWarnEnabled()) {
@ -91,7 +91,7 @@ public class ChannelProcessingFilter extends SpringSecurityFilter implements Ini
throws IOException, ServletException { throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(request, response, chain); FilterInvocation fi = new FilterInvocation(request, response, chain);
List<ConfigAttribute> attr = this.filterInvocationSecurityMetadataSource.getAttributes(fi); List<ConfigAttribute> attr = this.securityMetadataSource.getAttributes(fi);
if (attr != null) { if (attr != null) {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
@ -112,16 +112,16 @@ public class ChannelProcessingFilter extends SpringSecurityFilter implements Ini
return channelDecisionManager; return channelDecisionManager;
} }
public FilterInvocationSecurityMetadataSource getFilterInvocationSecurityMetadataSource() { public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
return filterInvocationSecurityMetadataSource; return securityMetadataSource;
} }
public void setChannelDecisionManager(ChannelDecisionManager channelDecisionManager) { public void setChannelDecisionManager(ChannelDecisionManager channelDecisionManager) {
this.channelDecisionManager = channelDecisionManager; this.channelDecisionManager = channelDecisionManager;
} }
public void setFilterInvocationSecurityMetadataSource(FilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource) { public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource) {
this.filterInvocationSecurityMetadataSource = filterInvocationSecurityMetadataSource; this.securityMetadataSource = filterInvocationSecurityMetadataSource;
} }
public int getOrder() { public int getOrder() {

View File

@ -15,7 +15,7 @@
package org.springframework.security.web.access.channel; package org.springframework.security.web.access.channel;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.*;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import java.io.IOException; import java.io.IOException;
@ -31,8 +31,6 @@ import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.access.ConfigAttribute; import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig; import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation; import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.channel.ChannelDecisionManager;
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource; import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
@ -50,7 +48,7 @@ public class ChannelProcessingFilterTests {
ChannelProcessingFilter filter = new ChannelProcessingFilter(); ChannelProcessingFilter filter = new ChannelProcessingFilter();
MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path", true, "MOCK"); MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path", true, "MOCK");
filter.setFilterInvocationSecurityMetadataSource(fids); filter.setSecurityMetadataSource(fids);
filter.afterPropertiesSet(); filter.afterPropertiesSet();
} }
@ -69,7 +67,7 @@ public class ChannelProcessingFilterTests {
MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path", true, "SUPPORTS_MOCK_ONLY"); MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path", true, "SUPPORTS_MOCK_ONLY");
filter.setFilterInvocationSecurityMetadataSource(fids); filter.setSecurityMetadataSource(fids);
filter.afterPropertiesSet(); filter.afterPropertiesSet();
} }
@ -81,7 +79,7 @@ public class ChannelProcessingFilterTests {
MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path", true, "SUPPORTS_MOCK_ONLY", "INVALID_ATTRIBUTE"); MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path", true, "SUPPORTS_MOCK_ONLY", "INVALID_ATTRIBUTE");
filter.setFilterInvocationSecurityMetadataSource(fids); filter.setSecurityMetadataSource(fids);
filter.afterPropertiesSet(); filter.afterPropertiesSet();
} }
@ -92,7 +90,7 @@ public class ChannelProcessingFilterTests {
MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path", true, "SOME_ATTRIBUTE"); MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path", true, "SOME_ATTRIBUTE");
filter.setFilterInvocationSecurityMetadataSource(fids); filter.setSecurityMetadataSource(fids);
MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletRequest request = new MockHttpServletRequest();
request.setQueryString("info=now"); request.setQueryString("info=now");
@ -110,7 +108,7 @@ public class ChannelProcessingFilterTests {
MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path", true, "SOME_ATTRIBUTE"); MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path", true, "SOME_ATTRIBUTE");
filter.setFilterInvocationSecurityMetadataSource(fids); filter.setSecurityMetadataSource(fids);
MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletRequest request = new MockHttpServletRequest();
request.setQueryString("info=now"); request.setQueryString("info=now");
@ -129,7 +127,7 @@ public class ChannelProcessingFilterTests {
MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path", true, "NOT_USED"); MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path", true, "NOT_USED");
filter.setFilterInvocationSecurityMetadataSource(fids); filter.setSecurityMetadataSource(fids);
MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletRequest request = new MockHttpServletRequest();
request.setQueryString("info=now"); request.setQueryString("info=now");
@ -148,8 +146,8 @@ public class ChannelProcessingFilterTests {
MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path", false, "MOCK"); MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path", false, "MOCK");
filter.setFilterInvocationSecurityMetadataSource(fids); filter.setSecurityMetadataSource(fids);
assertTrue(filter.getFilterInvocationSecurityMetadataSource() != null); assertSame(fids, filter.getSecurityMetadataSource());
filter.init(null); filter.init(null);
filter.afterPropertiesSet(); filter.afterPropertiesSet();