What is the best programming language for Hacking?

How to build a secure Grails 4 Application using Spring Security Core

How to build a secure Grails 4 Application using Spring Security Core

In this Grails 4 tutorial, we will show you how to build a secure Grails 4 application using Spring Security Core Plugin. We will add the login and register function to the Grails 4 application.

In this Grails 4 tutorial, we will show you how to build a secure Grails 4 application using Spring Security Core Plugin. We will add the login and register function to the Grails 4 application. The purpose of using the Spring Security plugin has simplified the integration of Spring Security Java (we have written this tutorial). The usage of this Grails 4 Spring Security plugin similar to Grails 2 or 3, but there's a lot of updates on the Spring Security code and its dependencies to match the compatibilities.

Table of Contents:

The flow of this tutorial is very simple. We have a secure Product list that only accessible to the authorized user with ROLE_USER and Product CRUD for the user with ROLE_ADMIN. Any access to this Product resource will be redirected to the Login Page as default (if no authorized user). In the login page, there will be a link to the registration page that will register a new user.

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

  1. JDK 8
  2. Grails 4
  3. Grails Spring Security Core Plugin
  4. Terminal or Command Line
  5. Text Editor or IDE

Before starting the main steps, make sure you have downloaded and installed the latest Grails 4. In Mac, we are using the SDKMan. For that, type this command in the Terminal to install SDKMan.

curl -s https://get.sdkman.io | bash

Follow all instructions that showed up during installation. Next, open the new Terminal window or tab then type this command.

source "$HOME/.sdkman/bin/sdkman-init.sh"

Now, you can install Grails 4 using SDKMan.

sdk install grails 4.0.1

Set that new Grails 4 as default. To check the Grails version, type this command.

grails -version
| Grails Version: 4.0.1
| JVM Version: 1.8.0_92
Create Grails 4 Application

Same as previous Grails version, to create a new Grails application, simply type this command.

grails create-app com.djamware.gadgethouse

That command will create a new Grails 4 application with the name "gadgethouse" with the package name "com.djamware". Next, go to the newly created project folder then enter the Grails 4 interactive console.

cd ./gadgethouse
grails

In the Grails interactive console, type this command to run this Grails application for the first time.

grails> run-app

Here's the new Grails 4 look like.

Install Grails Spring Security Core Plugin

Now, we will install and configure Grails Spring Security Core Plugin. For the database, we keep H2 in-memory database (You can change to other relational database configuration). To install the Grails Spring Security Core Plugin, open and edit build.gradle then add this dependency in dependencies array.

compile "org.grails.plugins:spring-security-core:4.0.0.RC2"

Next, stop the running Grails application using this command in the Grails interactive console.

stop-app

Compile the Grails application, to install the Spring Security Core plugin.

compile
Create User, Role, and Product Domain Class

We will use the Grails s2-quickstart command to create User and Role domain class for authentication. Type this command in Grails interactive console.

s2-quickstart com.djamware User Role

That command will create User and Role domain class with the package name "com.djamware". Next, open and edit grails-app/domain/com/djamware/Role.groovy to add default field after the bracket closing when this domain calls.

String toString() {
  authority
}

Next, create a domain class using regular Grails command for Product.

create-domain-class com.djamware.Product

Next, we need to create an additional field in the user domain class. For that, open and edit grails-app/domain/com/djamware/User.groovy then add a fullName field after the password field.

String fullname

Also, add a constraint for that field.

static constraints = {
    password nullable: false, blank: false, password: true
    username nullable: false, blank: false, unique: true
    fullname nullable: false, blank: false
}

Next, open and edit grails-app/domain/com/djamware/Product.groovy then replace that domain class with these Groovy codes.

package com.djamware

class Product {

    String prodCode
    String prodName
    String prodModel
    String prodDesc
    String prodImageUrl
    String prodPrice

    static constraints = {
        prodCode nullable: false, blank: false
        prodName nullable: false, blank: false
        prodModel nullable: false, blank: false
        prodDesc nullable: false, blank: false
        prodImageUrl nullable: true
        prodPrice nullable: false, blank: false
    }

    String toString() {
        prodName
    }
}
Create CustomUserDetailsService

Because we have added a field in the previous User domain class, we need to create a custom UserDetails. Create a new Groovy file src/main/groovy/com/djamware/CustomUserDetails.groovy then add these lines of Groovy codes that add full name field to the Grails UserDetails.

package com.djamware

import grails.plugin.springsecurity.userdetails.GrailsUser
import org.springframework.security.core.GrantedAuthority

class CustomUserDetails extends GrailsUser {

   final String fullname

   CustomUserDetails(String username, String password, boolean enabled,
                 boolean accountNonExpired, boolean credentialsNonExpired,
                 boolean accountNonLocked,
                 Collection<GrantedAuthority> authorities,
                 long id, String fullname) {
      super(username, password, enabled, accountNonExpired,
            credentialsNonExpired, accountNonLocked, authorities, id)

      this.fullname = fullname
   }
}

Next, type this command in the Grails interactive console to create a new Grails service.

grails> create-service com.djamware.CustomUserDetails

Open that file then replace all Groovy codes with these codes.

package com.djamware

import grails.plugin.springsecurity.SpringSecurityUtils
import grails.plugin.springsecurity.userdetails.GrailsUserDetailsService
import grails.plugin.springsecurity.userdetails.NoStackUsernameNotFoundException
import grails.gorm.transactions.Transactional
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.UsernameNotFoundException

class CustomUserDetailsService implements GrailsUserDetailsService {

   /**
    * Some Spring Security classes (e.g. RoleHierarchyVoter) expect at least
    * one role, so we give a user with no granted roles this one which gets
    * past that restriction but doesn't grant anything.
    */
   static final List NO_ROLES = [new SimpleGrantedAuthority(SpringSecurityUtils.NO_ROLE)]

   UserDetails loadUserByUsername(String username, boolean loadRoles)
         throws UsernameNotFoundException {
      return loadUserByUsername(username)
   }

   @Transactional(readOnly=true, noRollbackFor=[IllegalArgumentException, UsernameNotFoundException])
   UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

      User user = User.findByUsername(username)
      if (!user) throw new NoStackUsernameNotFoundException()

      def roles = user.authorities

      // or if you are using role groups:
      // def roles = user.authorities.collect { it.authorities }.flatten().unique()

      def authorities = roles.collect {
         new SimpleGrantedAuthority(it.authority)
      }

      return new CustomUserDetails(user.username, user.password, user.enabled,
            !user.accountExpired, !user.passwordExpired,
            !user.accountLocked, authorities ?: NO_ROLES, user.id,
            user.fullname)
   }
}

Next, register that new CustomUserDetailsService in the grails-app/conf/spring/resources.groovy.

import com.djamware.UserPasswordEncoderListener
import com.djamware.CustomUserDetailsService
// Place your Spring DSL code here
beans = {
    userPasswordEncoderListener(UserPasswordEncoderListener)
    userDetailsService(CustomUserDetailsService)
}
Override Login Auth View

We will customize the login page to make UI better and add a link to the Register page. For that, create a login folder under views then create an auth.gsp file in that folder. Open and edit grails-app/views/login/auth.gsp then add these lines of GSP HTML tags.

<html>
<head>
    <meta name="layout" content="${gspLayout ?: 'main'}"/>
    <title><g:message code='springSecurity.login.title'/></title>
</head>

<body>
    <div class="row">
      <div class="col-sm-9 col-md-7 col-lg-5 mx-auto">
        <div class="card card-signin my-5">
          <div class="card-body">
            <h5 class="card-title text-center">Please Login</h5>
            <g:if test='${flash.message}'>
                <div class="alert alert-danger" role="alert">${flash.message}</div>
            </g:if>
            <form class="form-signin" action="${postUrl ?: '/login/authenticate'}" method="POST" id="loginForm" autocomplete="off">
              <div class="form-group">
                  <label for="username">Username</label>
                <input type="text" class="form-control" name="${usernameParameter ?: 'username'}" id="username" autocapitalize="none"/>
              </div>

              <div class="form-group">
                  <label for="password">Password</label>
                <input type="password" class="form-control" name="${passwordParameter ?: 'password'}" id="password"/>
                <i id="passwordToggler" title="toggle password display" onclick="passwordDisplayToggle()">&#128065;</i>
              </div>

              <div class="form-group form-check">
                  <label class="form-check-label">
                      <input type="checkbox" class="form-check-input" name="${rememberMeParameter ?: 'remember-me'}" id="remember_me" <g:if test='${hasCookie}'>checked="checked"</g:if>/> Remember me
                </label>
              </div>
              <button id="submit" class="btn btn-lg btn-primary btn-block text-uppercase" type="submit">Sign in</button>
              <hr class="my-4">
              <p>Don't have an account? <g:link controller="register">Register</g:link></p>
            </form>
          </div>
        </div>
      </div>
    </div>
    <script type="text/javascript">
        document.addEventListener("DOMContentLoaded", function(event) {
            document.forms['loginForm'].elements['username'].focus();
        });
        function passwordDisplayToggle() {
            var toggleEl = document.getElementById("passwordToggler");
            var eyeIcon = '\u{1F441}';
            var xIcon = '\u{2715}';
            var passEl = document.getElementById("password");
            if (passEl.type === "password") {
                toggleEl.innerHTML = xIcon;
                passEl.type = "text";
            } else {
                toggleEl.innerHTML = eyeIcon;
                passEl.type = "password";
            }
        }
    </script>
</body>
</html>

Next, we will make this login page as default or homepage when the application opens in the browser. For that, open and edit grails-app/controllers/UrlMappings.groovy then replace this line.

"/"(view: "index")

With this line.

"/"(controller:'login', action:'auth')
Add User Info and Logout to the Navbar

Now, we have to implement POST logout to the Navbar. This logout button active when the user logged in successfully along with user info. For that, modify grails-app/views/layout/main.gsp then replace all GSP HTML tags with these.

<!doctype html>
<html lang="en" class="no-js">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
    <title>
        <g:layoutTitle default="Grails"/>
    </title>
    <meta name="viewport" content="width=device-width, initial-scale=1"/>
    <asset:link rel="icon" href="favicon.ico" type="image/x-ico"/>

    <asset:stylesheet src="application.css"/>

    <g:layoutHead/>
</head>

<body>

<nav class="navbar navbar-expand-lg navbar-dark navbar-static-top" role="navigation">
    <a class="navbar-brand" href="/#"><asset:image src="grails.svg" alt="Grails Logo"/></a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarContent" aria-controls="navbarContent" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
    </button>

    <div class="collapse navbar-collapse" aria-expanded="false" style="height: 0.8px;" id="navbarContent">
        <ul class="nav navbar-nav ml-auto">
            <g:pageProperty name="page.nav"/>
            <sec:ifLoggedIn>
              <li class="nav-item dropdown">
                  <a class="nav-link dropdown-toggle" href="#" id="navbardrop" data-toggle="dropdown">
                    <sec:loggedInUserInfo field='fullname'/>
                  </a>
                  <div class="dropdown-menu navbar-dark">
                    <g:form controller="logout">
                      <g:submitButton class="dropdown-item navbar-dark color-light" name="Submit" value="Logout" style="color:gray" />
                    </g:form>
                  </div>
              </li>
            </sec:ifLoggedIn>
        </ul>
    </div>

</nav>

<div class="container">
    <g:layoutBody/>
</div>

<div class="footer row" role="contentinfo">
    <div class="col">
        <a href="http://guides.grails.org" target="_blank">
            <asset:image src="advancedgrails.svg" alt="Grails Guides" class="float-left"/>
        </a>
        <strong class="centered"><a href="http://guides.grails.org" target="_blank">Grails Guides</a></strong>
        <p>Building your first Grails app? Looking to add security, or create a Single-Page-App? Check out the <a href="http://guides.grails.org" target="_blank">Grails Guides</a> for step-by-step tutorials.</p>

    </div>
    <div class="col">
        <a href="http://docs.grails.org" target="_blank">
            <asset:image src="documentation.svg" alt="Grails Documentation" class="float-left"/>
        </a>
        <strong class="centered"><a href="http://docs.grails.org" target="_blank">Documentation</a></strong>
        <p>Ready to dig in? You can find in-depth documentation for all the features of Grails in the <a href="http://docs.grails.org" target="_blank">User Guide</a>.</p>

    </div>

    <div class="col">
        <a href="https://grails-slack.cfapps.io" target="_blank">
            <asset:image src="slack.svg" alt="Grails Slack" class="float-left"/>
        </a>
        <strong class="centered"><a href="https://grails-slack.cfapps.io" target="_blank">Join the Community</a></strong>
        <p>Get feedback and share your experience with other Grails developers in the community <a href="https://grails-slack.cfapps.io" target="_blank">Slack channel</a>.</p>
    </div>
</div>

<div id="spinner" class="spinner" style="display:none;">
    <g:message code="spinner.alt" default="Loading&hellip;"/>
</div>

<asset:javascript src="application.js"/>

</body>
</html>

As you see, there are built in Grails Spring Security TagLib sec:ifLoggedIn and <sec:loggedInUserInfo field='fullname'/>. The <sec:loggedInUserInfo field='fullname'/> only working when you implementing CustomUserDetailsService.

Create Register Controller and View

Back to the Grails interactive console to create a controller for the Register page.

grails> create-controller com.djamware.Register

Open and edit that Groovy file then replace all Groovy codes with these codes that have 2 methods of Register landing page and register action.

package com.djamware

import grails.validation.ValidationException
import grails.gorm.transactions.Transactional
import grails.plugin.springsecurity.annotation.Secured
import com.djamware.User
import com.djamware.Role
import com.djamware.UserRole

@Transactional
@Secured('permitAll')
class RegisterController {

    static allowedMethods = [register: "POST"]

    def index() { }

    def register() {
        if(!params.password.equals(params.repassword)) {
            flash.message = "Password and Re-Password not match"
            redirect action: "index"
            return
        } else {
            try {
                def user = User.findByUsername(params.username)?: new User(username: params.username, password: params.password, fullname: params.fullname).save()
                def role = Role.get(params.role.id)
                if(user && role) {
                    UserRole.create user, role

                    UserRole.withSession {
                      it.flush()
                      it.clear()
                    }

                    flash.message = "You have registered successfully. Please login."
                    redirect controller: "login", action: "auth"
                } else {
                    flash.message = "Register failed"
                    render view: "index"
                    return
                }
            } catch (ValidationException e) {
                flash.message = "Register Failed"
                redirect action: "index"
                return
            }
        }
    }
}

Next, add the index.gsp inside grails-app/views/register/. Open and edit that file then add these lines of GSP HTML tags.

<html>
<head>
    <meta name="layout" content="${gspLayout ?: 'main'}"/>
    <title>Register</title>
</head>

<body>
    <div class="row">
    <div class="col-sm-9 col-md-7 col-lg-5 mx-auto">
      <div class="card card-signin my-5">
        <div class="card-body">
          <h5 class="card-title text-center">Register Here</h5>
                    <g:if test='${flash.message}'>
                        <div class="alert alert-danger" role="alert">${flash.message}</div>
                    </g:if>
              <form class="form-signin" action="register" method="POST" id="loginForm" autocomplete="off">
                      <div class="form-group">
                          <label for="role">Role</label>
              <g:select class="form-control" name="role.id"
                    from="${com.djamware.Role.list()}"
                    optionKey="id" />
                </div>

            <div class="form-group">
                    <label for="username">Username</label>
              <input type="text" placeholder="Your username" class="form-control" name="username" id="username" autocapitalize="none"/>
            </div>

            <div class="form-group">
                          <label for="password">Password</label>
              <input type="password" placeholder="Your password" class="form-control" name="password" id="password"/>
            </div>

            <div class="form-group">
                          <label for="password">Re-Enter Password</label>
              <input type="password" placeholder="Re-enter password" class="form-control" name="repassword" id="repassword"/>
            </div>

                      <div class="form-group">
                          <label for="username">Full Name</label>
              <input type="text" placeholder="Your full name" class="form-control" name="fullname" id="fullname" autocapitalize="none"/>
            </div>

            <button id="submit" class="btn btn-lg btn-primary btn-block text-uppercase" type="submit">Register</button>
            <hr class="my-4">
            <p>Already have an account? <g:link controller="login" action="auth">Login</g:link></p>
          </form>
        </div>
      </div>
    </div>
  </div>
    <script type="text/javascript">
        document.addEventListener("DOMContentLoaded", function(event) {
            document.forms['loginForm'].elements['username'].focus();
        });
    </script>
</body>
</html>
Create the Secure Product CRUD Scaffolding

Now, we will make Product CRUD scaffolding and make them secured and accessible to ROLE_USER and ROLE_ADMIN. To create CRUD scaffolding, simply run this command inside Grails interactive console.

grails>generate-all com.djamware.Product

That command will generate controller, service, and view for a Product domain class. Next, open and edit grails-app/controllers/ProductController.groovy then add the Secure annotation like these.

package com.djamware

import grails.validation.ValidationException
import static org.springframework.http.HttpStatus.*
import grails.plugin.springsecurity.annotation.Secured

class ProductController {

    ProductService productService

    static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]

    @Secured(['ROLE_ADMIN', 'ROLE_USER'])
    def index(Integer max) {
        params.max = Math.min(max ?: 10, 100)
        respond productService.list(params), model:[productCount: productService.count()]
    }

    @Secured(['ROLE_ADMIN', 'ROLE_USER'])
    def show(Long id) {
        respond productService.get(id)
    }

    @Secured('ROLE_ADMIN')
    def create() {
        respond new Product(params)
    }

    @Secured('ROLE_ADMIN')
    def save(Product product) {
        if (product == null) {
            notFound()
            return
        }

        try {
            productService.save(product)
        } catch (ValidationException e) {
            respond product.errors, view:'create'
            return
        }

        request.withFormat {
            form multipartForm {
                flash.message = message(code: 'default.created.message', args: [message(code: 'product.label', default: 'Product'), product.id])
                redirect product
            }
            '*' { respond product, [status: CREATED] }
        }
    }

    @Secured('ROLE_ADMIN')
    def edit(Long id) {
        respond productService.get(id)
    }

    @Secured('ROLE_ADMIN')
    def update(Product product) {
        if (product == null) {
            notFound()
            return
        }

        try {
            productService.save(product)
        } catch (ValidationException e) {
            respond product.errors, view:'edit'
            return
        }

        request.withFormat {
            form multipartForm {
                flash.message = message(code: 'default.updated.message', args: [message(code: 'product.label', default: 'Product'), product.id])
                redirect product
            }
            '*'{ respond product, [status: OK] }
        }
    }

    @Secured('ROLE_ADMIN')
    def delete(Long id) {
        if (id == null) {
            notFound()
            return
        }

        productService.delete(id)

        request.withFormat {
            form multipartForm {
                flash.message = message(code: 'default.deleted.message', args: [message(code: 'product.label', default: 'Product'), id])
                redirect action:"index", method:"GET"
            }
            '*'{ render status: NO_CONTENT }
        }
    }

    protected void notFound() {
        request.withFormat {
            form multipartForm {
                flash.message = message(code: 'default.not.found.message', args: [message(code: 'product.label', default: 'Product'), params.id])
                redirect action: "index", method: "GET"
            }
            '*'{ render status: NOT_FOUND }
        }
    }
}

Next, we will make Product controller as a default landing page after succeful login. For that, open and edit grails-app/conf/application.groovy then add this configuration.

grails.plugin.springsecurity.successHandler.defaultTargetUrl = '/product'
Run and Test Grails 4 Spring Security Core

Now, we will run and test the Grails 4 Spring Security Core application. In the Grails interactive console run this command.

grails>run-app

Open the browser then go to http://localhost:8080 and you will see this page.




That it's, the Grails 4 Tutorial: Spring Security Core Login Example. You can find the full working source code in our GitHub.

Thanks!

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 {
&nbsp;&nbsp; &nbsp;ext {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;springBootVersion = '2.1.2.RELEASE'
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;repositories {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;mavenCentral()
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;dependencies {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
&nbsp;&nbsp; &nbsp;}
}

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 {
&nbsp;&nbsp; &nbsp;mavenCentral()
}

dependencies {
&nbsp;&nbsp; &nbsp;implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
&nbsp;&nbsp; &nbsp;implementation 'org.springframework.boot:spring-boot-starter-security'
&nbsp;&nbsp; &nbsp;implementation 'org.springframework.boot:spring-boot-starter-web'
&nbsp;&nbsp; &nbsp;testImplementation 'org.springframework.boot:spring-boot-starter-test'
&nbsp;&nbsp; &nbsp;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) {
&nbsp;&nbsp; &nbsp;super();
&nbsp;&nbsp; &nbsp;this.prodName = prodName;
&nbsp;&nbsp; &nbsp;this.prodDesc = prodDesc;
&nbsp;&nbsp; &nbsp;this.prodPrice = prodPrice;
&nbsp;&nbsp; &nbsp;this.prodImage = prodImage;
}

Generate or create Getter and Setter for each field.

public String getId() {
&nbsp;&nbsp; &nbsp;return id;
}

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

public String getProdName() {
&nbsp;&nbsp; &nbsp;return prodName;
}

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

public String getProdDesc() {
&nbsp;&nbsp; &nbsp;return prodDesc;
}

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

public Double getProdPrice() {
&nbsp;&nbsp; &nbsp;return prodPrice;
}

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

public String getProdImage() {
&nbsp;&nbsp; &nbsp;return prodImage;
}

public void setProdImage(String prodImage) {
&nbsp;&nbsp; &nbsp;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 {

&nbsp;&nbsp; &nbsp;@Id
&nbsp;&nbsp; &nbsp;private String id;
&nbsp;&nbsp; &nbsp;@Indexed(unique = true, direction = IndexDirection.DESCENDING, dropDups = true)
&nbsp;&nbsp; &nbsp;private String email;
&nbsp;&nbsp; &nbsp;private String password;
&nbsp;&nbsp; &nbsp;private String fullname;
&nbsp;&nbsp; &nbsp;private boolean enabled;
&nbsp;&nbsp; &nbsp;@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

An introduction to Java Security

An introduction to Java Security

In this tutorial, we’ll go through the basics of security on the Java platform. We’ll also focus on what’s available to us for writing secure applications.

1. Overview

Security is a vast topic that encompasses many areas. Some of these are part of the language itself, like access modifiers and class loaders. Furthermore, others are available as services, which include data encryption, secure communication, authentication, and authorization, to a name a few.

Therefore, it’s not practical to gain meaningful insight into all of these in this tutorial. However, we’ll try to gain at least a meaningful vocabulary.

2. Language Features

Above all, security in Java begins right at the level of language features. This allows us to write secure code, as well as benefit from many implicit security features:

  • Static Data Typing: Java is a statically typed language, which reduces the possibilities of run-time detection of type-related errors
  • Access Modifiers: Java allows us to use different access modifiers like public and private to control access to fields, methods, and classes
  • Automatic Memory Management: Java has garbage-collection based memory management, which frees developers from managing this manually
  • Bytecode Verification: Java is a compiled language, which means it converts code into platform-agnostic bytecode, and runtime verifies every bytecode it loads for execution

This is not a complete list of security features that Java provides, but it’s good enough to give us some assurance!

3. Security Architecture in Java

Before we begin to explore specific areas, let’s spend some time understanding the core architecture of security in Java.

The core principles of security in Java are driven by interoperable and extensible Provider implementations. A particular implementation of Provider may implement some or all of the security services.

For example, some of the typical services a Provider may implement are:

  • Cryptographic Algorithms (such as DSA, RSA, or SHA-256)
  • Key generation, conversion, and management facilities (such as for algorithm-specific keys)

Java ships with many built-in providers. Also, it’s possible for an application to configure multiple providers with an order of preference.

 

Consequently, the provider framework in Java searches for a specific implementation of a service in all providers in the order of preference set on them.

Moreover, it’s always possible to implement custom providers with pluggable security functions in this architecture.

4. Cryptography

Cryptography is the cornerstone of security features in general and in Java. This refers to tools and techniques for secure communication in the presence of adversaries.

4.1. Java Cryptography

The Java Cryptographic Architecture (JCA) provides a framework to access and implement cryptographic functionalities in Java, including:

  • Digital signatures
  • Message digests
  • Symmetric and asymmetric ciphers
  • Message authentication codes
  • Key generators and key factories

Most importantly, Java makes use of Provider-based implementations for cryptographic functions.

Moreover, Java includes built-in providers for commonly used cryptographic algorithms like RSA, DSA, and AES, to name a few. We can use these algorithms to add security to data in rest, in use, or in motion.

4.2. Cryptography in Practice

A very common use case in applications is to store user passwords. We use this for authentication at a later point in time. Now, it’s obvious that storing plain text passwords compromises security.

So, one solution is to scramble the passwords in such a way that the process is repeatable, yet only one-way. This process is known as the cryptographic hash function, and SHA1 is one such popular algorithm.

So, let’s see how we can do this in Java:

MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] hashedPassword = md.digest("password".getBytes());

Here, MessageDigest is a cryptographic service that we are interested in. We’re using the method getInstance() to request this service from any of the available security providers.

5. Public Key Infrastructure

Public Key Infrastructure (PKI) refers to the setup that enables the secure exchange of information over the network using public-key encryption. This setup relies on trust that is built between the parties involved in the communication. This trust is based on digital certificates issued by a neutral and trusted authority known as a Certificate Authority (CA).

5.1. PKI Support in Java

Java platform has APIs to facilitate the creation, storage, and validation of digital certificates:

  • KeyStore: Java provides the KeyStore class for persistent storage of cryptographic keys and trusted certificates. Here, KeyStore can represent both key-store and trust-store files. These files have similar content but vary in their usage.
  • CertStore: Additionally, Java has the CertStore class, which represents a public repository of potentially untrusted certificates and revocation lists. We need to retrieve certificates and revocation lists for certificate path building amongst other usages.

Java has a built-in trust-store called “cacerts” that contains certificates for well known CAs.

5.2. Java Tools for PKI

Java has some really handy tools to facilitate trusted communication:

  • There is a built-in tool called “keytool” to create and manage key-store and trust-store
  • There is also another tool “jarsigner” that we can use to sign and verify JAR files

5.3. Working with Certificates in Java

Let’s see how we can work with certificates in Java to establish a secure connection using SSL. A mutually authenticated SSL connection requires us to do two things:

  • Present Certificate — We need to present a valid certificate to another party in the communication. For that, we need to load the key-store file, where we must have our public keys:
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
char[] keyStorePassword = "changeit".toCharArray();
try(InputStream keyStoreData = new FileInputStream("keystore.jks")){
    keyStore.load(keyStoreData, keyStorePassword);
}
  • Verify Certificate — We also need to verify the certificate presented by another party in the communication. For this we need to load the trust-store, where we must have previously trusted certificates from other parties:
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
// Load the trust-store from filesystem as before

We rarely have to do this programmatically and normally pass system parameters to Java at runtime:

-Djavax.net.ssl.trustStore=truststore.jks
-Djavax.net.ssl.keyStore=keystore.jks
6. Authentication

Authentication is the process of verifying the presented identity of a user or machine based on additional data like password, token, or a variety of other credentials available today.

6.1. Authentication in Java

Java APIs makes use of pluggable login modules to provide different and often multiple authentication mechanisms to applications. LoginContext provides this abstraction, which in turn refers to configuration and loads an appropriate LoginModule.

While multiple providers make available their login modules, Java has some default ones available for use:

  • Krb5LoginModule, for Kerberos-based authentication
  • JndiLoginModule, for username and password-based authentication backed by an LDAP store
  • KeyStoreLoginModule, for cryptographic key-based authentication

6.2. Login by Example

One of the most common mechanisms of authentication is the username and password. Let’s see how we can achieve this through JndiLoginModule.

This module is responsible for getting the username and password from a user and verifying it against a directory service configured in JNDI:

LoginContext loginContext = new LoginContext("Sample", new SampleCallbackHandler());
loginContext.login();

Here, we are using an instance of LoginContext to perform the login. LoginContext takes the name of an entry in the login configuration — in this case, it’s “Sample”. Also, we have to provide an instance of CallbackHandler, using the LoginModule that interacts with the user for details like username and password.

Let’s take a look at our login configuration:

Sample {
  com.sun.security.auth.module.JndiLoginModule required;
};

Simple enough, it suggests that we’re using JndiLoginModule as a mandatory LoginModule.

7. Secure Communication

Communication over the network is vulnerable to many attack vectors. For instance, someone may tap into the network and read our data packets as they’re being transferred. Over the years, the industry has established many protocols to secure this communication.

7.1. Java Support for Secure Communication

Java provides APIs to secure network communication with encryption, message integrity, and both client and server authentication:

  • SSL/TLS: SSL and its successor, TLS, provide security over untrusted network communication through data encryption and public-key infrastructure. Java provides support of SSL/TLS through SSLSocket defined in the package “java.security.ssl“.
  • SASL: Simple Authentication and Security Layer (SASL) is a standard for authentication between client and server. Java supports SASL as part of the package “java.security.sasl“.
  • GGS-API/Kerberos: Generic Security Service API (GSS-API) offers uniform access to security services over a variety of security mechanisms like Kerberos v5. Java supports GSS-API as part of the package “java.security.jgss“.

7.2. SSL Communication in Action

Let’s now see how we can open a secure connection with other parties in Java using SSLSocket:

SocketFactory factory = SSLSocketFactory.getDefault();
try (Socket connection = factory.createSocket(host, port)) {
    BufferedReader input = new BufferedReader(
      new InputStreamReader(connection.getInputStream()));
    return input.readLine();
}

Here, we are using SSLSocketFactory to create SSLSocket. As part of this, we can set optional parameters like cipher suites and which protocol to use.

For this to work properly, we must have created and set our key-store and trust-store as we saw earlier.

8. Access Control

Access Control refers to protecting sensitive resources like a filesystem or codebase from unwarranted access. This is typically achieved by restricting access to such resources.

8.1. Access Control in Java

We can achieve access control in Java using classes Policy and Permission mediated through the SecurityManager class. SecurityManager is part of the “java.lang” package and is responsible for enforcing access control checks in Java.

When the class loader loads a class in the runtime, it automatically grants some default permissions to the class encapsulated in the Permission object. Beyond these default permissions, we can grant more leverage to a class through security policies. These are represented by the class Policy.

During the sequence of code execution, if the runtime encounters a request for a protected resource, SecurityManager verifies the requested Permission against the installed Policy through the call stack. Consequently, it either grants permission or throws SecurityException.

8.2. Java Tools for Policy

Java has a default implementation of Policy that reads authorization data from the properties file. However, the policy entries in these policy files have to be in a specific format.

Java ships with “policytool”, a graphical utility to compose policy files.

8.3. Access Control Through Example

Let’s see how we can restrict access to a resource like a file in Java:

SecurityManager securityManager = System.getSecurityManager();
if (securityManager != null) {
    securityManager.checkPermission(
      new FilePermission("/var/logs", "read"));
}

Here, we’re using SecurityManager to validate our read request for a file, wrapped in FilePermission.

But, SecurityManager delegates this request to AccessController. AccessController internally makes use of the installed Policy to arrive at a decision.

Let’s see an example of the policy file:

grant {
  permission
    java.security.FilePermission
      <<ALL FILES>>, "read";
};

We are essentially granting read permission to all files for everyone. But, we can provide much more fine-grained control through security policies.

It’s worth noting that a SecurityManager might not be installed by default in Java. We can ensure this by always starting Java with the parameter:

-Djava.security.manager -Djava.security.policy=/path/to/sample.policy
9. XML Signature

XML signatures are useful in securing data and provide data integrity. W3C provides recommendations for governance of XML Signature. We can use XML signature to secure data of any type, like binary data.

9.1. XML Signature in Java

Java API supports generating and validating XML signatures as per the recommended guidelines. Java XML Digital Signature API is encapsulated in the package “java.xml.crypto“.

The signature itself is just an XML document. XML signatures can be of three types:

  • Detached: This type of signature is over the data that is external to the Signature element
  • Enveloping: This type of signature is over the data that is internal to the Signature element
  • Enveloped: This type of signature is over the data that contains the Signature element itself

Certainly, Java supports creating and verifying all the above types of XML signatures.

9.2. Creating an XML Signature

Now, we’ll roll up our sleeves and generate an XML signature for our data. For instance, we may be about to send an XML document over the network. Hence, we would want our recipient to be able to verify its integrity.

So, let’s see how we can achieve this in Java:

XMLSignatureFactory xmlSignatureFactory = XMLSignatureFactory.getInstance("DOM");
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true);
  
Document document = documentBuilderFactory
  .newDocumentBuilder().parse(new FileInputStream("data.xml"));
  
DOMSignContext domSignContext = new DOMSignContext(
  keyEntry.getPrivateKey(), document.getDocumentElement());
  
XMLSignature xmlSignature = xmlSignatureFactory.newXMLSignature(signedInfo, keyInfo);
xmlSignature.sign(domSignContext);

To clarify, we’re generating an XML signature for our data present in the file “data.xml”. Meanwhile, there are a few things to note about this piece of code:

  • Firstly, XMLSignatureFactory is the factory class for generating XML signatures
  • XMLSigntaure requires a SignedInfo object over which it calculates the signature
  • XMLSigntaure also needs KeyInfo, which encapsulates the signing key and certificate
  • Finally, XMLSignature signs the document using the private key encapsulated as DOMSignContext

As a result, the XML document will now contain the Signature element, which can be used to verify its integrity.

10. Security Beyond Core Java

As we have seen by now, the Java platform provides a lot of the necessary functionality to write secure applications. However, sometimes, these are quite low-level and not directly applicable to, for example, the standard security mechanism on the web.

For example, when working on our system, we generally don’t want to have to read the full OAuth RFC and implement that ourselves. We often need quicker, higher-level ways to achieve security. This is where application frameworks come into the picture – these help us achieve our objective with much less boilerplate code.

And, on the Java platform – generally that means Spring Security. The framework is part of the Spring ecosystem, but it can actually be used outside of pure Spring application.

In simple terms, it helps is achieve authentication, authorization and other security features in a simple, declarative, high-level manner.

11. Conclusion

In short, in this tutorial, we went through the high-level architecture of security in Java. Also, we understood how Java provides us with implementations of some of the standard cryptographic services.

We also saw some of the common patterns that we can apply to achieve extensible and pluggable security in areas like authentication and access control.

To sum up, this just provides us with a sneak peek into the security features of Java. Consequently, each of the areas discussed in this tutorial merits further exploration. But hopefully, we should have enough insight to get started in this direction!

Thanks for reading

If you liked this post, share it with all of your programming buddies!

Follow us on Facebook | Twitter

Further reading

Java Programming Masterclass for Software Developers

Selenium WebDriver with Java -Basics to Advanced+Frameworks

Java In-Depth: Become a Complete Java Engineer!

Top 4 Spring Annotations for Java Developer in 2019

Java Tutorial for Absolute Beginners

100+ Java Interview Questions and Answers In 2019

Python vs Java: Understand Object Oriented Programming

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

Spring Security Tutorial: Simple Authentication with Spring Boot

Build Web App Authentication using Spring Boot, Spring Security, MongoDB and Angular 8