Show List

Spring Boot Security Using Basic Auth


Basic Auth involves Authentication and Authorization. Authentication is validating the identity of the client. Authorization is determining what access they have.

In case of Basic Auth, the credentials are sent using request header. UserName: Password are encoded using base 64 and are sent as part of Authorization key
Here is the project structure we are going to create. This is a SpringBoot project with Web, Security, H2 dependencies
pom.xml

We are going to use in memory H2 database for the Rest Service. Spring Boot starter security dependency will provide jars to implement authorization and authentication.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

</dependencies>

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

</project>
Student.java

This is our model for students info in the databe.
package com.example.demo.model;

import javax.persistence.*;

@Entity
@Table
public class Student {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int student_id;
private String name;
private String grade;

public Student(){

}
public Student( String name, String grade) {
this.name = name;
this.grade = grade;
}

public int getStudent_id() {
return student_id;
}

public void setStudent_id(int student_id) {
this.student_id = student_id;
}

public String getName() {
return name;
}

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

public String getGrade() {
return grade;
}

public void setGrade(String grade) {
this.grade = grade;
}
}
StudentRepo.java

Extending SpringCrudRepository to interact with the database.
package com.example.demo.dao;

import com.example.demo.model.Student;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface StudentRepo extends CrudRepository<Student, Long> {}
StudentController.java

This is the Rest Controller to handle GET/POST/PUT/DELETE requests for the students.
package com.example.demo.controller;

import com.example.demo.dao.StudentRepo;
import com.example.demo.model.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

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

@RestController
public class StudentController {

@Autowired
private StudentRepo repo;

@GetMapping("/students")
private List<Student> getAllStudents(){
return (List<Student>) repo.findAll();
}

@GetMapping("/student/{id}")
private Optional<Student> getStudents(@PathVariable Long id){
return repo.findById(id);
}

@PostMapping("/students")
private Student addStudent(@RequestBody Student newStudent){
return repo.save(newStudent);
}

@DeleteMapping("/students/{id}")
private void removeStudent(@PathVariable Long id){
repo.deleteById(id);
}

@PutMapping("/students/{id}")
private Student updateStudent(@RequestBody Student newStudent, @PathVariable Long id){
return repo.findById(id)
.map(student -> {
student.setName(newStudent.getName());
student.setGrade(newStudent.getGrade());
return repo.save(student);
})
.orElseGet(() -> {
newStudent.setStudent_id(Math.toIntExact(id));
return repo.save(newStudent);
});
}
}
Encoder.java

This is the bean that returns BCryptPasswordEncoder and is used to encrypt the password.
package com.example.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class Encoder {

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
SecurityConfig.java

This is used to configure the security filter chain.   Security filter chain describes security rules also known as filters. Security chain is going to filter out requests that it deems invalid.

We are using in memory users authentication in this example. There are two users added in the memory one with role ADMIN and other with role GUEST.
package com.example.demo.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;


@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter
{

@Autowired
private PasswordEncoder passwordEncoder;

@Override
protected void configure(HttpSecurity http) throws Exception
{
http
.csrf().disable()//As API is not going to be used by the browsers so can disable this protection
.authorizeRequests()//First authorize request

.antMatchers(HttpMethod.GET, "/students").hasAnyRole("ADMIN", "GUEST")
.antMatchers(HttpMethod.POST, "/students").hasRole("ADMIN")
.anyRequest().authenticated()//Any request needs to be authenticated
.and()
.httpBasic()//We are going to authenticate the requests using basic authentication
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);//Disable sessions
}

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception
{
auth.inMemoryAuthentication()
.withUser("admin")
.password(passwordEncoder.encode("admin"))
.roles("ADMIN")
.and()
.withUser("guest")
.password(passwordEncoder.encode("guest"))
.roles("GUEST");
}
}

WebSecurityConfigurerAdapter is deprecated starting from Spring Boot 2.7.0. We can rewrite the above security configuration as below:
package com.example.demo.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;


@Configuration
public class SecurityConfig
{

@Autowired
private PasswordEncoder passwordEncoder;

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()//As API is not going to be used by the browsers so can disable this protection
.authorizeRequests()//First authorize request

.antMatchers(HttpMethod.GET, "/students").hasAnyRole("ADMIN", "GUEST")
.antMatchers(HttpMethod.POST, "/students").hasRole("ADMIN")
.anyRequest().authenticated()//Any request needs to be authenticated
.and()
.httpBasic()//We are going to authenticate the requests using basic authentication
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);//Disable sessions

return http.build();
}

@Bean
public InMemoryUserDetailsManager userDetailsService() {
UserDetails admin = User
.withUsername("admin")
.password(passwordEncoder.encode("admin"))
.roles("ADMIN")
.build();

UserDetails guest = User
.withUsername("guest")
.password(passwordEncoder.encode("guest"))
.roles("GUEST")
.build();

return new InMemoryUserDetailsManager(admin, guest);
}


}

We can run the application and test the requests now using postman:

Source Code:

https://github.com/it-code-lab/Spring-Boot-Rest-Basic-Auth

    Leave a Comment


  • captcha text