PART 1: Create the base application

Spring Security Basics: Implementing Authentication and Authorization

·

5 min read

Before proceeding to the implementation of authentication and authorization , let's create a simple application to serve as a foundation. The application will have the following API end points.

  1. GET /api/hello

  2. GET /api/protected

  3. GET /api/admin

Also a database software is required. Here the setup uses PostgreSQL database. You are free to choose yours.

💡
Don't forget to create a database named spring_access_db in your favorite database tool.

Let's go through the following steps to build the foundation.

  1. Create the project

  2. Implement the API end points

  3. Create user and role entities

  4. Create user and role repositories

  5. Configure database connectivity

  6. Populate database with sample users

  7. Run the application

  8. Verify users and roles are created by querying the database

  9. Try accessing the API end points

Create the project

To create the project go to https://start.spring.io/ and create the sample project with the following dependencies.

  • Spring Web

  • Spring Data JPA

  • PostgreSQL Driver

💡
If you are using IntelliJ IDEA Ultimate or Visual Studio Code you can create the project within the IDE.
💡
Create project with IntelliJ IDEA Ultimate : Click here for doc
💡
Create project with Visual Studio Code: Click here for doc

Here I used the Spring initializer and opened the project in IntelliJ IDEA (Community Edition)

  1. Add the dependencies

  2. Fill up the project metadata

  3. Generate the Zip file and extract it.

  4. Then open it in your IDE.

Implement the API end points

Create a class named ApiController and implement the APIs there.

  • ApiController.java

      package com.gintophilip.springauth.controller;
    
      import org.springframework.http.ResponseEntity;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RestController;
    
      @RestController
      @RequestMapping("/api")
      public class ApiController {
          @GetMapping("/hello")
          public ResponseEntity<String> hello() {
              return ResponseEntity.ok("Hello");
          }
    
          @GetMapping("/protected")
          public ResponseEntity<String> protectedResource() {
              String message = """
                      This is a protected resource <br>
                      You are seeing this because you are an authenticated user
                      """;
              return ResponseEntity.ok(message);
          }
    
          @GetMapping("/admin")
          public ResponseEntity<String> admin() {
              String message = "Hello Admin";
              return ResponseEntity.ok(message);
          }
      }
    

Create user and role entities

Create two classes for modeling the user and role entities.

ApplicationUser.java

package com.gintophilip.springauth.entities;

import jakarta.persistence.*;

@Entity
public class ApplicationUser {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String firstName;
    private String lastName;
    @Column(unique = true)
    private String email;
    private String password;
    @ManyToOne
    @JoinTable(name = "user_roles")
    private Roles role;

    public Long getId() {
        return id;
    }

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

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Roles getRole() {
        return role;
    }

    public void setRole(Roles role) {
        this.role = role;
    }
}

Roles.java

package com.gintophilip.springauth.entities;

import jakarta.persistence.*;

@Entity
public class Roles {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @Column(unique = true)
    private String roleName;

    public Long getId() {
        return id;
    }

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

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }
}

Create user and role repositories

UserRepository.java

package com.gintophilip.springauth.repository;

import com.gintophilip.springauth.entities.ApplicationUser;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository  extends JpaRepository<ApplicationUser,Long> {

    ApplicationUser findByEmail(String email);
}

RoleRepository.java

package com.gintophilip.springauth.repository;

import com.gintophilip.springauth.entities.Roles;
import org.springframework.data.jpa.repository.JpaRepository;

public interface RoleRepository extends JpaRepository<Roles,Long> {

}

Configure database connectivity

Let's configure the connection to the database named spring_access_db with the following settings:

username: postgres

password: 12345

For configuring the database connectivity update the application.properties file as follows.

spring.datasource.url=jdbc:postgresql://localhost:5432/spring_access_db
spring.datasource.username=postgres
spring.datasource.password=12345
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto = update
💡
If you are using a different database, please ensure that the connection URL is updated to correspond with your specific database. also the hibernate dialect

Populate database with sample users

Let's Create a class which implements the CommandLineRunner interface. The method run will be executed once the application is ready.

DatabaseUtilityRunner.java

package com.gintophilip.springauth;

import com.gintophilip.springauth.entities.ApplicationUser;
import com.gintophilip.springauth.entities.Roles;
import com.gintophilip.springauth.repository.RoleRepository;
import com.gintophilip.springauth.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class DataBaseUtilityRunner implements CommandLineRunner {
    @Autowired
    UserRepository usersRepository;
    @Autowired
    RoleRepository rolesRepository;
    @Override
    public void run(String... args) throws Exception {
        try {
            Roles userRole = new Roles();
            userRole.setRoleName("USER");
            Roles adminRole = new Roles();
            adminRole.setRoleName("ADMIN");
            rolesRepository.save(userRole);
            rolesRepository.save(adminRole);
            ApplicationUser user1 = new ApplicationUser();
            user1.setFirstName("John");
            user1.setEmail("john@test.com");
            user1.setPassword("123456");
            user1.setRole(userRole);

            ApplicationUser adminUser = new ApplicationUser();
            adminUser.setFirstName("sam");
            adminUser.setEmail("sam@test.com");
            adminUser.setPassword("12345");

            adminUser.setRole(adminRole);
            usersRepository.save(user1);
            usersRepository.save(adminUser);
        }catch (Exception exception){

        }

    }
}

Verify users and roles are created by querying the database

As a result of the previous step three tables will be created with the provided data in the database spring_access_db;

Here I use the psql CLI client to view and query the database.

List of tables

spring_access_db-# \dt
              List of relations
 Schema |       Name       | Type  |  Owner   
--------+------------------+-------+----------
 public | application_user | table | postgres
 public | roles            | table | postgres
 public | user_roles       | table | postgres
(3 rows)

Table: application_user

Query: select * from application_user;

spring_access_db=# select * from application_user;
 id |     email     | first_name | last_name | password 
----+---------------+------------+-----------+----------
  1 | john@test.com | John       |           | 123456
  2 | sam@test.com  | sam        |           | 12345
(2 rows)

In this example, the password is stored in clear text. In a real-world scenario, it is essential to store passwords in an encrypted form rather than as plain text. We'll do this later.

Table: roles

Query:select * from roles;

spring_access_db=# select * from roles;
 id  | role_name 
-----+-----------
 802 | USER
 803 | ADMIN
(2 rows)

Table: user_roles

Query:select * from user_roles;

spring_access_db=# select * from user_roles;
 role_id | id 
---------+----
     802 |  1
     803 |  2
(2 rows)

Run the application

Now run the application. Once it is up and running, open the browser and try to access the API endpoints.

  1. http://localhost:8080/api/hello

  2. http://localhost:8080/api/protected

  3. http://localhost:8080/api/admin

You will be able to access all the APIs without any restrictions. This completes the setup of base application.

In the comings steps we'll see how to protect these end points by integrating authentication and authorization mechanisms using Spring Security.