Security is of great concern in any web application. If you are looking for a proven and industry standard solution to secure your Java/J2ee based application, then widely used and highly customizable authentication and access control framework - Spring Security is well worth considering.
This post will show all the steps to setup, configure and integrate Spring Security 3 to protect your web application from being breached using a simple hello world example.
Tools and Technologies used in this article
Note : Spring 3 requires at least JDK 5. So, make sure you have JDK 5 or above.
1. Initial Spring 3 MVC Web Application
We'll start with creating (Refer Spring 3 MVC Framework Based Hello World Web Application Example) a simple Spring 3 MVC project (say SpringSecurityHelloWorld). There will be two pages (say public.jsp and mypage.jsp), one controller (SpringSecurityHelloController) with two handler methods and Spring Configuration File (dispatcher-servlet.xml).
File: WEB-INF/pages/public.jsp
<html>
<title>Public Page</title>
<body>
<h4>${message}</h4>
</body>
</html>
File: WEB-INF/pages/secured/mypage.jsp
<html>
<title>My Secured Page</title>
<body>
<h2>Hello World!</h2>
<h4>${message}</h4>
</body>
</html>
File: com/srccodes/spring/controller/SpringSecurityHelloController.java
package com.srccodes.spring.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author Abhijit Ghosh
* @version 1.0
*/
@Controller
public class SpringSecurityHelloController {
@RequestMapping("/public")
public String accessPublicPage(Model model) {
model.addAttribute("message", "This page is publicly accessible. No authentication is required to view.");
return "public";
}
@RequestMapping("/secured/mypage")
public String accessSecuredPage(Model model) {
model.addAttribute("message", "Only you are authenticated and authorized to view this page.");
return "/secured/mypage";
}
}
File: WEB-INF/dispatcher-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
<context:component-scan base-package="com.srccodes.spring.controller" />
<mvc:annotation-driven />
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
Find below the screenshot of the project structure of our initial Spring 3 MVC Web Application
So far there is no security and anybody can access both public and secured pages without login. We'll integrate Spring Security with our initial web application so that page 'public.jsp' remains publicly accessible but to access the secured page 'mypage.jsp', user needs to login.
2. Add Spring Security Maven Dependencies
Add Spring Security Maven Dependencies in Maven pom.xml.
File: pom.xml
<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>com.srccodes.spring</groupId>
<artifactId>SpringSecurityHelloWorld</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>SpringSecurityHelloWorld</name>
<url>http://maven.apache.org</url>
<properties>
<org.springframework.version>3.1.4.RELEASE</org.springframework.version>
<spring-security.version>3.1.4.RELEASE</spring-security.version>
</properties>
<dependencies>
<!-- Spring MVC depends on spring-core, spring-beans, spring-context, spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<!-- Spring Security Dependencies -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${spring-security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring-security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring-security.version}</version>
</dependency>
</dependencies>
<build>
<finalName>SpringSecurityHelloWorld</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
Note: If you use Spring Framework 3.2.x and Spring Security 3.1.x (or less), then you may encounter Spring Asm Dependency Issue: java.lang.IncompatibleClassChangeError.
3. Spring Security configuration
Create a separate spring security xml and add following configuration to enable Spring security.
File: WEB-INF/spring-security.xml
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<http auto-config='true'>
<intercept-url pattern="/secured/*" access="ROLE_USER" />
</http>
<authentication-manager>
<authentication-provider>
<user-service>
<user name="srccodes" password="password" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
</beans:beans>
Note:
4. Integration of Spring Security
Spring Security is entirely based on servlet filter. We need to declare a filter called DelegatingFilterProxy in web.xml.
File: WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- Spring context files to be loaded -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/dispatcher-servlet.xml,
/WEB-INF/spring-security.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- filter declaration for Spring Security -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
DelegatingFilterProxy is actually a filter proxy which delegates filter's methods to a Spring managed bean (by default named as "springSecurityFilterChain") which implements javax.servlet.Filter. Name of this bean must same with the
Server Console
Jul 7, 2013 9:53:14 PM org.springframework.security.web.DefaultSecurityFilterChain <init>
INFO: Creating filter chain: org.springframework.security.web.util.AnyRequestMatcher@1, [org.springframework.security.web.context.SecurityContextPersistenceFilter@34cf935a, org.springframework.security.web.authentication.logout.LogoutFilter@27d314cc, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@12fa7181, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@7831d5e2, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@fd12614, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@6c1af328, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@5d51fe8a, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@321e45e3, org.springframework.security.web.session.SessionManagementFilter@617e53c9, org.springframework.security.web.access.ExceptionTranslationFilter@2caee320, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@dc160cb]
Jul 7, 2013 9:53:14 PM org.springframework.security.config.http.DefaultFilterChainValidator checkLoginPageIsntProtected
INFO: Checking whether login URL '/spring_security_login' is accessible with your configuration
Jul 7, 2013 9:53:14 PM org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization completed in 13612 ms
Jul 7, 2013 9:53:14 PM org.apache.catalina.core.StandardContext filterStart
SEVERE: Exception starting filter XXXXX
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'XXXXX' is defined
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:549)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1095)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:277)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1109)
at org.springframework.web.filter.DelegatingFilterProxy.initDelegate(DelegatingFilterProxy.java:326)
at org.springframework.web.filter.DelegatingFilterProxy.initFilterBean(DelegatingFilterProxy.java:236)
at org.springframework.web.filter.GenericFilterBean.init(GenericFilterBean.java:194)
at org.apache.catalina.core.ApplicationFilterConfig.initFilter(ApplicationFilterConfig.java:281)
Note : In web.xml, filter init-param "targetBeanName" can be used to specify the name of the target Spring bean defined in the application context.
5. Overall Project Structure
6. Demo
Now we'll test what we have achieved so far. Start the server and deploy the web application. Open the url http://
So far so good. Now we'll try to open our secured / protected page (mypage.jsp) at http://
Note: If user defined login form is not supplied, then Spring will automatically create one for authentication.
Supposedly we should not be able to view the secured page without valid username and password. So, let's first try with wrong credentials.
Oops!!!. Spring security has caught us again. It is also showing error message "secured page without valid username and password." with "Reason: Bad credentials". Now we have no option left but to try with correct username (srccodes) and password (password). But this time Spring security will redirect us to the initially requested URL and we'll be able to view the content of the secured page.
Download SrcCodes
All code samples shown in this post are available on GitHub.
Comments