Photo by Hunter Haley on Unsplash
PART 1: Create the base application
Spring Security Basics: Implementing Authentication and Authorization
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.
GET /api/hello
GET /api/protected
GET /api/admin
Also a database software is required. Here the setup uses PostgreSQL database. You are free to choose yours.
spring_access_db
in your favorite database tool.Let's go through the following steps to build the foundation.
Create the project
Implement the API end points
Create user and role entities
Create user and role repositories
Configure database connectivity
Populate database with sample users
Run the application
Verify users and roles are created by querying the database
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
Here I used the Spring initializer and opened the project in IntelliJ IDEA (Community Edition)
Add the dependencies
Fill up the project metadata
Generate the Zip file and extract it.
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
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.
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.