brass colored metal padlock with chain

How to protect REST API using Basic Authentication?

brass colored metal padlock with chain
Photo by Life Of Pix on Pexels.com

REST APIs are one of the primary means of communication between different apps in modern web applications.

Anyone can send a request to a public REST API and get a response.

This poses security risk.

We need only legitimate users to hit our REST APIs and get the information they need.

We can do this by securing our REST APIs using one of the many authentication methods available.

The simplest of this is “Basic Authentication”

You secure your REST API using username and password , a widely used method of security.

Spring provides ways to do this in a cleaner way.

You just need to configure a filter which will intercept the requests and check if it is authentic.

Let’s see how to implement this step by step.

First let’s see how to protect your REST APIs using random passwords provided by Spring itself ,

You need to do very minimal configuration for this.

Then we will see how to use a database to validate the username and password.

After you create a spring boot project , follow the below steps:

  1. Add security related dependencies
  2. Add security configuration in a configuration class
  3. Create a sample REST API and test

STEP 1: Add security related dependencies

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

</dependency>

The above dependency is required for all security related stuff Spring provides us.

Apart from the security related dependencies we also need the web dependency to expose the REST APIs to the web:

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

STEP 2 : Add security related configuration


Create a configuration class by annotating it with @Configuration

Also annotate it with @EnableWebSecurity to enable security.

Then add a filter using the below code :



	@Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {

return http
.authorizeHttpRequests(a -> a.anyRequest().authenticated())
.httpBasic(Customizer.withDefaults())
.build();
}

We are creating a SecurityFilterChain bean in the above code.

This will be used by Spring to interpret all requests.

And we authenticate only http requests which is specified with authorizeHttpRequests() method

The type of authentication is obviously basic authentication which is specified with httpBasic() method , we pass default values provided by Spring to this method .

Here is the full configuration class:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

@Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {

return http
.authorizeHttpRequests(a -> a.anyRequest().authenticated())
.httpBasic(Customizer.withDefaults())
.build();
}

}

STEP 3: Test

Let’s create a sample REST API and test our changes.

Here is a very simple REST API:

@RestController
public class TestController {

@GetMapping("/test")
public String getMessage() {

return "Hello World!";
}
}

All it does is return a message when you call the endpoint /test.

Now if you run the application and hit the above endpoint (http://localhost:8080/test) in your browser or a REST client tool like postman you will be prompted to enter username and password.

If you are using postman you can directly enter the username and password under Authorization tab -> Basic Auth.

The default user name is “user”,

And the default random password is printed in the console while starting your application.

You can enter that password and hit the API and notice that you will get a successful response.

If you enter the wrong credentials you will get 401 unauthorized error.

The above implementation has a serious problem.

You cannot rely on randomly generated password by Spring and that which is printed in the console.

In a real world scenario you may want the credentials stored in a database and validate against it.

For this,

You need to make a few more changes.

STEP 4: Create the tables

First we need tables which contain user details.

Spring requires two tables :

  1. users
  2. authorities

users table will contain username and password and if the user is enabled or not.

These are mandatory fields , you can also add optional columns to indicate if user is locked , password is expired etc.

authorities table will contain the role of the users , this is required for authorization ,

In this example we are just dealing with authentication , so we will not go deep into this table.

The script for creating these tables are already available in users.ddl script in spring-security-core jar under the package:

org.springframework.security.core.userdetails.jdbc

You can copy the script from the file and create the tables manually.

Here are the two create scripts:

create table users(username varchar(50) not null primary key,password varchar(500) not null,enabled boolean not null);

create table authorities (username varchar(50) not null,authority varchar(50) not null,constraint fk_authorities_users foreign key(username) references users(username));

There is one more create statement to create the index , I ignored it for simplicity.

Run the above scripts after creating a schema in your database.

You can also create the above tables using hibernate by creating entity classes.

Another way to create is to create schema.sql and include the above DDL scripts there and tell Spring to run those scripts on starting up the application.

For this example though we will keep it simple.

It is easier to build you knowledge from a simple base than get crowded with complex details at the start.

Once you create the tables , you need to configure them in your application.properties file.

STEP 5: Configure users database in application.properties

Here is a sample configuration:



spring.datasource.url=jdbc:mysql://localhost:3306/users
spring.datasource.username=USERNAME
spring.datasource.password=PASSWORD
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

The properties are self explanatory.

You specify the URL of your database schema,

and the username of your database

and the password of your database

and finally the driver class (I have used MySQL database and hence the above driver class name)

Now your database is set.

You need to insert some users to validate against.

We will do that at the end.

Now let’s update our configuration class to tell Spring that it needs to validate against the database and not against a randomly generated password.

STEP 6: Update Security Configuration class

You need to create a JdbcUserDetailsManager bean and pass a datasource object to it.

This will indicate Spring to connect to database using JDBC.

Here is the configuration :

@Bean
public JdbcUserDetailsManager jdbcManager(DataSource dataSource) {
return new JdbcUserDetailsManager(dataSource);
}

And then you need to do one more thing.

It is not safe to store passwords in text format in your tables.

So you need to encode them and store.

And you need to tell Spring what encoding algorithm you are following.

One of the familiar encoding algorithm is the BCryptPassword Encoding algorithm.

Let’s create a bean for the same in the configuration class:

@Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}

The above bean will take care of encoding and decoding passwords.

The above step is mandated by Spring so you have to provide an encoder bean

Our configuration class is now ready.

Here is the complete configuration class:


@Configuration
@EnableWebSecurity
public class SecurityConfiguration {


@Bean
public JdbcUserDetailsManager jdbcManager(DataSource dataSource) {

return new JdbcUserDetailsManager(dataSource);
}

@Bean
public PasswordEncoder encoder() {

return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {

return http
.authorizeHttpRequests(a -> a.anyRequest().authenticated())
.httpBasic(Customizer.withDefaults())
.build();
}

}

STEP 7: Insert values in users and authorities table

Let’s insert a sample user in the tables.

The password should be stored in encoded format.

Since we are using BCrypt encoding this should be encoded using the same algorithm.

You can do this in java using the below line of code:

String encodedPassword = new BCryptPasswordEncoder().encode("your_password");

Here are sample SQL statements:


insert into users.users values("superman","$2a$10$va6NwafstuURVwhjt4UDFeTZrN6CezueP4C8GHZeEC07xE4ESqgMu",true)
insert into users.authorities values('superman','ROLE_USER');

Our credentials are ready,

Now if you hit your API using postman or browser after entering the above credentials (username:”superman”, password: “hello@123”) you should see the message “Hello World!” returned.

Here is the entire code:

https://github.com/vijaysrj/restbasicauth

Comments

Leave a Reply

Discover more from The Full Stack Developer

Subscribe now to keep reading and get access to the full archive.

Continue reading