Spring Security for Spring MVC 4 Application Simple Example using Spring Boot



Well, after quite a long time, nearly a year, I am all set to publish my next post here. This has been a post that is long overdue and highly requested for. I am gonna write about how to secure a Spring MVC 4 web application using Spring Security. I am going to use Spring Boot to build a quick and configuration-less application. I have written in detail on how to use Spring Boot in Spring Data Rest application here.

Spring Boot can be used with build tools such as Maven or Gradle. These build tools help you share jars between various applications, build your application and generate reports. I am going to use the same application that is provided in the spring security getting started guide but with JSP for views.


Set up project with Spring Boot

1. Go to New -> Maven Project in Eclipse,


2. Click Next -> Check Create a simple project -> Give workspace location



3. Click Next ->In Archetype selection, select maven-archetype-webapp



and then give little details about the project in the next screen,



When we are done implementing the project at the end of this tutorial, the project structure is going to look like this,


4. Let us create a very simple spring mvc application and secure it using spring security. Rewrite your pom.xml file to match what is given below.

pom.xml

<?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>
    <groupId>org.programmingfree</groupId>
    <artifactId>pf-securing-web</artifactId>
    <version>0.1.0</version>
 <packaging>war</packaging>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.2.5.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
      <groupId>org.apache.tomcat.embed</groupId>
      <artifactId>tomcat-embed-jasper</artifactId>
      <scope>provided</scope>
  </dependency>
  <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</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-web</artifactId>
    </dependency>
    </dependencies>

    <properties>
        <java.version>1.8</java.version>
    </properties>

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

    <repositories>
        <repository>
            <id>spring-releases</id>
            <name>Spring Releases</name>
            <url>https://repo.spring.io/libs-release</url>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>spring-releases</id>
            <name>Spring Releases</name>
            <url>https://repo.spring.io/libs-release</url>
        </pluginRepository>
    </pluginRepositories>

</project>

 -- If you notice in the above pom.xml we are adding two dependencies given below since we are using JSP for the views.

<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
</dependency>

-- Though we use "war" for packaging, we will still be able to execute it. This is taken care of by 'spring-boot-maven-plugin'.
-- We have 'spring-boot-starter-security' as one of the dependencies and this is going to bootstrap everything related to security for us.

5. Create views inside WEB-INF\jsp one by one.

src\main\webapp\WEB-INF\jsp\home.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
    <head>
        <title>Spring Security Example - ProgrammingFree</title>
    </head>
    <body>
        <h1>Welcome!</h1>

        <p>Click <a href="<spring:url value='/hello' />">here</a> to see a greeting.</p>
    </body>
</html>

This is a simple welcome page and it is not going to be secured. This page has a link to a greeting page (hello.jsp) which can be accessed only after being authenticated.

src\main\webapp\WEB-INF\jsp\hello.jsp


<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
    <head>
        <title>Hello World!</title>
    </head>
    <body>
        <h1>Hello <b><c:out value="${pageContext.request.remoteUser}"/></b> </h1>
        <form action="/logout" method="post">
            <input type="submit" value="Sign Out"/>
            <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
        </form>
    </body>
</html>

This page displays a greeting message with the name of logged in user and can only be accessed by autheticated users.

src\main\webapp\WEB-INF\jsp\login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
    <head>
        <title>Spring Security Example </title>
    </head>
    <body>
    <c:if test="${param.error ne null}">
        <div>
            Invalid username and password.
        </div>
     </c:if>
         <c:if test="${param.logout ne null}">
        <div>
            You have been logged out.
        </div>
      </c:if>
                <form action="/login" method="post">
            <div><label> User Name : <input type="text" name="username"/> </label></div>
            <div><label> Password: <input type="password" name="password"/> </label></div>
            <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
            <div><input type="submit" value="Sign In"/></div>
        </form>
    </body>
</html>

As the name states itself, this page contains the login form for the users to submit their credentials.

6. Create three java classes inside a package called 'hello' with the code given below.

src\main\java\hello\MvcConfig.java

package hello;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/home").setViewName("home");
        registry.addViewController("/").setViewName("home");
        registry.addViewController("/hello").setViewName("hello");
        registry.addViewController("/login").setViewName("login");
    }
    
    @Bean
 public InternalResourceViewResolver viewResolver() {
  InternalResourceViewResolver resolver = new InternalResourceViewResolver();
  resolver.setPrefix("/WEB-INF/jsp/");
  resolver.setSuffix(".jsp");
  return resolver;
 }

}

src\main\java\hello\WebSecurityConfig.java

package hello;

import org.springframework.beans.factory.annotation.Autowired;
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;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;

@Configuration
@EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/", "/home").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()
                .permitAll();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .inMemoryAuthentication()
                .withUser("user").password("password").roles("USER");
    }
}

src\main\java\hello\Application.java


package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) throws Throwable {
        SpringApplication.run(Application.class, args);
    }
}

That is all! Now to run the application, right click project, run as Maven Build with the goal,

clean install spring-boot:run

This will install the project with all necessary dependencies, generate war file in target folder and start the embedded tomcat server provided by spring boot. Open a browser and hit http://localhost:8080/ to see the home page,



On clicking the link for greeting message, user will be redirected to login page as shown below,




As you can see, the login page provides a simple form that captures a username and password and posts them to "/login". As configured, Spring Security provides a filter that intercepts that request and authenticates the user. If the user fails to authenticate, the page is redirected to "/login?error" and our page displays the appropriate error message. Upon successfully signing out, our application is sent to "/login?logout" and our page displays the appropriate success message.




User is redirected to login page after signing out, with a logout message,


If the credentials are wrong, user is redirected to login page with error message,



To know how to directly run the downloaded project, watch this video.

How it works

First let me start with application setup. Throughout the implementation, we did not write any xml configuration and even web.xml is eliminated with the use of Spring Boot. Let me go step by step on how Spring Boot set up the application for us,

1. Once maven downloads all required libraries to the classpath (WEB-INF\lib), Spring Boot looks into the classpath and makes reasonable assumptions about what you’re missing, and adds it.

2. Spring Boot launches an application from a class which is annotated with @SpringBootApplication, so in our example it starts with 'Application.java'

@SpringBootApplication is a convenience annotation that adds all of the following:

             -- @Configuration tags the class as a source of bean definitions for the application context.

             -- @EnableAutoConfiguration tells Spring Boot to start adding beans based on classpath settings, other beans, and various property settings.

            -- Normally you would add @EnableWebMvc for a Spring MVC app, but Spring Boot adds it automatically when it sees spring-webmvc on the classpath. This flags the application as a web application and activates key behaviors such as setting up a DispatcherServlet.

           -- @ComponentScan tells Spring to look for other components, configurations, and services inside the same package as it is in. In this case it looks in to all classes inside the 'hello' package.

3. While 'hello' package is scanned, it will go through all classes with @Configuration and will register all configurations present. In our example, we have MvcConfig and WebSecurityConfig annotated with @Configuration

4. MvcConfig class registers the views with urls and hence exposes these url mappings to corresponding views. 

Spring Security

When spring-security is present in the classpath, Spring automatically secures all HTTP end points with basic authentication. To further customize the security settings, such as authenticating users against details stored in a database or autenticate only specific http endpoints not all etc. you should setup security configuration. In our example, we provide a simple in-memory authentication for all the pages except for the home page(home.jsp) and this is configured in WebSecurityConfig class.

The WebSecurityConfig class is annotated with @EnableWebMvcSecurity to enable Spring Security’s web security support and provide the Spring MVC integration. It also extends WebSecurityConfigurerAdapter and overrides a couple of its methods to set some specifics of the web security configuration. The configure(HttpSecurity) method defines which URL paths should be secured and which should not. Specifically, the "/" and "/home" paths are configured to not require any authentication. All other paths must be authenticated.

When a user successfully logs in, they will be redirected to the previously requested page that requires authentication. There is a custom "/login" page specified by loginPage(), and everyone is allowed to view it. As for the configure(AuthenticationManagerBuilder) method, it sets up an in-memory user store with a single user. That user is given a username of "user", a password of "password", and a role of "USER".

Last we need to provide the user a way to display the current username and Sign Out. Update the hello.html to say hello to the current user and contain a "Sign Out" form as shown below

CSRF Attack

You might have noticed myself placing hidden input type with name="${_csrf.parameterName}" and  value="${_csrf.token}" in the login page and the page from where users log out. This is to protect the application against Cross Site Request Forgery (CSRF) attacks.

CSRF protection is enabled by default with Java Configuration. You can disable it as well. If you are using Spring MVC <form:form> tag, the CsrfToken is automatically included. Since we are not using Spring form tag, I have used hidden inputs to send csrf tokens to the server. A detailed and a very clear explanation of CSRF support in Spring MVC is provided in this article.

 DOWNLOAD

How to run the downloaded project


Note: I have added a static resource in the demo project for styling and I have excluded that in the tutorial for simplicity sake. I will write about how to use static resources such as javascript or css, while using Spring Security and Spring Boot in a separate article.



Keep yourself subscribed for getting programmingfree articles delivered directly to your inbox once in a month. Thanks for reading!

Subscribe to GET LATEST ARTICLES!


Related

Trending 2874043638504411216

Post a Comment

  1. Thanks for sharing the useful tutorial..

    ReplyDelete
  2. Sorry, but application doesn't work. After trying to access any page it writes:
    Whitelabel Error Page

    This application has no explicit mapping for /error, so you are seeing this as a fallback.

    Mon Nov 16 14:57:45 CET 2015
    There was an unexpected error (type=Not Found, status=404).
    No message available

    ReplyDelete
  3. Thank you for this tutorial, everything works )

    ReplyDelete
  4. Hi there, but why CSS doesn't get loaded, please refer to the article how to use resources in spring boot security.

    ReplyDelete
    Replies
    1. It works without @EnableWebMVC how to make it use static resources with that annotation for security

      Delete

emo-but-icon

SUBSCRIBE


item