DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • RESTful Web Services: How To Create a Context Path for Spring Boot Application or Web Service
  • Node.js Http Module to Consume Spring RESTful Web Application
  • Spring Boot - How To Use Native SQL Queries | Restful Web Services
  • Authentication With Remote LDAP Server in Spring Web MVC

Trending

  • How to Merge HTML Documents in Java
  • Detection and Mitigation of Lateral Movement in Cloud Networks
  • Creating a Web Project: Caching for Performance Optimization
  • Secrets Sprawl and AI: Why Your Non-Human Identities Need Attention Before You Deploy That LLM
  1. DZone
  2. Coding
  3. Frameworks
  4. Secure REST Services and Web Applications With Spring Boot Security Starter

Secure REST Services and Web Applications With Spring Boot Security Starter

In this article, we show you how to integrate security into your web application during the development process using Spring Boot Security.

By 
Ranga Karanam user avatar
Ranga Karanam
DZone Core CORE ·
Jun. 30, 17 · Tutorial
Likes (9)
Comment
Save
Tweet
Share
55.7K Views

Join the DZone community and get the full member experience.

Join For Free

This guide will help you understand the different features Spring Boot Starter Security brings by using two examples:

  • A REST Service.
  • A Simple Web Application Page.

You Will Learn

  • What is Spring Boot Starter Security?
  • What features are provided by Spring Boot Starter Security?
  • How do you enable Spring Security on a web application?
  • How do you enable Spring Security on a REST Web Service?
  • How do you invoke a REST Service using Basic Authentication?
  • We will look at an example of security in a simple web application as well as security in a REST service with Basic Authentication.

Bootstrapping a Web Application With Spring Initializr

Creating a web application with Spring Initializr is a cake walk. We will use Spring Web MVC as our web framework for both the web page and the rest service.

Spring Initializr http://start.spring.io/ is great tool to bootstrap your Spring Boot projects.

Image

As shown in the image above, the following steps have to be completed. 

  • Launch Spring Initializr and choose the following:
    • Choose com.in28minutes.springboot as Group.
    • Choose student-services as Artifact.
    • Choose the following dependencies:
      • Web
  • Click Generate Project.
  • Import the project into Eclipse.

Setting Up a Simple Web Application

Let’s quickly set up a simple web application to secure using Spring Security. We will create one controller which will redirect to the welcome view - a simple JSP.

JSP Support

We want to use JSP as the view. Tomcat is the default embedded servlet container for Spring Boot Starter Web. To enable support for JSPs, we would need to add a dependency on tomcat-embed-jasper.

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

Adding Login Controller

LoginController maps the root URL “/” to the showLoginPage method. A hard coded name is populated into the model. It returns a view name of “welcome” which maps to welcome.jsp.

package com.in28minutes.springboot.web.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class LoginController {

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String showLoginPage(ModelMap model) {
        model.put("name", "in28Minutes");
        return "welcome";
    }

}

Adding welcome.jsp

welcome.jsp is a simple JSP with a welcome message.

<div class="container">
    Welcome ${name}!! 
</div>

Configure a View Resolver

welcome.jsp is in the folder: src/main/webapp/WEB-INF/jsp/. We will configure a view resolver in application.properties to map the view name to the physical JSP.

spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp

Run the Web Application

Launch StudentServicesApplication as a Java application. The following screenshot shows how the application looks when launched at http://localhost:8080.

Image

Add a Simple REST Service

Let's add a simple REST Service as well. We will add:

  • Two Model Objects - We will call them Course and Student. A student can register for multiple courses.
  • One Business Service - This will manage the business logic. Most of the business logic we use is on top of hard coded data stored in a static ArrayList.
  • One Rest Controller - We will call this StudentController. It exposes one REST service to retrieve the list of courses for which a student is registered.

Model and Business Logic

The below snippets show an extract from the model objects, Course and Student.

public class Course {
private String id;
private String name;
private String description;
private List<String> steps;
}


public class Student {
private String id;
private String name;
private String description;
private List<Course> courses;
}

StudentService provides a method, public List<Course> retrieveCourses(String studentId), to retrieve the courses for which a student registered.

@Component
public class StudentService {

private static List<Student> students = new ArrayList<>();

static {
// Initialize Data
Course course1 = new Course("Course1", "Spring", "10 Steps",
Arrays.asList("Learn Maven", "Import Project", "First Example",
"Second Example"));
Course course2 = new Course("Course2", "Spring MVC", "10 Examples",
Arrays.asList("Learn Maven", "Import Project", "First Example",
"Second Example"));

Student ranga = new Student("Student1", "Ranga Karanam",
"Hiker, Programmer and Architect", new ArrayList<>(
Arrays.asList(course1, course2)));

students.add(ranga);
}

public Student retrieveStudent(String studentId) {
for (Student student : students) {
if (student.getId().equals(studentId)) {
return student;
}
}
return null;
}

public List<Course> retrieveCourses(String studentId) {
Student student = retrieveStudent(studentId);

if (student == null) {
return null;
}

return student.getCourses();
}
}

Creating REST Service

The REST Service StudentController exposes a simple Get service at URI mapping location, “/students/{studentId}/courses.” The StudentService is auto-wired in.

package com.in28minutes.springboot.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import com.in28minutes.springboot.model.Course;
import com.in28minutes.springboot.service.StudentService;

@RestController
public class StudentController {

@Autowired
private StudentService studentService;

@GetMapping("/students/{studentId}/courses")
public List<Course> retrieveCoursesForStudent(@PathVariable String studentId) {
return studentService.retrieveCourses(studentId);
}
}

Executing REST Service

Executing the REST Service is easy. Go to the URL, http://localhost:8080/students/Student1/courses, in your browser or your favorite rest client. I used Postman.

Image

Add Spring Boot Starter Security

Let’s now add Spring Boot Starter Security as a dependency to this project.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

New Dependencies

This screenshot shows the new dependencies that the spring-boot-starter-security brings in.

Image

Auto Configuration

When we restart the application, we see following statements printed in the log.

Mapping filter: 'springSecurityFilterChain' to: [/*]

Using default security password: 25e07e82-720d-4109-ba8d-25177c6347e6

Creating filter chain:
...
...

All this magic is because of auto-configuration:

  • Mapping filter: 'springSecurityFilterChain' to: [/*] : Spring Security is by default turned on for all the URLs in the application.
  • Basic Authentication is the default.
  • Using default security password: 25e07e82-720d-4109-ba8d-25177c6347e6 : The default user ID is 'user.' The default password is printed in the server's startup log. In this example, the password is: 25e07e82-720d-4109-ba8d-25177c6347e6
  • Some filter chains are configured to enable security.

Executing REST Service

When we execute the REST Service now at http://localhost:8080/students/Student1/courses, it returns authentication failure. It returns a 401 status with the message “Bad credentials.” This is because our service is now protected by Spring Security.

{
  "timestamp": 1485768623632,
  "status": 401,
  "error": "Unauthorized",
  "message": "Bad credentials",
  "path": "/students/Student1/courses"
}

The below image shows the execution of service in Postman:

Image

Executing REST Service With Basic Authentication

Get the password from the log by searching for Using default security password:. The user ID is 'user.' Use this combination to execute the service using Basic Authentication as shown in the screenshot below.

Image

Running the Web Application

When you launch the URL http://localhost:8080 in the browser, you get a popup asking for the user ID and password. You will need to enter the same details that we provided for the REST Service.

Image

That’s really a lot of magic we turned on just by adding a simple dependency Spring Boot Starter Security.

Configuring Custom Users and Roles

Let’s now configure custom users and roles.

  • We will use two roles, Admin and User. The Admin role has access to the web application and the User role has access to execute REST Services.
  • We will create one user for the Admin role with the credentials, admin1/secret1.
  • We will create one user for the User role with the credentials, user1/secret1.
package com.in28minutes.springboot.security;

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 SecurityConfig extends WebSecurityConfigurerAdapter {
// Authentication : User --> Roles
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.inMemoryAuthentication().withUser("user1").password("secret1")
.roles("USER").and().withUser("admin1").password("secret1")
.roles("USER", "ADMIN");
}

// Authorization : Role -> Access
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and().authorizeRequests().antMatchers("/students/**")
.hasRole("USER").antMatchers("/**").hasRole("ADMIN").and()
.csrf().disable().headers().frameOptions().disable();
}

}

Executing REST Services

Now we can use the user1/secret1 combination to execute REST services.

Launch the Web Application

We need to use the admin1/secret1 combination in the popup to launch the web application.

Next Steps

Complete Code Example

/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>com.in28minutes.springboot</groupId>
<artifactId>student-services-security</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>student-services</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.4.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>

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

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

<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.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

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


</project>

/src/main/java/com/in28minutes/springboot/controller/LoginController.java

package com.in28minutes.springboot.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class LoginController {

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String showLoginPage(ModelMap model) {
        model.put("name", "in28Minutes");
        return "welcome";
    }

}

/src/main/java/com/in28minutes/springboot/controller/StudentController.java

package com.in28minutes.springboot.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import com.in28minutes.springboot.model.Course;
import com.in28minutes.springboot.service.StudentService;

@RestController
public class StudentController {

@Autowired
private StudentService studentService;

@GetMapping("/students/{studentId}/courses")
public List<Course> retrieveCoursesForStudent(@PathVariable String studentId) {
return studentService.retrieveCourses(studentId);
}
}

/src/main/java/com/in28minutes/springboot/model/Course.java

package com.in28minutes.springboot.model;

import java.util.List;

public class Course {
private String id;
private String name;
private String description;
private List<String> steps;

// Needed by Caused by: com.fasterxml.jackson.databind.JsonMappingException:
// Can not construct instance of com.in28minutes.springboot.model.Course:
// no suitable constructor found, can not deserialize from Object value
// (missing default constructor or creator, or perhaps need to add/enable
// type information?)
public Course() {

}

public Course(String id, String name, String description, List<String> steps) {
super();
this.id = id;
this.name = name;
this.description = description;
this.steps = steps;
}

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getDescription() {
return description;
}

public String getName() {
return name;
}

public List<String> getSteps() {
return steps;
}

@Override
public String toString() {
return String.format(
"Course [id=%s, name=%s, description=%s, steps=%s]", id, name,
description, steps);
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Course other = (Course) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}

}

/src/main/java/com/in28minutes/springboot/model/Student.java

package com.in28minutes.springboot.model;

import java.util.List;

public class Student {
private String id;
private String name;
private String description;
private List<Course> courses;

public Student(String id, String name, String description,
List<Course> courses) {
super();
this.id = id;
this.name = name;
this.description = description;
this.courses = courses;
}

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

public List<Course> getCourses() {
return courses;
}

public void setCourses(List<Course> courses) {
this.courses = courses;
}

@Override
public String toString() {
return String.format(
"Student [id=%s, name=%s, description=%s, courses=%s]", id,
name, description, courses);
}
}

/src/main/java/com/in28minutes/springboot/security/SecurityConfig.java

package com.in28minutes.springboot.security;

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 SecurityConfig extends WebSecurityConfigurerAdapter {
// Authentication : User --> Roles
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.inMemoryAuthentication().withUser("user1").password("secret1")
.roles("USER").and().withUser("admin1").password("secret1")
.roles("USER", "ADMIN");
}

// Authorization : Role -> Access
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and().authorizeRequests().antMatchers("/students/**")
.hasRole("USER").antMatchers("/**").hasRole("ADMIN").and()
.csrf().disable().headers().frameOptions().disable();
}

}

/src/main/java/com/in28minutes/springboot/service/StudentService.java

package com.in28minutes.springboot.service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.springframework.stereotype.Component;

import com.in28minutes.springboot.model.Course;
import com.in28minutes.springboot.model.Student;

@Component
public class StudentService {

private static List<Student> students = new ArrayList<>();

static {
// Initialize Data
Course course1 = new Course("Course1", "Spring", "10 Steps",
Arrays.asList("Learn Maven", "Import Project", "First Example",
"Second Example"));
Course course2 = new Course("Course2", "Spring MVC", "10 Examples",
Arrays.asList("Learn Maven", "Import Project", "First Example",
"Second Example"));

Student ranga = new Student("Student1", "Ranga Karanam",
"Hiker, Programmer and Architect", new ArrayList<>(
Arrays.asList(course1, course2)));

students.add(ranga);
}

public Student retrieveStudent(String studentId) {
for (Student student : students) {
if (student.getId().equals(studentId)) {
return student;
}
}
return null;
}

public List<Course> retrieveCourses(String studentId) {
Student student = retrieveStudent(studentId);

if (student == null) {
return null;
}

return student.getCourses();
}
}

/src/main/java/com/in28minutes/springboot/StudentServicesApplication.java

package com.in28minutes.springboot;

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

@SpringBootApplication
public class StudentServicesApplication {

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

/src/main/resources/application.properties

spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp

/src/main/webapp/WEB-INF/jsp/welcome.jsp

<div class="container">
    Welcome ${name}!! 
</div>

/src/test/java/com/in28minutes/springboot/StudentServicesApplicationTests.java

package com.in28minutes.springboot;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class StudentServicesApplicationTests {

@Test
public void contextLoads() {
}

}
Spring Framework REST Web Protocols Web Service Spring Security application Spring Boot Web application

Published at DZone with permission of Ranga Karanam, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • RESTful Web Services: How To Create a Context Path for Spring Boot Application or Web Service
  • Node.js Http Module to Consume Spring RESTful Web Application
  • Spring Boot - How To Use Native SQL Queries | Restful Web Services
  • Authentication With Remote LDAP Server in Spring Web MVC

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!