18 May 2015

Introduction

Spring security is a Java/J2EE framework that provides authentication and authorization for enterprise applications. Objectives of this project are

  • authenticate and authorize all incoming requests (Hard coded user name/password is root/root)
  • navigate to welcome jsp via spring security login page
  • on successful login, display welcome page with a link
  • on clicking the URL, you will get navigated to another page.
  • create customize login page (by default spring security framework provides login page)

Required Software

  • Eclipse for J2EE
  • JDK 1.7
  • Maven 2.2.x or above
  • Tomcat 7

Steps to write code

The content of pom.xml, web.xml, security xml, jsp and controller classes are given below. Go through the in-line code comments for better understanding.

  • 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 http://maven.apache.org/xsd/maven-v4_0_0.xsd">
  	<modelVersion>4.0.0</modelVersion>
	<groupId>com.ashish.rest.controller</groupId>
	<artifactId>SpringSecurity</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	
	<properties>
		<spring.version>4.0.1.RELEASE</spring.version>
		<spring.security.version>3.2.0.RELEASE</spring.security.version>
	</properties>
	
	<repositories>
		<repository>
			<id>maven2-repository.java.net</id>
			<name>Java.net Repository for Maven</name>
			<url>http://download.java.net/maven/2/</url>
			<layout>default</layout>
		</repository>
	</repositories>
	<build>
		<sourceDirectory>src</sourceDirectory>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.1</version>
				<configuration>
					<source>1.7</source>
					<target>1.7</target>
				</configuration>
			</plugin>
			<plugin>
				<artifactId>maven-war-plugin</artifactId>
				<version>2.4</version>
				<configuration>
					<warSourceDirectory>WebContent</warSourceDirectory>
					<failOnMissingWebXml>false</failOnMissingWebXml>
				</configuration>
			</plugin>
		</plugins>
	</build>
	<dependencies>
		<dependency>
		    <groupId>jstl</groupId>
		    <artifactId>jstl</artifactId>
		    <version>1.2</version>
		</dependency>
		
		<!-- Spring dependencies -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${spring.version}</version>
		</dependency>
 
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</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-core</artifactId>
		    <version>${spring.security.version}</version>
		    <type>jar</type>
		    <scope>compile</scope>
		</dependency>
		<dependency>
		    <groupId>org.springframework.security</groupId>
		    <artifactId>spring-security-web</artifactId>
		    <version>${spring.security.version}</version>
		    <type>jar</type>
		    <scope>compile</scope>
		</dependency>
		<dependency>
		    <groupId>org.springframework.security</groupId>
		    <artifactId>spring-security-config</artifactId>
		    <version>${spring.security.version}</version>
		    <type>jar</type>
		    <scope>compile</scope>
		</dependency>
	</dependencies>

</project>
  • web.xml


<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	id="WebApp_ID" version="3.0">
	<display-name>Restful WebApplication</display-name>
	<welcome-file-list>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>

	<!-- Add the following entry for Spring MVC -->
	<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>/springmvc/*</url-pattern>
	</servlet-mapping>
 
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
			<!-- URL mapping is here -->
			/WEB-INF/dispatcher-servlet.xml
			<!-- Spring security configuration is here -->
			/WEB-INF/security-applicationContext.xml
		</param-value>
	</context-param>
 
	<listener>
		<listener-class>
			org.springframework.web.context.ContextLoaderListener
		</listener-class>
	</listener>
	<!-- Security Filter -->
	<!-- Any request for RESTful service or SpringMVC service has to pass through spring security filter -->
	<filter>
	    <filter-name>springSecurityFilterChain</filter-name>
	    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
	    <init-param>
	        <param-name>contextAttribute</param-name>
	        <param-value>org.springframework.web.context.WebApplicationContext.ROOT</param-value>
	    </init-param>
	 </filter>
	 <filter-mapping>
	     <filter-name>springSecurityFilterChain</filter-name>
	     <url-pattern>/*</url-pattern>
	 </filter-mapping>
</web-app>
  • dispatcher-servlet.xml file has spring MVC configuration

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	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.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
 
	<context:component-scan base-package="com.ashish.springmvc.controller" />
 
	<bean
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix">
			<value>/WEB-INF/views/</value>
		</property>
		<property name="suffix">
			<value>.jsp</value>
		</property>
	</bean>
</beans>
  • security-applicationContext.xml file has spring security configuration. Go through the inline comments

<?xml version="1.0" encoding="UTF-8"?>
<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"
    xmlns:security="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.2.xsd">
 
 <security:global-method-security secured-annotations="enabled" />
<!-- 
	auto-config: Includes some basic services like form-login,http-basic, logout etc
	use-expressions: It is here to use expressions to secure individual URLs. These expressions can be 
					 e.g. hasRole([role]), hasAnyRole([role1,role2]), permitAll, denyAll etc
	intercept-url: This will match the requested url pattern from request and will decide what action to take based on access value.
	form-login: This will come into picture when user will try to access any secured URL. 
				A login page mapped to “login-page” attribute will be served for authentication check. 
				If not provided, spring will provide an inbuilt login page to user. It also contains attribute 
				for default target if login success, or login failure due to invalid user/password match.
 --> 
    <security:http auto-config="true"  use-expressions="true">
    	<!-- intercept-url=/j_spring_security_check: By default, spring auto generates and configures a UsernamePasswordAuthenticationFilter bean. 
    						This filter, by default, responds to the URL /j_spring_security_check when processing a login POST from your web-form. 
    						For username field it uses ‘j_username‘ and for password field it uses ‘j_password‘. 
    	-->
    	<security:intercept-url pattern="/j_spring_security_check" access="permitAll" />
        <security:intercept-url pattern="/springmvc/login" access="permitAll" />
        <security:intercept-url pattern="/springmvc/logout" access="permitAll" />
        <security:intercept-url pattern="/springmvc/accessdenied" access="permitAll" />
        <security:intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
        <security:form-login login-page="/springmvc/login" default-target-url="/springmvc/homepage" authentication-failure-url="/springmvc/accessdenied" />
        <security:logout logout-success-url="/springmvc/logout" />
    </security:http>
 
 <!-- 
 	  Authentication Providers for Form Login:
 	  below user-service hardcoded the username and password (root/root) in this xml file itself
 	  In realtime application this is going to be some user service fetching data from remote database 
  -->
    <security:authentication-manager alias="authenticationManager">
        <security:authentication-provider>
            <security:user-service>
                <security:user name="root" password="root" authorities="ROLE_USER" />
                <!-- Below is the way to check username/password from remote database. It is commented out now -->
                <!-- <security:jdbc-user-service data-source-ref="dataSource" /> -->
            </security:user-service>
        </security:authentication-provider>
    </security:authentication-manager>
 
 <!-- ADD THE DATASOURCES HERE -->
 
</beans:beans>
  • index.jsp: This is welcome file as configured in web.xml

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
 
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Spring 4 MVC - HelloWorld Index Page</title>
</head>
<body>
 
	<center>
		<h2>Hello World</h2>
		<h3>
			<a href="springmvc/hello?name=Ashish">Click Here</a>
		</h3>
	</center>
</body>
</html>
  • login.jsp, logout.jsp, denied.jsp, helloworld.jsp files are created inside WEB-INF/views folder

    • login.jsp

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
 
<html>
    <body>
        <h1 id="banner">Login to Security Demo</h1> 
        <form name="f" action="<c:url value='/j_spring_security_check'/>"
                    method="POST">
            <table>
                <tr>
                    <td>Username:</td>
                    <td><input type='text' name='j_username' /></td>
                </tr>
                <tr>
                    <td>Password:</td>
                    <td><input type='password' name='j_password'></td>
                </tr>
                <tr>
                    <td colspan="2"> </td>
                </tr>
                <tr>
                    <td colspan='2'><input name="submit" type="submit"> <input name="reset" type="reset"></td>
                </tr>
            </table>
        </form>
    </body>
</html>
  • logout.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
	<% session.invalidate(); %>
	You are now logged out!!
 
	<a href="${pageContext.request.contextPath}/">go back</a>
</body>
</html>
  • denied.jsp

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
    <body>
     
        <h1 id="banner">Unauthorized Access !!</h1>
     
        <hr />
     
        <c:if test="${not empty error}">
            <div style="color:red">
                Invalid Username/password !!<br />
                Caused : ${sessionScope["SPRING_SECURITY_LAST_EXCEPTION"].message}
            </div>
        </c:if>
     
        <p class="message">Access denied!</p>
        <a href="login">Go back to login page</a>
    </body>
</html>
  • helloworld.jsp

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Hello World title</title>
</head>
<body>
	<a href="<c:url value="logout" />" > Logout</a>
	<center>
		<h2>Hello World</h2>
		<h3>
			${message} ${name}
		</h3>
	</center>
</body>
</html>
  • LoginController.java controls login,logout and denied request and HelloWorldController.java controls hello world request

  • LoginController.java


package com.ashish.springmvc.controller;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

//Below annotation is used to mark this class as controller.
//<context:component-scan base-package="com.ashish.springmvc.controller" /> is declared
//in mvc-applicationContext.xml file to scan controller in com.ashish.springmvc.controller package
@Controller
public class LoginController {
	private static final Log log = LogFactory.getLog(LoginController.class);
	
	@RequestMapping(value = "/login", method = RequestMethod.GET)
    public ModelAndView login(ModelMap model) {
		log.info("Going login page got called");
		ModelAndView mv = new ModelAndView("login");
		return mv;
    }
 
    @RequestMapping(value = "/accessdenied", method = RequestMethod.GET)
    public String loginerror(ModelMap model) {
        model.addAttribute("error", "true");
        return "denied";
    }
 
    @RequestMapping(value = "/logout", method = RequestMethod.GET)
    public String logout(ModelMap model) {
        return "logout";
    }
}

  • HelloWorldController.java

package com.ashish.springmvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
 
@Controller
public class HelloWorldController {
	String message = "Welcome to Spring MVC!";
 
	@RequestMapping("/hello")
	public ModelAndView showMessage(
			@RequestParam(value = "name", required = false, defaultValue = "World") String name) {
		System.out.println("in controller");
 
		ModelAndView mv = new ModelAndView("helloworld");
		mv.addObject("message", message);
		mv.addObject("name", name);
		return mv;
	}
}

Screen navigation

The screen navigation or page flow for the above is shown below

Common issues during POST/GET call

SL NO Issue Solution
1 Exception handling in http tag redirects to some other page, i.e. access denied page. In such scenario if you are getting “405 Method Not Allowed” error. You won’t get actual reason for the issue until you remove the exception handling in http tag. Once exception handling attribute is removed then you will get this error “Invalid CSRF Token ‘null’ was found on the request parameter ‘_csrf’ or header ‘X-CSRF-TOKEN’.” To solve this error, pass _csfr as a query parameter and <input type=”hidden” name=”${_csrf.parameterName}” value=”${_csrf.token}” /> to be added in jsp page.


blog comments powered by Disqus
J2EE,SOAP,RESTful,SVN,PMD,SONAR,JaCoCo,HTTP,API,MAVEN,AngularJS,GitHub,LDAP,AOP,ORM,JMS,MVC,AWS,SQL,PHP,H2DB,JDBC