Securing a Spring Boot JSP Application usng Spring Securiy

In this article we will see How to add Spring Security support in a Spring Boot JSP application. We will use the HTTP Basic authentication to secure a spring boot web application.

Project structure

Following is the project structure, indicating where to put jsps and static resources like css and js files, this project structure shows standard way of creating a spring boot web project.



Dependencies (pom.xml)

In order to create a Spring Boot JSP application with Spring Security we have to add spring-boot-starter-tomcat and tomcat-embed-jasper in pom.xml, tomcat-embed-jasper is required to compile JSPs.

spring-boot-starter-security is to be added to pom.xml to make it support spring security configuration and features.
<?xml version="1.0" encoding="UTF-8"?>
<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>
	<packaging>war</packaging>
	<groupId>com.tb</groupId>
	<artifactId>tb-web-jsp</artifactId>
	<version>0.1.0</version>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.5.RELEASE</version>
	</parent>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-tomcat</artifactId>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.apache.tomcat.embed</groupId>
			<artifactId>tomcat-embed-jasper</artifactId>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>4.0.1</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
	</dependencies>

	<properties>
		<java.version>1.8</java.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

Spring boot application launch class

SpringBootServletInitializer" is used to run a SpringApplication from a traditional WAR deployment, it binds Servlet, Filter and ServletContextInitializer beans from the application context to the server.

/src/main/java/com/tb/java/App.java
package com.tb.java;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;

/**
 * @author nagesh.chauhan
 *
 */
@SpringBootApplication
@ComponentScan("com.tb.*")
public class App extends SpringBootServletInitializer {

	@Override
	protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
		return application.sources(App.class);
	}

	public static void main(String[] args) {
		SpringApplication.run(App.class, args);
	}
}
Here, @SpringBootApplication does all the magic, a class having main() and annotated with @SpringBootApplication is called Spring boot application launch class.


Spring Security Configuration

To add spring security support to a Spring Boot JSP project we need to add a configuration class extending WebSecurityConfigurerAdapter.

Here AuthenticationManagerBuilder is used to define in memory user and password with roles and HttpSecurity is where we define rules related to application security.

According to configuration, any url starting from "/home" will not require any authentication while urls starting from "/user" and "/admin" will require authentication as per their respective roles.

/src/main/java/com/tb/java/SpringSecurityConfig.java
package com.tb.java;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.inMemoryAuthentication().withUser("user").password("{noop}userpassword").roles("USER").and()
				.withUser("admin").password("{noop}adminpassword").roles("USER", "ADMIN");

	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.httpBasic().and().authorizeRequests().antMatchers("/home*").permitAll().antMatchers("/user/**")
				.hasAnyRole("USER", "ADMIN").antMatchers("/admin/**").hasRole("ADMIN").and().csrf().disable().formLogin()
				.disable();
	}
}


Controller class

HomeController is annotated with @Controller, telling spring that the class will handle incoming requests, Model parameter is used to send data from Controller to JSP and "index" returned as a string is name of JSP.

/src/main/java/com/tb/controller/HomeController.java
package com.tb.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HomeController {

	@GetMapping({ "/", "/home" })
	private String home(Model model) {
		model.addAttribute("message", "Hello World !");
		return "index";
	}
	
	@GetMapping({ "user" })
	private String user(Model model) {
		model.addAttribute("message", "Hello User !");
		return "index";
	}
	
	@GetMapping({ "admin" })
	private String admin(Model model) {
		model.addAttribute("message", "Hello Admin !");
		return "index";
	}
}



Add JSPs and Static resources


/src/main/webapp/WEB-INF/jsp/index.jsp
<!DOCTYPE html>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<title>Spring Security Basic AUTH Example</title>
<link href="/css/style.css" rel="stylesheet">
<script src="/js/app.js"></script>
</head>
<body>
	<h1>${message}</h1>
	<h2>Spring Security Basic AUTH Example</h2>

</body>
</html>

/src/main/resources/static/css/style.css
html {
	display: table;
	width: 100%;
}

body {
	display: table-cell;
	text-align: center;
	vertical-align: middle;
}

/src/main/resources/static/js/app.js
(function(){
    console.log("Hello World!");
})();

Changes in application.properties

We have added "prefix" and "suffix" properties of spring.mvc.view, this will map jsp names passed in controller to fully qualified named respective to webapp folder.

/src/main/resources/application.properties
spring.mvc.view.prefix: /WEB-INF/jsp/
spring.mvc.view.suffix: .jsp

logging.level.org.springframework=TRACE
logging.level.com=TRACE

Run and Test the application

Here we are all done with "Creating a spring boot jsp application supporting sprinig security", do a maven clean-install and you will find .war file created in "target" folder, this file can be used to deploy on externam application server like tomcat.

While using IDE like Eclipse, we can also run the application from App.java using run as > Java Application and the application will start on default 8080 port.

If everything goes right you will see a JSP view as shown below on both http://localhost:8080/ and "http://localhost:8080/home":

1) Not secured URL

http://localhost:8080/



2) Secured URL

http://localhost:8080/user/



Use user as username and userpassword as password, after authentication you will see a page like this.



3) Access Denied


User: "user" have ROLE_USER only, if we try to access /admin/* urls with this user there will be a 403 - Access Denied error because "/admin/*" urls require ROLE_ADMIN.

http://localhost:8080/admin/



We are done will "Integrating Spring security with Spring boot and JSP", in upcoming applications we will see more about "Spring Boot" and "Spring Framework".

Download complete project code from: GitHub