Spring Security part IV : ExceptionTranslationFilter & FilterSecurityInterceptor

In this post we’ll examine in depth the ExceptionTranslationFilter and FilterSecurityInterceptor filters

VII ExceptionTranslationFilter

<!-- Filter to redirect to login page -->
<bean id="exceptionTranslationFilter" class="org.springframework.security.web.access.ExceptionTranslationFilter">
        <property name="authenticationEntryPoint" ref="authenticationEntryPoint"/>
        <property name="accessDeniedHandler" ref="accessDeniedHandler"/>
</bean>
    
<bean id="authenticationEntryPoint"  class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
	  <property name="loginFormUrl" value="/pages/Security/login.html"/>
	  <property name="forceHttps" value="false"/>
</bean>
    
<bean id="accessDeniedHandler" class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
	<property name="errorPage" value="/pages/Security/accessDenied.html"/>
</bean>

1) ExceptionTranslationFilter

The ExceptionTranslationFilter purpose is to redirect the user to the login page if he’s not yet authenticated and to a default error page if he tries to access an unauthorized resources.

The authenticationEntryPoint defines a loginFormUrl pointing to the default login page. Optionally you can force usage of HTTPS by setting forceHttps to true.

Your login page should resemble:

<form action="myApplication/j_myApplication_security_check" method="post">
	<fieldset>
		<legend>Login form</legend>
		<label for="j_username">Login</label>
		<input type="text" id="j_username" size="20"/>

		<label for="j_password">Password</label>
		<input type="password" id="j_password" size="20"/>
			
		<button type="submit">Submit</button>
		<button type="reset">Reset</button>
	</fieldset>		
</form>

Remember about the filterProcessesUrl parameter we mentionned in the previous post ? It is used here as default action for the login form.

For Spring Security to retrieve the login & password values in the HTTP request attributes, we should set their ids to “j_username” & “j_password” respectively. This is the default convention.

Again it is possible to change these defaults by setting the “usernameParameter” & “passwordParameter” for the authenticationProcessingFilter.

<bean id="authenticationProcessingFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
	<property name="authenticationManager" ref="authenticationManager"/>
	<property name="usernameParameter" value="j_myUserNameId"/>
	<property name="passwordParameter" value="j_myPasswordId"/>
	<property name="filterProcessesUrl" value="/j_myApplication_security_check"/>
	...
</bean>

 

2) AccessDeniedHandler

This handler simply forwards the user to the error page defined by the “errorPage” parameter if he is not authorized to access the requested resources, quite straightforward.

 

VIII FilterSecurityInterceptor

The core of access management is done by this filter.
First, the namespace for Spring security should be set in order to use the shorthand “sec“.

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 	xmlns:sec="http://www.springframework.org/schema/security"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd">

1) <sec:filter-security-metadata-source>

&lt;!-- Filter for role checking --&gt;
&lt;bean id=&quot;filterSecurityInterceptor&quot; class=&quot;org.springframework.security.web.access.intercept.FilterSecurityInterceptor&quot;&gt;
	&lt;property name=&quot;authenticationManager&quot; ref=&quot;authenticationManager&quot;/&gt;
	&lt;property name=&quot;accessDecisionManager&quot; ref=&quot;httpRequestAccessDecisionManager&quot;/&gt;
	&lt;property name=&quot;securityMetadataSource&quot;&gt;
		&lt;sec:filter-security-metadata-source lowercase-comparisons=&quot;true&quot; request-matcher=&quot;ant&quot; use-expressions=&quot;true&quot;&gt;
			&lt;sec:intercept-url pattern=&quot;/pages/Security/**&quot; access=&quot;permitAll&quot;/&gt;
			&lt;sec:intercept-url pattern=&quot;/resources/**&quot; access=&quot;permitAll&quot;/&gt;
			&lt;sec:intercept-url pattern=&quot;/pages/Settings/**&quot; access=&quot;hasRole('SETTINGS')&quot;/&gt;
			&lt;sec:intercept-url pattern=&quot;/pages/Home/*&quot; access=&quot;hasRole('HOME')&quot;/&gt;              
			&lt;sec:intercept-url pattern=&quot;/pages/Admin/**&quot; access=&quot;hasRole('ADMINISTRATOR')&quot;/&gt;
			&lt;sec:intercept-url pattern=&quot;/servlet/Download&quot; access=&quot;hasAnyRole('DOWNLOAD','PREMIUM_ACCOUNT')&quot;/&gt;
                
			&lt;sec:intercept-url pattern=&quot;/**&quot; access=&quot;isAuthenticated()&quot;/&gt;
		&lt;/sec:filter-security-metadata-source&gt;
	&lt;/property&gt;
&lt;/bean&gt;

Lots of interesting points to mention here.

We define inside the <filter-security-metadata-source> tag a list of resource URLs and the corresponding roles to access them. This tag exposes the following attributes:

  • lowercase-comparisons : if true Spring will compare paths after forcing to lowercase
  • request-matcher : defines the pattern matching style for incoming URLs. Possible values are ant, regexp & ciRegex
  • use-expressions: if true, Spring will enable the use of expressions (like hasRole()) in the ‘access‘ attributes in <intercept-url> elements

At line 8, we allow un-authenticated access to theme resources (css, images, javascripts) by setting access=”permitAll”. Similarly, all pages in “/pages/Security” folder, including login form, are granted the permitAll access (otherwise the user can never reach the login form).

At line 9, accessing all pages in “/pages/Settings” subfolder requires the role SETTINGS. The role definition relies on the hasRole(String) expression. A comprehensive list of all available expressions for access control is documented here Expression-Based Access Control

At line 12 we have an example of access control base on a list of roles using the hasAnyRole() expression.

Finally, at line 14, we restrict the access to all of our application resources (path = “/**”) by requiring that the user should be authenticated (not anonymous).

There is an important gotcha with the above access control definition. Spring will apply access control rules in their declaration order. Consequently it is important to declare first all un-secured resources and finish with the most restricted resources.

For example, if we had declared <sec:intercept-url pattern=”/**” access=”isAuthenticated()”/> before <sec:intercept-url pattern=”/pages/Security/**” access=”permitAll”/>, the application will never be accessible since on entering the login form, the user is rejected because not authenticated.

 

2) AccessDecisionManager

Above we just define access control rules for incoming URLs. Still we need a manager to enforce these rules and there comes the AccessDecisionManager into play.

&lt;bean id=&quot;httpRequestAccessDecisionManager&quot; 
	class=&quot;org.springframework.security.access.vote.AffirmativeBased&quot;&gt;
	&lt;property name=&quot;allowIfAllAbstainDecisions&quot; value=&quot;false&quot;/&gt;
	&lt;property name=&quot;decisionVoters&quot;&gt;
		&lt;list&gt;
			&lt;ref bean=&quot;webExpressionVoter&quot;/&gt;
			&lt;ref bean=&quot;authenticatedVoter&quot;/&gt;
		&lt;/list&gt;
	&lt;/property&gt;
&lt;/bean&gt;
    
&lt;bean id=&quot;webExpressionVoter&quot; 
	class=&quot;org.springframework.security.web.access.expression.WebExpressionVoter&quot;/&gt;
&lt;bean id=&quot;authenticatedVoter&quot; 
	class=&quot;org.springframework.security.access.vote.AuthenticatedVoter&quot; /&gt;

First we use the org.springframework.security.access.vote.AffirmativeBased provided by Spring to control access rules. This default implementation of the AccessDecisionManager interface simply grants access if any injected decisionVoters returns an affirmative response.

Two other implementations of the AccessDecisionManager interface are available:

  • ConsensusBased: access is granted if a majority of decision voters return an affirmative response
  • UnanimousBased: access is granted only if all decision voters return an affirmative response

The allowIfAllAbstainDecisions property on the is set to false so that at least one affirmative response is required from decision voters to grant access.

Next we inject two decision voters into this AccessDecisionManager:

  • WebExpressionVoter: grant access using Expression-Based Access Control as mentioned above. This voter is mandatory if we have specified use-expressions = true for the <filter-security-metadata-source>
  • AuthenticatedVoter: default voter. This voter will give affirmative response if the user is authenticated, authenticated with remember-me option or is authenticated anonymously

There are some other implementations of the AccessDecisionVoter interface provided by Spring, we just mention them for information:

  • Jsr250Voter: voter compatible with the JSR 250 security annotations
    • @RunAs
    • @RolesAllowed
    • @PermitAll
    • @DenyAll
    • @DeclareRoles
  • RoleVoter: the equivalent to WebExpressionsVoter but using user roles to take decisions. The access is granted if the user role label starts with “ROLE_“.This default prefix can be changed by overriding the rolePrefix property.
  • AclEntryVoter: voter using an ACL list

5 Comments

  1. Marcelo Módolo

    Hi!

    I made step by step config but I’m with this error:

    22:45:05,388 ERROR [org.springframework.web.context.ContextLoader] (MSC service thread 1-4) Context initialization failed: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named ‘org.springframework.security.filterChainProxy’ is defined

    Can you send the web.xml and secury-context.xml?

    Thanks,
    Marcelo Módolo

    Reply
    1. DuyHai DOAN

      Hello Marcelo

      The error message you got is quite explicit. Basically Spring is complaining because it cannot find the bean named “filterChainProxy” in the context.

      If you get back to my first post on Spring security configuration Configuration & Security Chain, I said that the filter name (filterChainProxy) should point to an existing bean in your Spring context.

      Did you have the following bean in your context ?

      <bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
      	<sec:filter-chain-map path-type="ant">
      	    <sec:filter-chain pattern="/webServices/**" filters="
      	           securityContextPersistenceFilterForWebServices,
      	           WSAuthenticationFilter,
      	           exceptionTranslationFilter,
      	           filterSecurityInterceptor" />
      	    <sec:filter-chain pattern="/**"  filters="
      	           securityContextPersistentFilter,
      	           logoutFilter,
      	           authenticationProcessingFilter,
      	           anonymousFilter,
      	           exceptionTranslationFilter,
      	           filterSecurityInterceptor" />
        	</sec:filter-chain-map>
      </bean>
      

      Please notice the <bean id=”filterChainProxy” class=”org.springframework.security.web.FilterChainProxy”> line.

      Reply
      1. javap

        Configuration

        Reply
  2. javap

    My Configuration is as follows

    After I added WebExpressionVoter I am getting the following error
    java.lang.IllegalArgumentException: AccessDecisionManager does not support secure object class: interface org.aopalliance.intercept.MethodInvocation

    Any input is much appreciated
    Thanks

    Reply
    1. DuyHai DOAN

      @javap

      Sorry, it seems that your configuration has been stripped out by WordPress. To paste source code, you should use the following tags:

      [ s o u r c e c o d e language=”xxx”]

      // your source code here

      [/ s o u r c e c o d e]

      Replace “s o u r c e c o d e” by “sourcecode”. I had to add spaces so WordPress does not interpret it as source code tag and display it properly

      for language, you can put “java” or “xml”

      Reply

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.