Securing RESTful API with Spring Boot, Security, and Data MongoDB

Securing RESTful API with Spring Boot, Security, and Data MongoDB

A comprehensive step by step tutorial on securing or authentication RESTful API with Spring Boot, Security, and Data MongoDB

A comprehensive step by step tutorial on securing or authentication RESTful API with Spring Boot, Security, and Data MongoDB. Previously, we have shown you how to securing Spring Boot, MVC and MongoDB web application. In this tutorial, the secure endpoint will restrict the access from an unauthorized request. Every request to secure endpoint should bring authorization token with it. Of course, there will be an endpoint for login which will get authorization token after successful login.

Table of Contents:

The following software, tools, and frameworks are required for this tutorial:

We assume that you already installed all required software, tools, and frameworks. So, we will not cover how to install that software, tools, and frameworks.

1. Generate a New Spring Boot Gradle Project

To create or generate a new Spring Boot Application or Project, simply go to Spring Initializer. Fill all required fields as below then click on Generate Project button.

The project will automatically be downloaded as a Zip file. Next, extract the zipped project to your java projects folder. On the project folder root, you will find build.gradle file for register dependencies, initially it looks like this.

buildscript {
    ext {
        springBootVersion = '2.1.2.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'com.djamware'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'org.springframework.security:spring-security-test'
}

Now, you can work with the source code of this Spring Boot Project using your own IDE or Text Editor. We are using Spring Tool Suite (STS). In STS, import the extracted zipped file as Existing Gradle Project.

Next, we have to add the JWT library to the build.gradle as the dependency. Open and edit build.gradle then add this line to dependencies after other implementation.

implementation 'io.jsonwebtoken:jjwt:0.9.1'

Next, compile the Gradle Project by type this command from Terminal or CMD.

./gradlew compile

Or you can compile directly from STS by right-clicking the project name then choose Gradle -> Refresh Gradle Project. Next, open and edit src/main/resources/application.properties then add these lines.

spring.data.mongodb.database=springmongodb
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017

2. Create Product, User and Role Model or Entity Classes

We will be creating all required models or entities for products, user and role. In STS, right-click the project name -> New -> Class. Fill the package with com.djamware.SecurityRest.models, the name with Product, and leave other fields and checkbox as default then click Finish Button.

Next, open and edit src/main/java/com/djamware/SecurityRest/models/Product.java then add this annotation above the class name that will point to MongoDB collection.

@Document(collection = "products")

Inside Product class, add these variables.

@Id
String id;
String prodName;
String prodDesc;
Double prodPrice;
String prodImage;

Add constructors after the variable or fields.

public Product() {
}

public Product(String prodName, String prodDesc, Double prodPrice, String prodImage) {
    super();
    this.prodName = prodName;
    this.prodDesc = prodDesc;
    this.prodPrice = prodPrice;
    this.prodImage = prodImage;
}

Generate or create Getter and Setter for each field.

public String getId() {
    return id;
}

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

public String getProdName() {
    return prodName;
}

public void setProdName(String prodName) {
    this.prodName = prodName;
}

public String getProdDesc() {
    return prodDesc;
}

public void setProdDesc(String prodDesc) {
    this.prodDesc = prodDesc;
}

public Double getProdPrice() {
    return prodPrice;
}

public void setProdPrice(Double prodPrice) {
    this.prodPrice = prodPrice;
}

public String getProdImage() {
    return prodImage;
}

public void setProdImage(String prodImage) {
    this.prodImage = prodImage;
}

Using STS you can organize imports automatically from the menu Source -> Organize Imports then you can see the imports after the package name.

package com.djamware.SecurityRest.models;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

You can do the same way as the above step for User and Role class. Here’s the User class looks like.

package com.djamware.SecurityRest.models;

import java.util.Set;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.IndexDirection;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection = "users")
public class User {

    @Id
    private String id;
    @Indexed(unique = true, direction = IndexDirection.DESCENDING, dropDups = true)
    private String email;
    private String password;
    private String fullname;
    private boolean enabled;
    @DBRef
&nbsp;&nbsp; &nbsp;private Set<Role> roles;
&nbsp;&nbsp; &nbsp;public String getId() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return id;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setId(String id) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.id = id;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public String getEmail() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return email;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setEmail(String email) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.email = email;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public String getPassword() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return password;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setPassword(String password) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.password = password;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public String getFullname() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return fullname;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setFullname(String fullname) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.fullname = fullname;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public boolean isEnabled() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return enabled;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setEnabled(boolean enabled) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.enabled = enabled;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public Set<Role> getRoles() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return roles;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setRoles(Set<Role> roles) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.roles = roles;
&nbsp;&nbsp; &nbsp;}

}

And the Role class will be like this.

package com.djamware.SecurityRest.models;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.IndexDirection;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection = "roles")
public class Role {

&nbsp;&nbsp; &nbsp;@Id
&nbsp; &nbsp; private String id;
&nbsp; &nbsp; @Indexed(unique = true, direction = IndexDirection.DESCENDING, dropDups = true)

&nbsp; &nbsp; private String role;
&nbsp;&nbsp; &nbsp;public String getId() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return id;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setId(String id) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.id = id;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public String getRole() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return role;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setRole(String role) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.role = role;
&nbsp;&nbsp; &nbsp;}

}

3. Create Product, User and Role Repository Interfaces

Next steps to create Product, User, and Role Repository Interfaces. From the STS, right-click the project name -> New -> Interface then fill all required fields and checkboxes as below before click Finish button.

Next, open and edit src/main/java/com/djamware/SecurityRest/repositories/ProductRepository.java then add extends to MongoDB CRUD Repository.

public interface ProductRepository extends CrudRepository<Product, String> {

}

Inside the class name add this method.

@Override
void delete(Product deleted);

Organize all required imports.

import org.springframework.data.repository.CrudRepository;
import com.djamware.SecurityRest.models.Product;

The same way can be applied to User and Role repositories. So, the User Repository Interface will look like this.

package com.djamware.SecurityRest.repositories;

import org.springframework.data.mongodb.repository.MongoRepository;
import com.djamware.SecurityRest.models.User;

public interface UserRepository extends MongoRepository<User, String> {

&nbsp;&nbsp; &nbsp;User findByEmail(String email);
}

And the Role Repository Interface will look like this.

package com.djamware.SecurityRest.repositories;

import org.springframework.data.mongodb.repository.MongoRepository;
import com.djamware.SecurityRest.models.Role;

public interface RoleRepository extends MongoRepository<Role, String> {

&nbsp;&nbsp; &nbsp;Role findByRole(String role);
}

4. Create a Custom User Details Service

To implements authentication using existing User and Role from MongoDB, we have to create a custom user details service. From the STS, right-click the project name -> New -> Class File then fill all required fields and checkboxes as below before clicking the finish button.

Next, open and edit src/main/java/com/djamware/SecurityRest/services/CustomUserDetailsService.java then give an annotation above the class name and implement the Spring Security User Details Service.

@Service
public class CustomUserDetailsService implements UserDetailsService {
}

Next, inject all required beans at the first line of the class bracket.

@Autowired
private UserRepository userRepository;

@Autowired
private RoleRepository roleRepository;

@Autowired
private PasswordEncoder bCryptPasswordEncoder;

Add a method to find a user by email field.

public User findUserByEmail(String email) {
&nbsp; &nbsp; return userRepository.findByEmail(email);
}

Add a method to save a new user.

public void saveUser(User user) {
&nbsp; &nbsp; user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
&nbsp; &nbsp; user.setEnabled(true);
&nbsp; &nbsp; Role userRole = roleRepository.findByRole("ADMIN");
&nbsp; &nbsp; user.setRoles(new HashSet<>(Arrays.asList(userRole)));
&nbsp; &nbsp; userRepository.save(user);
}

Override the Spring Security User Details to load User by email.

@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {

&nbsp; &nbsp; User user = userRepository.findByEmail(email);
&nbsp; &nbsp; if(user != null) {
&nbsp; &nbsp; &nbsp; &nbsp; List<GrantedAuthority> authorities = getUserAuthority(user.getRoles());
&nbsp; &nbsp; &nbsp; &nbsp; return buildUserForAuthentication(user, authorities);
&nbsp; &nbsp; } else {
&nbsp; &nbsp; &nbsp; &nbsp; throw new UsernameNotFoundException("username not found");
&nbsp; &nbsp; }
}

Add a method to get a set of Roles that related to a user.

private List<GrantedAuthority> getUserAuthority(Set<Role> userRoles) {
&nbsp; &nbsp; Set<GrantedAuthority> roles = new HashSet<>();
&nbsp; &nbsp; userRoles.forEach((role) -> {
&nbsp; &nbsp; &nbsp; &nbsp; roles.add(new SimpleGrantedAuthority(role.getRole()));
&nbsp; &nbsp; });

&nbsp; &nbsp; List<GrantedAuthority> grantedAuthorities = new ArrayList<>(roles);
&nbsp; &nbsp; return grantedAuthorities;
}

Add a method for authentication purpose.

private UserDetails buildUserForAuthentication(User user, List<GrantedAuthority> authorities) {
&nbsp; &nbsp; return new org.springframework.security.core.userdetails.User(user.getEmail(), user.getPassword(), authorities);
}

5. Configure Spring Boot Security Rest

Now, the main purpose of this tutorial is configuring Spring Security Rest. First, we have to create a bean for JWT token generation and validation. Right-click the project name -> New -> Class File. Fill the package name as com.djamware.SecurityRest.configs and the Class name as JwtTokenProvider then click the Finish button. Next, open and edit that newly created class file then give it an annotation above the class name.

@Component
public class JwtTokenProvider {
}

Add variables and injected bean inside the class bracket at the top lines.

@Value("${security.jwt.token.secret-key:secret}")
private String secretKey = "secret";

@Value("${security.jwt.token.expire-length:3600000}")
private long validityInMilliseconds = 3600000; // 1h

@Autowired
private CustomUserDetailsService userDetailsService;

Add a method for initialization.

@PostConstruct
protected void init() {
&nbsp; &nbsp; secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
}

Add a method to create a JWT token.

public String createToken(String username, Set<Role> set) {
&nbsp; &nbsp; Claims claims = Jwts.claims().setSubject(username);
&nbsp; &nbsp; claims.put("roles", set);
&nbsp; &nbsp; Date now = new Date();
&nbsp; &nbsp; Date validity = new Date(now.getTime() + validityInMilliseconds);
&nbsp; &nbsp; return Jwts.builder()//
&nbsp; &nbsp; &nbsp; &nbsp; .setClaims(claims)//
&nbsp; &nbsp; &nbsp; &nbsp; .setIssuedAt(now)//
&nbsp; &nbsp; &nbsp; &nbsp; .setExpiration(validity)//
&nbsp; &nbsp; &nbsp; &nbsp; .signWith(SignatureAlgorithm.HS256, secretKey)//
&nbsp; &nbsp; &nbsp; &nbsp; .compact();
}

Add a method to load User by username.

public Authentication getAuthentication(String token) {
&nbsp; &nbsp; UserDetails userDetails = this.userDetailsService.loadUserByUsername(getUsername(token));
&nbsp; &nbsp; return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
}

Add a method to get the username by JWT token.

public String getUsername(String token) {
&nbsp; &nbsp; return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();
}

Add a method to resolve JWT token from request headers of Authorization that has a Bearer prefix.

public String resolveToken(HttpServletRequest req) {
&nbsp; &nbsp; String bearerToken = req.getHeader("Authorization");
&nbsp; &nbsp; if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
&nbsp; &nbsp; &nbsp; &nbsp; return bearerToken.substring(7, bearerToken.length());
&nbsp; &nbsp; }
&nbsp; &nbsp; return null;
}

Add a method to validate a JWT token.

public boolean validateToken(String token) {
&nbsp; &nbsp; try {
&nbsp; &nbsp; &nbsp; &nbsp; Jws<Claims> claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);
&nbsp; &nbsp; &nbsp; &nbsp; if (claims.getBody().getExpiration().before(new Date())) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return false;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; return true;
&nbsp; &nbsp; } catch (JwtException | IllegalArgumentException e) {
&nbsp; &nbsp; &nbsp; &nbsp; throw new JwtException("Expired or invalid JWT token");
&nbsp; &nbsp; }
}

Finally, organize imports like below.

package com.djamware.SecurityRest.configs;

import java.util.Base64;
import java.util.Date;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import com.djamware.SecurityRest.models.Role;
import com.djamware.SecurityRest.services.CustomUserDetailsService;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

Next, create a JWT filter class with the name JwtTokenFilter in configs package that extends Spring GenericFilterBean. Replace all Java codes with these lines of codes.

package com.djamware.SecurityRest.configs;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.GenericFilterBean;

public class JwtTokenFilter extends GenericFilterBean {

&nbsp;&nbsp; &nbsp;private JwtTokenProvider jwtTokenProvider;

&nbsp; &nbsp; public JwtTokenFilter(JwtTokenProvider jwtTokenProvider) {
&nbsp; &nbsp; &nbsp; &nbsp; this.jwtTokenProvider = jwtTokenProvider;
&nbsp; &nbsp; }

&nbsp; &nbsp; @Override
&nbsp; &nbsp; public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain)
&nbsp; &nbsp; &nbsp; &nbsp; throws IOException, ServletException {
&nbsp; &nbsp; &nbsp; &nbsp; String token = jwtTokenProvider.resolveToken((HttpServletRequest) req);
&nbsp; &nbsp; &nbsp; &nbsp; if (token != null && jwtTokenProvider.validateToken(token)) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Authentication auth = token != null ? jwtTokenProvider.getAuthentication(token) : null;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; SecurityContextHolder.getContext().setAuthentication(auth);
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; filterChain.doFilter(req, res);
&nbsp; &nbsp; }
}

Next, create a class with the name JwtConfigurer for JWT configuration in configs package then replace all codes with these lines of codes.

package com.djamware.SecurityRest.configs;

import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

public class JwtConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {

&nbsp;&nbsp; &nbsp;private JwtTokenProvider jwtTokenProvider;

&nbsp; &nbsp; public JwtConfigurer(JwtTokenProvider jwtTokenProvider) {
&nbsp; &nbsp; &nbsp; &nbsp; this.jwtTokenProvider = jwtTokenProvider;
&nbsp; &nbsp; }

&nbsp; &nbsp; @Override
&nbsp; &nbsp; public void configure(HttpSecurity http) throws Exception {
&nbsp; &nbsp; &nbsp; &nbsp; JwtTokenFilter customFilter = new JwtTokenFilter(jwtTokenProvider);
&nbsp; &nbsp; &nbsp; &nbsp; http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
&nbsp; &nbsp; }
}

Finally, we have to configure the Spring Security by creating a Java class file inside configs package with the name WebSecurityConfig. Give annotations to this class and extends Spring WebSecurityConfigurerAdapter.

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
}

Inject JWT token provider inside this class.

@Autowired
JwtTokenProvider jwtTokenProvider;

Add an override method to configure Authentication Manager Builder.

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
&nbsp;&nbsp; &nbsp;UserDetailsService userDetailsService = mongoUserDetails();
&nbsp;&nbsp; &nbsp;auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());

}

Next, add an override method to configure Spring Security Http Security.

@Override
protected void configure(HttpSecurity http) throws Exception {
&nbsp;&nbsp; &nbsp;http.httpBasic().disable().csrf().disable().sessionManagement()
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;.antMatchers("/api/auth/login").permitAll().antMatchers("/api/auth/register").permitAll()
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;.antMatchers("/api/products/**").hasAuthority("ADMIN").anyRequest().authenticated().and().csrf()
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;.disable().exceptionHandling().authenticationEntryPoint(unauthorizedEntryPoint()).and()
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;.apply(new JwtConfigurer(jwtTokenProvider));
}

Next, declare all required beans for this configuration.

@Bean
public PasswordEncoder bCryptPasswordEncoder() {
&nbsp;&nbsp; &nbsp;return new BCryptPasswordEncoder();
}

@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
&nbsp;&nbsp; &nbsp;return super.authenticationManagerBean();
}

@Bean
public AuthenticationEntryPoint unauthorizedEntryPoint() {
&nbsp;&nbsp; &nbsp;return (request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;"Unauthorized");
}

@Bean
public UserDetailsService mongoUserDetails() {
&nbsp;&nbsp; &nbsp;return new CustomUserDetailsService();
}

6. Create Product and Authentication Controllers

Now it’s time for REST API endpoint. All RESTful API will be created from each controller. Product controller will handle CRUD endpoint of product and Authentication controller will handle login and register endpoint. Right-click project name -> New -> Class then fill the package with com.djamware.SecurityRest.controllers and the class name as ProductController. Open and edit the newly created class file then replace all codes with these lines of codes.

package com.djamware.SecurityRest.controllers;

import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.djamware.SecurityRest.models.Product;
import com.djamware.SecurityRest.repositories.ProductRepository;

@RestController
public class ProductController {

&nbsp;&nbsp; &nbsp;@Autowired
&nbsp; &nbsp; ProductRepository productRepository;

&nbsp; &nbsp; @RequestMapping(method=RequestMethod.GET, value="/api/products")
&nbsp; &nbsp; public Iterable<Product> product() {
&nbsp; &nbsp; &nbsp; &nbsp; return productRepository.findAll();
&nbsp; &nbsp; }

&nbsp; &nbsp; @RequestMapping(method=RequestMethod.POST, value="/api/products")
&nbsp; &nbsp; public String save(@RequestBody Product product) {
&nbsp; &nbsp; &nbsp; &nbsp; productRepository.save(product);

&nbsp; &nbsp; &nbsp; &nbsp; return product.getId();
&nbsp; &nbsp; }

&nbsp; &nbsp; @RequestMapping(method=RequestMethod.GET, value="/api/products/{id}")
&nbsp; &nbsp; public Optional<Product> show(@PathVariable String id) {
&nbsp; &nbsp; &nbsp; &nbsp; return productRepository.findById(id);
&nbsp; &nbsp; }

&nbsp; &nbsp; @RequestMapping(method=RequestMethod.PUT, value="/api/products/{id}")
&nbsp; &nbsp; public Product update(@PathVariable String id, @RequestBody Product product) {
&nbsp; &nbsp; &nbsp;&nbsp; &nbsp;Optional<Product> prod = productRepository.findById(id);
&nbsp; &nbsp; &nbsp; &nbsp; if(product.getProdName() != null)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; prod.get().setProdName(product.getProdName());
&nbsp; &nbsp; &nbsp; &nbsp; if(product.getProdDesc() != null)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; prod.get().setProdDesc(product.getProdDesc());
&nbsp; &nbsp; &nbsp; &nbsp; if(product.getProdPrice() != null)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; prod.get().setProdPrice(product.getProdPrice());
&nbsp; &nbsp; &nbsp; &nbsp; if(product.getProdImage() != null)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; prod.get().setProdImage(product.getProdImage());
&nbsp; &nbsp; &nbsp; &nbsp; productRepository.save(prod.get());
&nbsp; &nbsp; &nbsp; &nbsp; return prod.get();
&nbsp; &nbsp; }

&nbsp; &nbsp; @RequestMapping(method=RequestMethod.DELETE, value="/api/products/{id}")
&nbsp; &nbsp; public String delete(@PathVariable String id) {
&nbsp; &nbsp; &nbsp; &nbsp; Optional<Product> product = productRepository.findById(id);
&nbsp; &nbsp; &nbsp; &nbsp; productRepository.delete(product.get());

&nbsp; &nbsp; &nbsp; &nbsp; return "product deleted";
&nbsp; &nbsp; }
}

For login, we need to create a POJO to mapping required fields of User. Create a new class file with the name AuthBody inside controllers package then replace all Java codes with these lines of codes.

package com.djamware.SecurityRest.controllers;

public class AuthBody {

&nbsp;&nbsp; &nbsp;private String email;
&nbsp; &nbsp; private String password;

&nbsp;&nbsp; &nbsp;public String getEmail() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return email;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setEmail(String email) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.email = email;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public String getPassword() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return password;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setPassword(String password) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.password = password;
&nbsp;&nbsp; &nbsp;}

}

Finally, create a controller for authentication with the name AuthController inside the controllers’ package. Open and edit that newly created file then replace all Java codes with these lines of codes.

package com.djamware.SecurityRest.controllers;

import static org.springframework.http.ResponseEntity.ok;

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.djamware.SecurityRest.configs.JwtTokenProvider;
import com.djamware.SecurityRest.models.User;
import com.djamware.SecurityRest.repositories.UserRepository;
import com.djamware.SecurityRest.services.CustomUserDetailsService;

@RestController
@RequestMapping("/api/auth")
public class AuthController {

&nbsp;&nbsp; &nbsp;@Autowired
&nbsp;&nbsp; &nbsp;AuthenticationManager authenticationManager;

&nbsp;&nbsp; &nbsp;@Autowired
&nbsp;&nbsp; &nbsp;JwtTokenProvider jwtTokenProvider;

&nbsp;&nbsp; &nbsp;@Autowired
&nbsp;&nbsp; &nbsp;UserRepository users;

&nbsp;&nbsp; &nbsp;@Autowired
&nbsp;&nbsp; &nbsp;private CustomUserDetailsService userService;

&nbsp;&nbsp; &nbsp;@SuppressWarnings("rawtypes")
&nbsp;&nbsp; &nbsp;@PostMapping("/login")
&nbsp;&nbsp; &nbsp;public ResponseEntity login(@RequestBody AuthBody data) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;try {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;String username = data.getEmail();
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, data.getPassword()));
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;String token = jwtTokenProvider.createToken(username, this.users.findByEmail(username).getRoles());
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Map<Object, Object> model = new HashMap<>();
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;model.put("username", username);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;model.put("token", token);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return ok(model);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;} catch (AuthenticationException e) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;throw new BadCredentialsException("Invalid email/password supplied");
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;}

&nbsp;&nbsp; &nbsp;@SuppressWarnings("rawtypes")
&nbsp;&nbsp; &nbsp;@PostMapping("/register")
&nbsp;&nbsp; &nbsp;public ResponseEntity register(@RequestBody User user) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;User userExists = userService.findUserByEmail(user.getEmail());
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;if (userExists != null) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;throw new BadCredentialsException("User with username: " + user.getEmail() + " already exists");
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;userService.saveUser(user);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Map<Object, Object> model = new HashMap<>();
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;model.put("message", "User registered successfully");
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return ok(model);
&nbsp;&nbsp; &nbsp;}
}

7. Run and Test Spring Boot Security Rest using Postman

Before run and test the application, we have to populate a Role data first. Open and edit src/main/java/com/djamware/SecurityRest/SecurityRestApplication.java then add these lines of codes inside the initialization method.

@Bean
CommandLineRunner init(RoleRepository roleRepository) {

&nbsp; &nbsp; return args -> {

&nbsp; &nbsp; &nbsp; &nbsp; Role adminRole = roleRepository.findByRole("ADMIN");
&nbsp; &nbsp; &nbsp; &nbsp; if (adminRole == null) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Role newAdminRole = new Role();
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; newAdminRole.setRole("ADMIN");
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; roleRepository.save(newAdminRole);
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; };

}

Next, make sure you have run the MongoDB server on your local machine then run the Gradle application using this command.

./gradlew bootRun

Or in STS just right-click the project name -> Run As -> Spring Boot App. Next, open the Postman application then change the method to GET and address to localhost:8080/api/products then click Send button.

You will see this response in the bottom panel of Postman.

{
&nbsp; &nbsp; "timestamp": "2019-03-07T13:16:34.935+0000",
&nbsp; &nbsp; "status": 401,
&nbsp; &nbsp; "error": "Unauthorized",
&nbsp; &nbsp; "message": "Unauthorized",
&nbsp; &nbsp; "path": "/api/products"
}

Next, change the method to POST then address to localhost:8080/api/auth/register then fill the body with raw data as below image then click Send button.

You will get the response in the bottom panel of Postman.

{
&nbsp; &nbsp; "message": "User registered successfully"
}

Next, change the address to localhost:8080/api/auth/login and change the body as below then click Send button.

{ "email":"[email&nbsp;protected]", "password": "q1w2we3r4" }

You will see this response in the bottom panel of Postman.

{
&nbsp; &nbsp; "username": "[email&nbsp;protected]",
&nbsp; &nbsp; "token": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJpbmZvQGRqYW13YXJlLmNvbSIsInJvbGVzIjpbeyJpZCI6IjVjODBjNjIzYjIwMTkxNGIyYTY5N2U4ZCIsInJvbGUiOiJBRE1JTiJ9XSwiaWF0IjoxNTUxOTY0OTc3LCJleHAiOjE1NTE5Njg1Nzd9.j5CHZ_LCmeQtdxQeH9eluxVXcOsHPWV1p8WnBn0CULo"
}

Copy the token then back to the GET product. Add a header with the name Authorization and the value that paste from a token that gets by login with additional Bearer prefix (with space) as below.

Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJpbmZvQGRqYW13YXJlLmNvbSIsInJvbGVzIjpbeyJpZCI6IjVjODBjNjIzYjIwMTkxNGIyYTY5N2U4ZCIsInJvbGUiOiJBRE1JTiJ9XSwiaWF0IjoxNTUxOTY0OTc3LCJleHAiOjE1NTE5Njg1Nzd9.j5CHZ_LCmeQtdxQeH9eluxVXcOsHPWV1p8WnBn0CULo

You should see this response after clicking the Send button.

[
&nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; "id": "5c80dc6cb20191520567b68a",
&nbsp; &nbsp; &nbsp; &nbsp; "prodName": "Dummy Product 1",
&nbsp; &nbsp; &nbsp; &nbsp; "prodDesc": "The Fresh Dummy Product in The world part 1",
&nbsp; &nbsp; &nbsp; &nbsp; "prodPrice": 100,
&nbsp; &nbsp; &nbsp; &nbsp; "prodImage": "https://dummyimage.com/600x400/000/fff"
&nbsp; &nbsp; }
]

You can test the POST product with the token in headers using the same way.

That it’s, the Securing RESTful API with Spring Boot, Security, and Data MongoDB tutorial. You can get the full source code from our GitHub.

Learn More

Build a Simple CRUD App with Spring Boot and Vue.js

Creating RESTful APIs with NodeJS and MongoDB Tutorial

MongoDB with Python Crash Course - Tutorial for Beginners

How to build RESTful APIs with ASP.NET Core

Understanding the basics of RESTful APIs

Developing RESTful APIs with Lumen (A PHP Micro-framework)

Java Programming Masterclass for Software Developers

Java In-Depth: Become a Complete Java Engineer!

JSP, Servlets and JDBC for Beginners: Build a Database App

JSP, Servlet, JSLT + Hibernate: A complete guide

How to Build a CRUD API with Java, MongoDB, and Spring Boot

How to Build a CRUD API with Java, MongoDB, and Spring Boot

This tutorial shows how to build a CRUD API with Java, MongoDB, and Spring Boot. How to build a CRUD API with Java and MongoDB. In this tutorial, you create a Java data model class and mapped it to a MongoDB domain document using Spring Data annotations. You use a simple embedded MongoDB database as the datastore. You use Spring Boot to quickly and easily expose your data model via a REST API. Finally, you secured the REST API using Okta and Okta’s Spring Boot Starter.

This tutorial leverages two technologies that are commonly used to build web services: MongoDB and Java (we’ll actually use Spring Boot). MongoDB is a NoSQL database, which is a generic term for any non-relational databases and differentiates them from relational databases. Relational databases, such as SQL, MySQL, Postgres, etc…, store data in large tables with well-defined structures. These structures are strong and tight and not easily changed or customized on a per-record basis (this structure can also be a strength, depending on the use case, but we won’t get too deep into that here). Further, because relational databases grew up pre-internet, they were designed to run on monolithic servers. This makes them hard to scale and sync across multiple machines.

NoSQL databases like MongoDB were developed, to a large degree, to fit the needs of internet scaling where server loads can balloon dramatically and the preferred growth pattern is the replication of servers, not scaling a single monolithic server. MongoDB is a document-based database that natively stores JSON and was built for distributed scaling. Mongo documents are JSON objects and have no predetermined structure on the side of the database. The structure of the documents is determined by the application and can be changed dynamically, adding or removing fields as needed. This means that Mongo documents are very flexible (possibly a blessing and a curse, FYI). Also, because MongoDB produces JSON documents, it has become very popular with many of the JS-based front-ends where Javascript is king and JSON is easily handled.

Spring Boot is an easy to use web application framework from Spring that can be used to create enterprise web services and web applications. They’ve done an admirable job simplifying the underlying complexity of the Spring framework, while still exposing all of its power. And no XML required! Spring Boot can be deployed in a traditional WAR format or can be run stand-alone using embedded Tomcat (the default), Jetty, or Undertow. With Spring you get the benefit of literally decades of proven enterprise Java expertise - Spring has run thousands of productions applications - combined with the simplicity of a modern, “just work” web framework, incredible depth of features, and great community support.

In this tutorial, you will create a simple Java class file to model your data, you will store this data in a MongoDB database, and you will expose the data with a REST API. To do this, you will use Spring Boot and Spring Data.

Once you have created an unsecured REST API, you are going to use Okta and Spring Security (along with Okta’s Spring Boot Starter) to quickly and easily at JSON Web Token (JWT) authentication to your web service.

Install Java, Spring Boot, MongoDB, and Other Project Dependencies

You’ll need to install a few things before you get started.

Java 11: This project uses Java 11. If you don’t have Java 11, you can install OpenJDK. Instructions are found on the OpenJDK website. OpenJDK can also be installed using Homebrew. SDKMAN is another great option for installing and managing Java versions.

HTTPie: This is a simple command-line utility for making HTTP requests. You’ll use this to test the REST application. Check out the installation instructions on their website.

Okta Developer Account: You’ll be using Okta as an OAuth/OIDC provider to add JWT authentication and authorization to the application. Go to developer.okta.com/signup and sign up for a free developer account, if you haven’t already.

Download a Skeleton Project From Spring Initializr

To create a skeleton project, you can use Spring Initializr. It’s a great way to quickly configure a starter for a Spring Boot project.

Open this link to view and download your pre-configured starter project on Spring Initializr.

Take a look at the settings if you like. You can even preview the project by clicking the Explore button at the bottom of the page.

Once you’re ready, click the green Generate button at the bottom of the page to download the starter project to your computer.

The starter for this project is a Spring Boot 2.2.2 project that uses Java as the application language and Gradle as the build system (there are other options for both). We’ve covered Gradle in-depth in a few other posts (see below), so we won’t go into too much detail here, except to say that you won’t need to install anything for Gradle to work because of the Gradle wrapper, which includes a version of Gradle with the project.

The included dependencies in this project are:

  • Spring Web (spring-boot-starter-web): web application functionality
  • Spring Data MongoDB (spring-boot-starter-data-mongodb): MongoDB functionality
  • Embedded MongoDB Database (de.flapdoodle.embed.mongo): embed an in-memory MongoDB database, great for testing and tutorials like this
  • Rest Repositories (spring-boot-starter-data-rest): needed for the @RepositoryRestResource annotation, which allows us to quickly generate a REST api from our domain classes
  • Okta (okta-spring-boot-starter): starter that simplifies integrating OAuth 2.0 and OIDC authentication and authorization
  • Lombok (lombok): a getter, constructor, and setter helper generator via annotations

Before you do anything else, you need to make two changes to the build.gradle file.

  1. Temporarily comment out the dependency okta-spring-boot-starter
  2. Change de.flapdoodle.embed.mongo from testImplementation to implementation

You’re doing number one because you won’t be configuring the JWT OAuth until later in the tutorial, and the application won’t run with this dependency in it unless it is configured. You’re changing the de.flapdoodle.embed.mongo dependency because typically this embedded database is only used in testing, but for the purposes of this tutorial, you’re using it in the actual implementation. In a production situation, you’d use a real MongoDB instance.

The dependencies {} block should look like this:

dependencies {  
    implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'  
    implementation 'org.springframework.boot:spring-boot-starter-data-rest'  
    implementation 'org.springframework.boot:spring-boot-starter-web'  
    //implementation 'com.okta.spring:okta-spring-boot-starter:1.3.0'  
    compileOnly 'org.projectlombok:lombok'  
    annotationProcessor 'org.projectlombok:lombok'  
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
    implementation 'de.flapdoodle.embed:de.flapdoodle.embed.mongo'  
}

With that done, you can run the application using:

./gradlew bootRun

If all goes well, you’ll see a bunch of output that ends with something like:

...
2019-12-16 20:19:16.430  INFO 69710 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2019-12-16 20:19:16.447  INFO 69710 --- [           main] c.o.m.m.MongodboauthApplication          : Started MongodboauthApplication in 16.557 seconds (JVM running for 18.032)

Open a second shell and use HTTPie to make a request:

$ http :8080

HTTP/1.1 200
Content-Type: application/hal+json;charset=UTF-8
Date: Mon, 16 Dec 2019 03:21:21 GMT
Transfer-Encoding: chunked

{
    "_links": {
        "self": {
            "href": "http://localhost:8080/profile"
        }
    }
}

The astute out there might be wondering why this request returned a 200 instead of a 404 (since you haven’t actually defined a controller endpoint).

When you included the spring-boot-starter-data-rest dependency, it included the functionality to automatically generate a “hypermedia-based RESTful front end” (as Spring describes it in their docs).

Create a Hypermedia-based RESTful Front End

What is a “hypermedia-based RESTful front end”? It is a REST API that uses Hypertext Application Language (HAL) format to output descriptive JSON. From the HAL Specification GitHub page:

HAL is a simple format that gives a consistent and easy way to hyperlink between resources in your API. Adopting HAL will make your API explorable, and its documentation easily discoverable from within the API itself. In short, it will make your API easier to work with and therefore more attractive to client developers.

Thus it’s a systematic way for a REST API to describe itself to client applications and for the client applications to easily navigate between the various endpoints.

Currently, there’s not much going on with the application, so there isn’t much to see in the response. It’ll make more sense a little later as we add endpoints and data.

Create a Domain Class with Java

To get the ball rolling, you need to create a domain class. Your application is going to be a simple refrigerator inventory application. You’ll be able to add, update, delete, and list all the items in a refrigerator. Each item will have 4 properties: 1) a unique ID assigned by the database, 2) a name, 3) an owner, and 4) and expiration date.

Create a RefrigeratorItem Java class and copy and paste the code below into it.

src/main/java/com/okta/mongodb/mongodboauth/RefrigeratorItem.java

package com.okta.mongodb.mongodboauth;  

import lombok.AllArgsConstructor;  
import lombok.Data;  
import lombok.NoArgsConstructor;  
import org.springframework.data.annotation.Id;  
import org.springframework.data.mongodb.core.mapping.Document;  

import java.util.Date;  

@Document  
@Data  
@AllArgsConstructor  
@NoArgsConstructor  
public class RefrigeratorItem {  

    @Id  
    private String id;  
    private String name;
    private String owner;
    private Date expiration;
}

The @Document annotation is the Spring Data annotation that marks this class as defining a MongoDB document data model. The other annotations are Lombok helpers that save us from the drudgery of creating various getters, setters, and constructors. See more about Lombok at the project’s website.

NOTE: If you’re using an IDE for this tutorial, you may need to install and enable the Lombok plugin.

Create a Spring Data Repository

The next step is to define a Spring Data repository. This is where some pretty incredible auto-magicking happens. You’re going to create a base class that extends the Spring Data class MongoRepository. This superclass includes all the necessary code for reading and writing our domain class to and from the database. Further, you will use the @RepositoryRestResource annotation to tell Spring Boot to automatically generate a REST endpoint for the data using the HAL JSON spec. mentioned above.

Create the repository class shown below.

src/main/java/com/okta/mongodb/mongodboauth/RefrigeratorRepository.java

package com.okta.mongodb.mongodboauth;  

import org.springframework.data.mongodb.repository.MongoRepository;  
import org.springframework.data.rest.core.annotation.RepositoryRestResource;  

@RepositoryRestResource(collectionResourceRel = "fridge", path = "fridge")
public interface RefrigeratorRepository extends MongoRepository<RefrigeratorItem, String> {  
}

You might notice that in the @RepositoryRestResource annotation you are specifying the /fridge URL context for the generated endpoints.

Test the Mongo Repository and Add Some Data

Stop the app (Control-C, if it’s still running) and re-run it.

./gradlew bootRun

Test the home endpoint again.

$ http :8080

HTTP/1.1 200
Content-Type: application/hal+json;charset=UTF-8
Date: Mon, 16 Dec 2019 03:41:05 GMT
Transfer-Encoding: chunked

{
    "_links": {
        "fridge": {
            "href": "http://localhost:8080/fridge{?page,size,sort}",
            "templated": true
        },
        "profile": {
            "href": "http://localhost:8080/profile"
        }
    }
}

This time you’ll see the /fridge endpoint is listed.

Test it out with http :8080/fridge. You should see a response like the one below:

{
    "_embedded": {
        "fridge": []
    },
    "_links": {
        "profile": {
            "href": "http://localhost:8080/profile/fridge"
        },
        "self": {
            "href": "http://localhost:8080/fridge{?page,size,sort}",
            "templated": true
        }
    },
    "page": {
        "number": 0,
        "size": 20,
        "totalElements": 0,
        "totalPages": 0
    }
}

Not a whole lot going on yet, but that’s easily changed. You’re going to use POST requests to add some data to the embedded MongoDB database. But first, you need to configure an application property.

Add the following line to your src/main/resources/application.properties file.

spring.jackson.date-format=MM-dd-yyyy

This tells Spring the expected date format for the expiration property, which will allow it to properly parse the JSON string into a Java date.

Stop (Control-C) and restart the application.

./gradlew bootRun

Now add some data using the following requests.

http POST :8080/fridge name=milk owner=Andrew expiration=01-01-2020 
http POST :8080/fridge name=cheese owner=Andrew expiration=02-10-2020
http POST :8080/fridge name=pizza owner=Andrew expiration=03-30-2020

Check out the inventory now and you should see these new items.

$ http :8080/fridge

HTTP/1.1 200
Content-Type: application/hal+json;charset=UTF-8
Date: Mon, 16 Dec 2019 03:45:23 GMT
Transfer-Encoding: chunked

{
    "_embedded": {
        "fridge": [
            {
                "_links": {
                    "refrigeratorItem": {
                        "href": "http://localhost:8080/fridge/5dae7b4c6a99f01364de916c"
                    },
                    "self": {
                        "href": "http://localhost:8080/fridge/5dae7b4c6a99f01364de916c"
                    }
                },
                "expiration": "01-01-2020",
                "name": "milk",
                "owner": "Andrew"
            },
            {
                "_links": {
                    "refrigeratorItem": {
                        "href": "http://localhost:8080/fridge/5dae7b4d6a99f01364de916d"
                    },
                    "self": {
                        "href": "http://localhost:8080/fridge/5dae7b4d6a99f01364de916d"
                    }
                },
                "expiration": "02-10-2020",
                "name": "cheese",
                "owner": "Andrew"
            },
            {
                "_links": {
                    "refrigeratorItem": {
                        "href": "http://localhost:8080/fridge/5dae7b4f6a99f01364de916e"
                    },
                    "self": {
                        "href": "http://localhost:8080/fridge/5dae7b4f6a99f01364de916e"
                    }
                },
                "expiration": "03-30-2020",
                "name": "pizza",
                "owner": "Andrew"
            }
        ]
    },
    "_links": {
        "profile": {
            "href": "http://localhost:8080/profile/fridge"
        },
        "self": {
            "href": "http://localhost:8080/fridge{?page,size,sort}",
            "templated": true
        }
    },
    "page": {
        "number": 0,
        "size": 20,
        "totalElements": 3,
        "totalPages": 1
    }
}

Notice that the returns JSON gives you the URL for each individual item. If you wanted to delete the first item in the list above, you could run the following request.

http DELETE :8080/fridge/5dae7b4c6a99f01364de916c

The long string,5dae7b4c6a99f01364de916c, is the unique ID for that item. MongoDB doesn’t use sequential integer ID numbers like SQL databases often do. It uses randomly generated UUIDs instead.

If you wanted to update an item, you could use a PUT, as shown below.

http PUT :8080/fridge/5dae7b4f6a99f01364de916e name="old pizza" expiration="03-30-2020" owner="Erin"

Note that with a PUT you have to send data for all the fields, not just the field you want to update, otherwise the omitted fields are set to null. If you just want to update select fields, use a PATCH.

http PATCH :8080/fridge/5dae7b4f6a99f01364de916e owner="Andrew"

With that rather paltry amount of work, you’ve created a MongoDB database model and exposed it to the world using a REST API. Pretty sweet!

The next step is to secure it. The last thing you need is hackers breaking into your house and stealing your pizza and cheese.

Create an OIDC Application for Your Java + MongoDB App

Okta is a software-as-service identity management provider. We provide solutions that make adding authentication and authorization to web applications easy. In this tutorial, you are going to use Okta to add JSON Web Token authentication and authorization to your application using OAuth 2.0 and OpenID Connect (OIDC).

OAuth 2.0 is an authorization protocol (verifying what the client or user is allowed to do) and OIDC is an authentication protocol (verifying the identity of the user) built on top of OAuth 2.0. They are a set of open standards that help ensure your web application’s security is handled safely and effectively. Together they provide a complete authentication and authorization protocol.

They are not, however, implementations. That’s where Okta comes in. Okta will be the identity provider and your Spring Boot app will be the client.

You should have already signed up for a free developer account at Okta. Navigate to the developer dashboard at https://developer.okta.com. If this is your first time logging in, you may need to click the Admin button.

To configure JSON Web Token (JWT) authentication and authorization, you need to create an OIDC application.

From the top menu, click on the Applications button. Click the Add Application button.

Select application type Web and click Next.

Give the app a name. I named mine “Spring Boot Mongo”.

Under Login redirect URIs, add a new URI: https://oidcdebugger.com/debug.

Under Grant types allowed, check Implicit (Hybrid).

The rest of the default values will work.

Click Done.

Leave the page open or take note of the Client ID. You’ll need it in a bit when you generate a token.

To test the REST API, you’re going to use the OpenID Connect Debugger to generate a token. This is why you need to add the login redirect URI and allow the implicit grant type.

Configure Spring Boot for OAuth 2.0

Now you need to update the Spring Boot application to use JWT authentication. First, open your src/main/resources/build.gradle file and uncomment the okta-spring-boot-starter dependency.

dependencies {  
    ...
    implementation 'com.okta.spring:okta-spring-boot-starter:1.3.0'  <-- UNCOMMENT ME
    ... 
}

Next, open your src/main/resources/application.properties file and add your Okta Issuer URI to it. The Issuer URI can be found by opening your Okta developer dashboard. From the top menu, select API and Authorization Servers. Your Issuer URI can be found in the panel in the row for the default authorization server.

spring.jackson.date-format=MM-dd-yyyy  
okta.oauth2.issuer=https://{yourOktaUrl}/oauth2/default

The last update is to add a new class called SecurityConfiguration.

src/main/java/com/okta/mongodb/mongodboauth/SecurityConfiguration.java

package com.okta.mongodb.mongodboauth;  

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

@Configuration  
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {  

    @Override  
    public void configure (HttpSecurity http) throws Exception {  
        http.authorizeRequests()
            .anyRequest().authenticated()
            .and()
            .oauth2ResourceServer()
            .jwt();
    }
}

This simple class configures Spring Boot to authenticate all requests and to use an OAuth 2.0 resource server with JWT authentication and authorization.

Now if you restart the application and try a request, you’ll get a 401.

$ http :8080

HTTP/1.1 401
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Length: 0
...

This is the expected response. You’re REST API is now protected and requires a valid token.

Generate a Token Using the OpenID Connect Debugger

To access your now-protected server, you need a valid JSON Web Token. The OIDC Debugger is a handy page that will allow you to generate a JWT.

Open the OIDC Debugger.

You will need to fill in the following values.

  • Authorization URI: https://{yourOktaUrl}/oauth2/default/v1/authorize
  • Client ID: the Client ID from your Okta OIDC application
  • State: just fill in any non-blank value (this is used in production to help protect against cross-site forgery attacks)
  • Response type: check box for token

The rest of the default values should work. Scroll down to the bottom and click Send Request.

If all went well, you will see your brand new access token.

Copy the token to your clipboard and store it in a shell variable like so:

TOKEN=eyJraWQiOiJrQkNxZ3o1MmQtOUhVSl94c0x4aGtzYlJxUDVD...

Now you can make authenticated and authorized requests.

$ http :8080 "Authorization: Bearer $TOKEN"

HTTP/1.1 200
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
...

{
    "_links": {
        "fridge": {
            "href": "http://localhost:8080/fridge{?page,size,sort}",
            "templated": true
        },
        "profile": {
            "href": "http://localhost:8080/profile"
        }
    }
}

In this tutorial, you created a Java data model class and mapped it to a MongoDB domain document using Spring Data annotations. You used a simple embedded MongoDB database as the datastore. You used Spring Boot to quickly and easily expose your data model via a REST API. Finally, you secured the REST API using Okta and Okta’s Spring Boot Starter.

The source code for this tutorial is available on GitHub at oktadeveloper/okta-java-mongodb-example.

Spring Data REST Tutorial: Developing RESTful APIs with Ease

Spring Data REST Tutorial: Developing RESTful APIs with Ease

In this article, you will learn how to develop REST APIs with ease by using Spring Data REST and Spring Boot together. Throughout the article, you will scaffold a new Spring Boot application, create a JPA entity, and use Spring Data REST to provide some basic operations over it. Besides that, you will also learn how to validate the data your API is dealing with and how to secure the application.

In this article, you will learn how to develop REST APIs with ease by using Spring Data REST and Spring Boot together. Throughout the article, you will scaffold a new Spring Boot application, create a JPA entity, and use Spring Data REST to provide some basic operations over it. Besides that, you will also learn how to validate the data your API is dealing with and how to secure the application. If you need, you can check the final code developed throughout the article in this GitHub repository.

Introduction

Developing RESTful APIs is a task that most developers have faced at least once in their lives. Nowadays, with the increasing popularity of front-end frameworks like Angular, React, and Vue.js and with the mass adoption of smartphones, RESTful APIs became the most popular approach backend developers leverage to provide an interface for their applications. Knowing that, the Spring community, as you will see in this article, worked hard to build Spring Data REST, a framework that facilitates the life of developers while creating these APIs.

Spring Data REST builds on top of Spring Data repositories, analyzes your application’s domain model and exposes hypermedia-driven HTTP resources for aggregates contained in the model. — Spring Data REST
Together with Spring Boot, Spring Data REST enables developers to create production-ready APIs in a matter of minutes. Want some proof? Keep reading!
Spring Data REST builds on top of Spring Data repositories, analyzes your application’s domain model and exposes hypermedia-driven HTTP resources for aggregates contained in the model. — Spring Data REST## What You Will Build

To learn about Spring Data REST, you will build a simple RESTful API that exposes CRUD (Create, Retrieve, Update, and Delete) operations over an entity called Ad. The Ad entity, in this case, stands for advertisements and will be used to represent some product or service that a user is trying to sell. As such, your API will enable users to manipulate ads that hold information like title, owner, description, and price.

As mentioned before, while developing this RESTful API, you will learn how to:

  • provide an API with different basic operations over the entity created (Ad);
  • validate data;
  • and secure the API.
Prerequisites

To follow along with this article, you will need Java 8 or greater (up to Java 11) installed in your machine. If you don’t have Java yet, please, refer to the official documentation to install it in your development environment.

Besides Java, you will need some IDE (Integrated Development Environment) to help you in the development process. For that, there are a few great choices available out there (e.g., IntelliJ IDEA, Eclipse, and NetBeans). If you don’t have a preferred one yet, try using the community version of IntelliJ IDEA.

Scaffolding an App with Spring Boot

For starters, before diving into developing your RESTful API, you will need to scaffold your new application. To do so, open the Spring Initializr website in your browser and fill the presented form as follows:

  • provide an API with different basic operations over the entity created (Ad);
  • validate data;
  • and secure the API.

Then, on the Dependencies section, use the search for dependencies field to add five dependencies:

  • provide an API with different basic operations over the entity created (Ad);
  • validate data;
  • and secure the API.

After filling this page, click on the Generate Project button to download your new application. When done, you will have to extract the downloaded .zip file and move the extracted folder to a desired location.

If you are on a Unix-like operating system, you can also unzip the downloaded file with the following command:

unzip ads.zip


With that in place, use your IDE to open your new project. For example, if you are using IntelliJ IDEA, you can open the project by issuing the following command from the project root:

idea .


Creating RESTful APIs for JPA Entities with Spring Data REST

Now that you have scaffolded a new Spring Boot project and that you have opened it in your IDE, the first thing you will do is to create the JPA entity that you will expose with the help of Spring Data REST. To do so, create a new class called Ad inside the com.auth0.ads package, and add the following code to it:

// ./src/main/java/com/auth0/ads/Ad.java
package com.auth0.ads;

import lombok.EqualsAndHashCode;
import lombok.Getter;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.math.BigDecimal;

@Entity
@Getter
@EqualsAndHashCode
public class Ad {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    public Long id;

    public String owner;
    public String title;
    public String description;
    public BigDecimal price;

    protected Ad() {
    }

    public Ad(String owner, String title, String description, BigDecimal price) {
        this.owner = owner;
        this.title = title;
        this.description = description;
        this.price = price;
    }
}


As you can see from the code above, you are defining a new JPA @Entity called Ad to hold five different fields. Most of these fields are self-explanatory (their names should be enough for you to understand what they will hold). The only one that might need some explanation is the Long id field. As you can see, this field is marked with two annotations:

  • provide an API with different basic operations over the entity created (Ad);
  • validate data;
  • and secure the API.

After creating the Ad JPA entity, you will need to focus on creating a class that will allow you to interface with the database. Also, you will need to map the database operations into RESTful API endpoints so external clients can use them. Sounds like a lot of work? On the contrary! This is where Spring Data REST shines.

To achieve both things described in the last paragraph (map to the database and expose the operations in your API), you will need to do only one thing. You will need to create an interface called AdRepository (inside the com.auth0.ads package) and add the following code to it:

// ./src/main/java/com/auth0/ads/AdRepository.java
package com.auth0.ads;

import org.springframework.data.repository.PagingAndSortingRepository;

public interface AdRepository extends PagingAndSortingRepository<Ad, Long> {
}


That’s it! With this interface in place, you are ready to run your application and start issuing requests to it. Magic? Indeed it looks like so. However, what is happening here is that Spring Boot (a framework that is bound to the “convention over configuration” strategy) and Spring Data REST identify that you defined an interface that extends PagingAndSortingRepository and work together to create a bunch of endpoints for you.

If you check the JavaDocs of the PagingAndSortingRepository interface, you will see that this interface is an “extension of [CrudRepository](https://docs.spring.io/spring-data/data-commons/docs/current/api/org/springframework/data/repository/PagingAndSortingRepository.html "CrudRepository") to provide additional methods to retrieve entities using pagination and sorting.

More specifically, as described on the Core Concepts documentation of the Spring Data library, together, PagingAndSortingRepository and CrudRepository add the following methods to your application to allow it to manipulate your entities in a SQL database:

  • provide an API with different basic operations over the entity created (Ad);
  • validate data;
  • and secure the API.

Then, as you are using Spring Data REST, this library creates RESTful endpoints to exposes all the methods defined by PagingAndSortingRepository and CrudRepository.

To see all this magic in action, you can either press the play button in your IDE, or you can issue the following code in a terminal (as long as this terminal is pointing to the project root):

./gradlew bootRun


After the application starts executing (it will take a few seconds for it to be ready), you can start sending HTTP requests to its endpoints. To do so, you can use a graphical HTTP client like Postman, or you can open a new terminal and use a tool like curl. The next subsection will show you how to use curl to issue some requests to your new application. However, translating them to Postman or a similar client should not be a problem.

Spring Data REST builds on top of Spring Data repositories, analyzes your application’s domain model and exposes hypermedia-driven HTTP resources for aggregates contained in the model. — Spring Data REST### Issuing requests to the Spring Data REST endpoints

For starters, to confirm that your application is working as expected, you can issue the following request (you might need to run the command below in a new terminal or, as mentioned, you can use Postman):

# check if there are ads persisted
curl http://localhost:8080/ads


Running this command will make your API return all the ads persisted on the database (which will be none: "ads" : [ ]), and a few other things like:

  • provide an API with different basic operations over the entity created (Ad);
  • validate data;
  • and secure the API.

As you haven’t created any entity yet, the results won’t be very interesting. So, the next thing you can do is to issue a request to insert a new ad in your API:

# insert a new ad
curl -X POST -H "Content-Type:application/json" -d '{
    "owner": "me",
    "title": "My Car",
    "description": "Pretty awesome but, unfortunately, I have to sell it.",
    "price": 225599.99
}' http://localhost:8080/ads 


Issuing this command will output the details of the ad created (all the properties above) along with a link to the ad itself (self.href). If you use this link in a curl command, you will retrieve the details of the ad:

# retrieve the new ad
curl http://localhost:8080/ads/1


Now, if you run the command to see the list of persisted ads again:

curl http://localhost:8080/ads


You will see that the ads array includes your new ad and that the page properties (size, totalElements, etc.) are updated accordingly.

After that, if you want, you can delete the ad by issuing the following command:

curl -X DELETE http://localhost:8080/ads/1


Incredible, huh? After scaffolding your application, you just had to create two things (the Ad entity and the AdRepository interface), and you got a runnable RESTful API.

Data Validation

After seeing Spring Data REST in action, you might be thinking: “Ok, this library does facilitate creating RESTful APIs, but I need to validate the data before persisting it to the database. Will it be easy to validate my data?” The answer to that is yes! By using the Bean Validation Java specification, validating your data is as easy as adding some annotations to the fields in your JPA entity.

Spring Data REST builds on top of Spring Data repositories, analyzes your application’s domain model and exposes hypermedia-driven HTTP resources for aggregates contained in the model. — Spring Data REST> Spring Data REST builds on top of Spring Data repositories, analyzes your application’s domain model and exposes hypermedia-driven HTTP resources for aggregates contained in the model. — Spring Data REST
If you want some proof, open the Ad class and update its code to look like this:

// ./src/main/java/com/auth0/ads/Ad.java
package com.auth0.ads;

import lombok.EqualsAndHashCode;
import lombok.Getter;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.math.BigDecimal;

@Entity
@Getter
@EqualsAndHashCode
public class Ad {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    public Long id;

    @NotNull(message = "Who is the owner of this ad?")
    public String owner;

    @NotNull(message = "Please, inform a title for your ad.")
    @Size(min = 5, max = 140, message = "Titles must have between {min} and {max} characters.")
    public String title;

    @NotNull(message = "Please, inform a description for your ad.")
    @Size(min = 5, max = 500, message = "Titles must have between {min} and {max} characters.")
    public String description;

    @Min(message = "Price cannot be negative", value = 0)
    public BigDecimal price;

    protected Ad() {}

    public Ad(String owner, String title, String description, BigDecimal price) {
        this.owner = owner;
        this.title = title;
        this.description = description;
        this.price = price;
    }
}


On the new version of this entity, you are importing and using three new annotations:

  • provide an API with different basic operations over the entity created (Ad);
  • validate data;
  • and secure the API.

After updating the Ad class, you can rerun your application (by issuing ./gradlew bootRun or by pressing the start button on your IDE). When the app finishes booting, you can issue requests like the following to confirm that the restrictions are working:

# won't work, too few characters on the title property
curl -X POST -H "Content-Type:application/json" -d '{
    "owner": "me",
    "title": "Abc",
    "description": "Pretty awesome but, unfortunately, I have to sell it.",
    "price": 500
}' http://localhost:8080/ads 

# won't work, negative price
curl -X POST -H "Content-Type:application/json" -d '{
    "owner": "me",
    "title": "My Car",
    "description": "Pretty awesome but, unfortunately, I have to sell it.",
    "price": -10
}' http://localhost:8080/ads 

# won't work, no description
curl -X POST -H "Content-Type:application/json" -d '{
    "owner": "me",
    "title": "My Car",
    "price": 500
}' http://localhost:8080/ads 

# will work, all properties contain valid values
curl -X POST -H "Content-Type:application/json" -d '{
    "owner": "me",
    "title": "My Car",
    "description": "Pretty awesome but, unfortunately, I have to sell it.",
    "price": 500
}' http://localhost:8080/ads 


While issuing the invalid requests above (i.e., the first three), you will see that your app returns a response that looks like this:

{
  "timestamp": "2019-02-19T18:35:52.094+0000",
  "status": 500,
  "error": "Internal Server Error",
  "message": "Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction",
  "path":"/ads"
}


Although this response is sufficient for the calling client to understand that something went wrong, it doesn’t help to identify what the problem was. To fix that, you can create a new class called RestExceptionHandler inside the com.auth0.ads package and add the following code to it:

// ./src/main/java/com/auth0/ads/RestExceptionHandler.java
package com.auth0.ads;

import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.TransactionSystemException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import javax.persistence.RollbackException;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.stream.Collectors;

@Order(Ordered.HIGHEST_PRECEDENCE)
@ControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {
    @ExceptionHandler(TransactionSystemException.class)
    protected ResponseEntity<List<String>> handleTransactionException(TransactionSystemException ex) throws Throwable {
        Throwable cause = ex.getCause();
        if (!(cause instanceof RollbackException)) throw cause;
        if (!(cause.getCause() instanceof ConstraintViolationException)) throw cause.getCause();
        ConstraintViolationException validationException = (ConstraintViolationException) cause.getCause();
        List<String> messages = validationException.getConstraintViolations().stream()
                .map(ConstraintViolation::getMessage).collect(Collectors.toList());
        return new ResponseEntity<>(messages, HttpStatus.BAD_REQUEST);
    }
}


With this class, you are creating and registering a global exception handler (ResponseEntityExceptionHandler) for your application. Then, inside this handler, you are defining a method that will catch instances of TransactionSystemException to unwrap the real problem (ex.getCause()). If the unwrapped exception is not an instance of RollbackException, then this method throws the underlying exception.

If the unwrapped problem is an instance of RollbackException, then this method unwraps the cause one more time (cause.getCause()). This time, the goal is to check if the second unwrap will result in an instance of ConstraintViolationException. If this is not the case, the unwrapped result is thrown. If this is the case, this method generates a list of messages to send back as a response.

After creating this exception handler, if you restart your app and issue bogus requests, you will get some friendly messages. For example, if you issue the following request:

curl -X POST -H "Content-Type:application/json" -d '{
    "owner": "me",
    "title": "My Car",
    "price": -10
}' http://localhost:8080/ads


You will get a list of messages saying that the “price cannot be negative” and to “please, inform a description for your ad.” Not to hard too validate your data in this stack, huh?

Securing Spring Data REST APIs

Cool, with a minimum amount of effort you were able to create a backend application that exposes a bunch of RESTful APIs and, in a couple of minutes, you were able to extend it with data validation. However, what about security? There is no production-ready RESTful API without security, right?

Luckily, adding a security layer to your current stack is not hard either. To do this, you will add the Spring Security framework on top of your application and will use it with Auth0 to generate and validate access tokens.

For starters, you will need to open the build.gradle file of your project and update it as follows:

// ... leave everything else untouched ...

dependencies {
    // ... leave the other dependencies untouched ...
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.1.2.RELEASE'
}


Here, you are making your project import two new dependencies. The first one is the Spring Boot extension for the Spring Security framework. The second one is a library that facilitates configuring OAuth (the authorization framework) which is used by Auth0.

After importing these new dependencies, you will have to:

  1. Add a couple of properties to the application.properties file to configure these dependencies.
  2. Create a class to define how the endpoints will be accessible.

However, before doing so, you will need to configure an Auth0 API to represent you RESTful backend.

Spring Data REST builds on top of Spring Data repositories, analyzes your application’s domain model and exposes hypermedia-driven HTTP resources for aggregates contained in the model. — Spring Data REST
After signing in (or signing up, if you didn’t have an Auth0 account yet), head to the APIs section of your Auth0 Dashboard and click on the Create API button. When you click on this button, Auth0 will show you a dialog where it will ask you for three things:

  • provide an API with different basic operations over the entity created (Ad);
  • validate data;
  • and secure the API.

After filling this form, click on the Create button. Then, back to your project, open the application.properties file and add the following to it:

# ./src/main/resources/application.properties
security.oauth2.resource.jwk.keySetUri=https://${YOUR-AUTH0-DOMAIN}/.well-known/jwks.json
security.oauth2.resource.id=https://ads-api


You will have to replace the ${YOUR-AUTH0-DOMAIN} placeholder with your Auth0 domain (e.g., blog-samples.auth0.com), and that you will have to make sure the resource.id property points to the identifier you used while registering your API.

Spring Data REST builds on top of Spring Data repositories, analyzes your application’s domain model and exposes hypermedia-driven HTTP resources for aggregates contained in the model. — Spring Data REST
With that in place, you will create a new class called SecurityConfig inside the com.auth0.ads package and add the following code to it:

// ./src/main/java/com/auth0/ads/SecurityConfig.java
package com.auth0.ads;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;

@Configuration
@EnableResourceServer
public class SecurityConfig extends ResourceServerConfigurerAdapter {
    @Value("${security.oauth2.resource.id}")
    private String resourceId;

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .mvcMatchers("/ads/**").authenticated();
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources.resourceId(resourceId);
    }
}


The code for this class is quite simple. As you can see, you are using some Spring Boot annotations (like @Configuration and @Value) to make Spring configure this class while bootstrapping the app. Also, you are defining two configure methods to define how the Spring Security framework must behave. The first configure method is telling the framework that all calls to /ads/** must be authenticated. The second one is telling the ResourceServerSecurityConfigurer to use, as the resourceId, the value you added to the security.oauth2.resource.id property.

After creating your Auth0 API and making the changes described above, you can restart your application one more time and issue the following request:

curl http://localhost:8080/ads


This request (or any other aiming endpoints starting with /ads that do not carry an access token) will result in the following response:

{
  "error":"unauthorized",
  "error_description":"Full authentication is required to access this resource"
}


To be able to issue valid (or authenticated) requests, you will need an access token. The process of getting an access token will depend on what type of client you are dealing with. This is out of scope here but, if you are dealing with a SPA application (like those created with React, Angular, and Vue.js), you can check this article to learn how to proceed. If you are dealing with some other type of client (e.g., regular web application or native application), check the Auth0’s docs for more info.

Nevertheless, to see the whole thing in action, you can head back to your Auth0 Dashboard, open the API you just created, and move to the Test section. On this section, you will see a button called Copy Token that will provide you a temporary token that you can use to test your API.

So, click on this button and then use your client (or the terminal) to issue an HTTP request to your API with the test token:

# use the token copied to set the TOKEN variable
TOKEN=eyJ...DRA

# issue an authenticated HTTP request
curl -H 'Authorization: Bearer '$TOKEN http://localhost:8080/ads


If everything works as expected, you will be able to consume your API endpoints again. Awesome, right?

Conclusion

In this article, you learned about how easy it is to develop RESTful API with Spring Data REST and Spring Boot. More specifically, you started by using the Spring Initializr website to scaffold a new application. After that, you used Spring Data REST to expose API endpoints to manipulate a JPA entity. Then, in the end, you learned how to validate data and how to secure your API.

With that in place, you are ready to start developing production-ready, secure APIs with Spring Boot, Spring Data REST, and Auth0.

Spring Data REST builds on top of Spring Data repositories, analyzes your application’s domain model and exposes hypermedia-driven HTTP resources for aggregates contained in the model. — Spring Data REST
*Originally published by Bruno Krebs at *https://auth0.com

Learn More

Build a Basic App with Spring Boot and JPA using PostgreSQL

Build a Simple CRUD App with Spring Boot and Vue.js

Building A REST API With MongoDB, Mongoose, And Node.js

Creating RESTful APIs with NodeJS and MongoDB Tutorial

Understanding the basics of RESTful APIs

GraphQL Tutorial: Understanding Spring Data JPA/SpringBoot

Complete Java Masterclass

Complete Step By Step Java For Testers

Java Web Service Complete Guide - SOAP + REST + Buide App

Selenium WebDriver with Java - Basics to Advanced& Interview

How to Create RESTful API service with Spring Boot

How to Create RESTful API service with Spring Boot

This post we'll Building a RESTful service with Spring Boot. You will use those values throughout the rest of this sample.

This is a getting started guide for people interested In spring.

Why Spring Boot for Your Microservices

Spring Boot enables building production-ready applications quickly and provides non-functional features: Embedded servers which are easy to deploy with the containers. It helps in monitoring the multiples components. It helps in configuring the components externally.

Getting Started

Create a new package called clients in the root of your project src as shown below

inside this package we will have 4 classes ;

Client.java
ClientController.java
ClientRepository.java
ClientService.java

the client class defines the object class and will aslo be our Entity class that will define the clients table in the database. To handle database transactions we will use the spring data jpa package, this is installed via the pom.xml

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

we will also need the spring web package that carries functions necessary for handling HTTP requests

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

finally installing the mysql DB drivers

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

after adding the lines above to your dependencies section in the pom.xml run mvn clean install

package com.example.sample.demo.clients;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
**
@Entity
public class Client {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long  id;
    private String name;
    private String email;

    public Client(){

    }


    public Client(Long id, String name, String email){
        super();
        this.id = id;
        this.name = name;
        this.email = email;

    }


    /**
     _ @return Long return the id
     _/
    public Long getId() {
        return id;
    }

    /**
     **_ @param id the id to set
     _**/
    public void setId(Long id) {
        this.id = id;
    }

    /**
     _ @return String return the name
     _/
    public String getName() {
        return name;
    }

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

  **
   _
  _
    public String getEmail() {
        return email;
    }

    /_*
     _ @param email the email to set
     */
    public void setEmail(String email) {
        this.email = email;
    }

}

The code above decribes the Client.java class not moost of the annotations describing the db datatypes have been extended from the java persistence api (jpa) next we will define our ClientRepository.java

ClientRepository

package com.example.sample.demo.clients;

import java.util.List;

import org.springframework.data.repository.CrudRepository;

public interface ClientRepository extends CrudRepository<Client, Long> {

    List<Client> findByName(String name);
}

ClientRepository extends the CrudRepository interface. The type of entity and ID that it works with,Client and Long, are specified in the generic parameters on CrudRepository. By extending CrudRepository, ClientRepositoryinherits several methods for working with Client persistence, including methods for saving, deleting, and finding Client entities.

Spring Data JPA also allows you to define other query methods by simply declaring their method signature. In the case of ClientRepository, this is shown with a findByName() method.

In a typical Java application, you’d expect to write a class that implements ClientRepository. But that’s what makes Spring Data JPA so powerful: You don’t have to write an implementation of the repository interface. Spring Data JPA creates an implementation on the fly when you run the application.

ClientService

The service hadles most of the logic and responses are directly called in the Controller class. The logic below could have well been handled directly in the controller class.

package com.example.sample.demo.clients;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class ClientService {

    @Autowired
    private ClientRepository clientRepository;



    public List<Client> getAllClients() {

        List<Client> Clients = new ArrayList<>();
        clientRepository.findAll().forEach(Clients::add);
        return Clients;

    }

    public Optional<Client> findParticularClient(Long id) {

        return clientRepository.findById(id);

    }

    public Boolean createClient(Client client){
        try{
            clientRepository.save(client);
            return true;
        }
        catch(Exception e){
            return false;
        }
    }


    public boolean deleteClientRecord(Long id){
        try{
            clientRepository.deleteById(id);
            return true;
        }
        catch(Exception e){
            return false;
        }
    }


    public boolean updateClientRecord(Client client, Long clientId){
        try{
            clientRepository.save(client);
            return true;
        }
        catch(Exception e){
            return false;
        }
    }




}

ClientController

In Spring’s approach to building web sites, HTTP requests are handled by a controller. You can easily identify these requests by the @Controller

@ResponseBody Binds the http paramenters to variables @Request Method dictates the method handled by the particular route A thing to note is that all responses in the @Controller class are Json Encoded by default this is one of the many adavantages of the spring framework.

package com.example.sample.demo.clients;

import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/_*
 _ ClientController
 */

@RestController
public class ClientController {

    @Autowired
    private ClientService clientService;

    @RequestMapping(value="/clientDetails", method = RequestMethod.GET)
    public Optional getClient(@RequestParam("id") Long id) {
        return clientService.findParticularClient(id);

    }

    @RequestMapping(value="/clients", method= RequestMethod.GET)
    public List getAllClients() {
        return clientService.getAllClients();
    }

    @RequestMapping(value="/clientCreate", method = RequestMethod.POST)
    public void createClient(@RequestBody Client client) {
        clientService.createClient(client);
    }

    @RequestMapping(value="/clientDelete", method = RequestMethod.DELETE)
    public void deleteClient(@RequestParam("id") Long id) {
        clientService.deleteClientRecord(id);
    }

    @RequestMapping(value="/clientUpdate", method = RequestMethod.PUT)
    public void updateClient(@RequestBody Client client, @RequestParam("id") Long clientId) {
        clientService.updateClientRecord(client, clientId);
    }

}

Database Connection

We are now done with our Rest application we would now like to test it but first we need to connect to the database. find the application.properties file in your main/resources folder

server.port=8090
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://localhost:3306/{your-database-name}?useSSL=false
spring.datasource.username=root
spring.datasource.password=
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect

in the same folder create a schema.sql file and add the following

CREATE TABLE client (
    id int AUTO_INCREMENT NOT NULL,
    name VARCHAR (200) NULL,
    email VARCHAR (100) NULL,
    PRIMARY KEY (id)
)

when running the application it is supposed to migrate and map the schema onto your database. or you could just create it the old fashioned way its upto you.

once everything is set up run the application

mvn spring-boot:run

Testing

Using Postman

Thank you for reading!