Sunday, December 2, 2012

Spring MVC: implementing authentication and authorization using Spring security

In this post, we'll go through the few steps that will allow you to implement both authentication and authorization security features in a Spring MVC application.

We'll use the Spring MVC demo application we've developed in a previous post as starting point. Feel free to take a look at it   first (here's the post), if you want to follow the steps below one at a time.

You'd rather get down to business right away? Here are the sources of this application (pick the master branch).

Tools and libraries
  • Eclipse Indigo
  • Spring-core 3.1.1
  • Spring-security 3.1.0
  • Tomcat 7
  • Maven 3

STEP 1 - Adding necessary Maven dependencies

In order to use Spring Security, we'll need to add the following dependencies in our pom.xml configuration:
  • spring-security-taglibs
  • spring-security-config
  • standard
  • jstl
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>Spring-MVC-Tutorial</groupId>
 <artifactId>Spring-MVC-Tutorial</artifactId>
 <packaging>war</packaging>
 <version>0.0.1-SNAPSHOT</version>

 <properties>
  <spring-version>3.1.1.RELEASE</spring-version>
  <spring-security-version>3.1.0.RELEASE</spring-security-version>
 </properties>

 <build>
  <plugins>
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.2</version>
    <configuration>
     <!-- specifiy which directory within the project hierarchy will be considered 
      as the root directory from the generated war file -->
     <warSourceDirectory>WebContent</warSourceDirectory>
    </configuration>
   </plugin>
  </plugins>
 </build>

 <dependencies>
  <!-- Spring Core -->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-core</artifactId>
   <version>${spring-version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context</artifactId>
   <version>${spring-version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>${spring-version}</version>
  </dependency>
  <!-- Spring security -->
  <dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-taglibs</artifactId>
   <version>${spring-security-version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-config</artifactId>
   <version>${spring-security-version}</version>
  </dependency>
  <!-- standard.jar -->
  <dependency>
   <groupId>taglibs</groupId>
   <artifactId>standard</artifactId>
   <version>1.1.2</version>
  </dependency>
  <!-- JSTL -->
  <dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>jstl</artifactId>
   <version>1.2</version>
  </dependency>
 </dependencies>
</project>

STEP 2 - Enabling Spring Security in the web application configuration

  • Once we add Spring Security features, unlike basic Spring MVC applications (in which, by default, the DispatcherServlet initialization will look for a file named [servlet-name]-servlet.xml in the WEB-INF directory), we'll need to explicitly define a ContextLoaderListener listener as well as a context parameter named contextConfigLocation whose value is the relative path to your Spring configuration file, in the web.xml file.
Here's the added part:

 <listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 </listener>

 <context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>
      /WEB-INF/MyDispatcherServlet-servlet.xml
  </param-value>
 </context-param>


  • Then, yet in the web.xml file, we'll add Spring security filter definition so that requests that are sent to our dispatcher servlet will first be intercepted by this security filter so that authentication/authorization can be checked against the request content, origin, and so on. At the same time, we'll define an URL mapping to this security filter to specify which requests we want to have the security filter intercept and process.
Here's the added part:
 
  springSecurityFilterChain
  org.springframework.web.filter.DelegatingFilterProxy
 

 
  springSecurityFilterChain
  /*
 
We are now done with the web.xml file and it looks like this:


 Spring MVC tutorial

 
  org.springframework.web.context.ContextLoaderListener
 

 
  contextConfigLocation
  
      /WEB-INF/MyDispatcherServlet-servlet.xml
  
 

 
  MyDispatcherServlet
  org.springframework.web.servlet.DispatcherServlet
 

 
  MyDispatcherServlet
  *.go
 

 
  springSecurityFilterChain
  org.springframework.web.filter.DelegatingFilterProxy
 

 
  springSecurityFilterChain
  /*
 

STEP 3 - Configuring Spring Security


In this step, we'll edit our Spring configuration file (that we've defined as being the MyDispatcherServlet-servlet.xml file back at the step 2), and configure the security features we want to use, that is, we'll configure authentication through a login page, and authentication through access right that the user must have in order to access a certain page.
Starting from the MyDispatcherServlet-servlet.xml file we created in the basic Spring MVC application tutorial, here are the added parts:

 <security:http auto-config="true">
  <security:intercept-url pattern="/home.go" access="ROLE_REGISTERED_USER"/> 
  <security:logout logout-success-url="/home.go"/>
 </security:http>
 
 <security:authentication-manager>
  <security:authentication-provider>  
   <security:user-service>
    <security:user name="user1" password="demo" authorities="ROLE_REGISTERED_USER" />
    <security:user name="user2" password="demo" authorities="ROLE_FREE_USER" />
   </security:user-service>
  </security:authentication-provider>
 </security:authentication-manager>

  • By defining the auto-config attribute value to true in the <security:http> element, we are using the default security features that are provided by Spring Security libraries, that is, it provides us, among others, a default login page and access rights checking
  • In the <security:intercept-url> element, we specify the URLs whose access should be restricted to users with the role(s) that we specify in the access attribute. In this example, we are basically saying that a user should be authenticated (he must log in through the default login page) and hold the ROLE_REGISTERED_USER role in order to access the "home.go" page.
  • With the <security:authentication-manager> element, we can specify how (directly querying a DB, providing a custom authentication/authorization service, through LDAP, ...)
    • username and password are retrieved to perform authentication checking
    • roles are retrieved to perform authorization checking
    In this example, we simply hard coded the usernames, their associated password and roles directly in the Spring configuration file.
Here's the whole Spring configuration file:


 
 
 

 
 

 
  
   
   
 
 
 
   
  
 
 
 
    
   
    
    
   
  
 


STEP 4 - Testing
Remark: in order to make the resulting application easier to test, we'll add a logout button to the page to be displayed, as follows:


<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
 pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>




Spring-MVC-Tutorial


 Welcome my Spring-MVC-Tutorial homepage
 "> Logout


Case 1 - Unauthenticated user

Once the application is deployed, try to access the home.go page and you'll be redirected to the default login page.



Case 2 - Wrong credentials

Try to login with wrong credentials: you'll be redirected to the login page but take a closer look to the address bar. See the  "spring_security_login?login_error"? This is the default behavior of Spring security when relying on its generated login page, to redirect the user to the login page and the login_error parameter is used to detect that the previous login attempt failed, so an error message must be displayed.



Case 3 - Authenticated and authorized user


On the login page, log in using the following credentials user1/demo. As the "user1" user has the ROLE_REGISTERED_USER role that is required to access the home.go page (remember the roles we've defined at step 3?), you'll be successfully directed to the required home.go page.


Case 4 - Authenticated but unauthorized user

From the previous page, click on the logout link to get back to the login page, then log in with the credentials user2/demo. As user2 only has the ROLE_FREE_USER role, we'll get an HTTP 403 ERROR page indicating that the user is not authorized to access the required page.


Sources

The source code of this example project is accessible here.

No comments:

Post a Comment