Spring Security In-Memory Authentication

In this tutorial, you will learn how to add Spring Security to your project and how to enable in-memory basic authentication. You will learn how to configure two different users with different Roles and Privileges. Both users, their roles and privileges will be stored in the memory of your application.

Create a New Spring Boot Project

I hope you already know how to create a new Spring Boot project and make it a RESTful Web service but just in case you do not have, here is a quick tutorial that shows how to build a very simple Web Service project with Spring Boot( Includes video tutorial ).

Add Spring Security

Once you have your new Spring Boot project created, open the pom.xml file and make sure that you have the following two dependencies added.

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

The very first dependency on the list is spring-boot-starter-security and it is what will add Spring Security to your Spring Boot project. After you add this dependency to your project, all web service endpoints you create will require user authentication.

If you attempt to access one of your RESTful Web Service endpoints via a browser window, you will be prompted to provide a user name and password.

Spring Security Login Page

Use “user” as a user name and to find a password, look into the console of your project. You should see something like this in the console output.

Using generated security password: 3f45799a-9ad7-41f5-84ba-8d53b2d1fe19

The password in your case will of course be different.

Configure In-Memory Users with Roles

We now need to create a couple of users that will be stored in memory and their custom username and password can be used to authenticate with our web service endpoint. Each user we are going to create will have its own ROLE. One user will have the role of a regular user and the other user will have the role of a manager.

Create a new class and:

  • Annotate it with @Configuration annotation and,
  • Make it extend WebSecurityConfigurerAdapter
  • Add code to create two users with different roles.
package com.appsdeveloperblog.tutorials.inmemoryauth.security;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
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.factory.PasswordEncoderFactories;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    @Override
    public UserDetailsService userDetailsService() {
 
        //User Role
        UserDetails theUser = User.withUsername("sergey")
                        .passwordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder()::encode)
                        .password("12345678").roles("USER").build();
        
        //Manager Role 
        UserDetails theManager = User.withUsername("john")
                .passwordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder()::encode)
                .password("87654321").roles("MANAGER").build();
        
  
        InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager();
              
        userDetailsManager.createUser(theUser);
        userDetailsManager.createUser(theManager);
        
        return userDetailsManager;

    }

}

The code example above will create two users with different roles and you should be able to use any of these users to authenticate when Spring Security prompts you to provide username and password.

Alternative approach 

You can also use an alternative approach to create users with their roles.

Please note that in the following approach eanch password has a prefix of {bcrypt} which specifies which password encodder should be used to encode the provided password. Below are different values you can use as password prifix:

  • Use {bcrypt} for BCryptPasswordEncoder,
  • Use {noop} for NoOpPasswordEncoder,
  • Use {pbkdf2} for Pbkdf2PasswordEncoder, 
  • Use {scrypt} for SCryptPasswordEncoder,
  • Use {sha256} for StandardPasswordEncoder.
package com.appsdeveloperblog.tutorials.inmemoryauth.security;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("sergey")
                .password("{noop}12345678")
                .roles("USER")
                .and()
                .withUser("John")
                .password("{noop}87654321")
                .roles("MANAGER");
    }

}

Configure In-Memory Users with Authorities

A user with a Role can have multiple Authorities. We also someetimes refer to Authorities as Privileges. For example, a user with a role SUPER_MANAGER might have authorities like DELETE_USER_AUTHORITY, UPDATE_USER_AUTHORITY while a user with a role with MANAGER might not have a privilege to delete users and only have an UPDATE_USER_AUTHORITY. Using Authorities you can define users with more granular access control and then restrict web service endpoints and HTTP requests to users with specific authorities rather than roles.

In the below code example, any user with a DELETE_USER_AUTHORITY will be able to communicate with /managers/status/check web service endpoint.

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .csrf().disable()
            .authorizeRequests() 
            .antMatchers("/managers/status/check").hasAnyAuthority("DELETE_USER_AUTHORITY")
            .antMatchers("/users/status/check").hasRole("USER")
            .anyRequest().authenticated()
            .and()
            .httpBasic()
            .and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}

Define Web Service Endpoints

Let’s now create a couple of Web Service endpoints and learn how to restrict access to them to a specific Role and an Authority.

In the following code example, we will create a new Rest Controller class with two endpoints:

  • /users/status/check
  • /managers/status/check
package com.appsdeveloperblog.tutorials.inmemoryauth.controllers;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UsersController {

    @GetMapping("/users/status/check")
    public String usersStatusCheck() {
        return "Authorized user";
    }
    
    @GetMapping("/managers/status/check")
    public String managersStatusCheck() {
        return "Authorized manager";
    }
    
}

Access Control for a User with a Role or AnyRole

Let’s now configure HttpSecurity, so that web service endpoint /users/status/check is available only to users with a role USER and web service endpoint /managers/status/check is available only to users with a role MANAGER.

Add the following function to the SecureConfig class.

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .csrf().disable()
            .authorizeRequests() 
            .antMatchers("/managers/status/check").hasRole("MANAGER")
            .antMatchers("/users/status/check").hasRole("USER")
            .anyRequest().authenticated()
            .and()
            .httpBasic()
            .and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}

If the web service endpoint needs to be accessible by a user with multiple roles then you can use hasAnyRole instead of hasRoleLike so:

.antMatchers("/users/status/check").hasAnyRole("USER", "MANAGER")

Here is a complete SecureConfig class.

package com.appsdeveloperblog.tutorials.inmemoryauth.security;

import org.springframework.context.annotation.Configuration;
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;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .authorizeRequests() 
                .antMatchers("/managers/status/check").hasRole("MANAGER")
                .antMatchers("/users/status/check").hasRole("USER")
                .anyRequest().authenticated()
                .and()
                .httpBasic()
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("sergey")
                .password("{noop}12345678")
                .roles("USER")
                .and()
                .withUser("John")
                .password("{noop}87654321")
                .roles("MANAGER");
    }
}

If you run your application now and send HTTP GET request to /managers/status/check the only user credentials that will allow access, will be for a user with a role MANAGER.

Access Control for a User with an Authority or AnyAuthority

In the example, below we will allow any user with DELETE_USER_AUTHORITY to be able to perform an HTTP DELETE operation on a web service endpoint /users/**. 

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .csrf().disable()
            .antMatchers(HttpMethod.DELETE, "/users/**").hasAuthority("DELETE_USER_AUTHORITY")
            .anyRequest().authenticated()
            .and()
            .httpBasic()
            .and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}

To allow multiple authorities use hasAnyAuthority instead of hasAuthority. For example:

.antMatchers(HttpMethod.DELETE, "/users/**").hasAnyAuthority("DELETE_USER_AUTHORITY", "DELETE_ANYUSER_AUTHORITY")

 

I hope this tutorial was helpful to you.

 

Leave a Reply

Your email address will not be published. Required fields are marked *