Spring boot security rest basic authentication example

Learn to use basic authentication to secure rest apis created inside a Spring boot application. The secured rest api will ask for authentication details before giving access the data it secure.

1. Maven dependency

To secure rest apis, we must include spring security related jar files in project runtime. Simplest way to add all required jars is add spring-boot-starter-security dependency.

<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>2.0.5.RELEASE</version>
	<relativePath />
</parent>

<dependencies>
	<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>

2. Configure WebSecurityConfigurerAdapter

To enable authentication and authorization support in spring boot rest apis, we can configure a utility class WebSecurityConfigurerAdapter. It helps in requiring the user to be authenticated prior to accessing any configured URL (or all urls) within our application.

package com.howtodoinjava.rest.config;

import org.springframework.beans.factory.annotation.Autowired;
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;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
    @Override
    protected void configure(HttpSecurity http) throws Exception 
    {
        http
         .csrf().disable()
         .authorizeRequests().anyRequest().authenticated()
         .and()
         .httpBasic();
    }
 
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) 
            throws Exception 
    {
        auth.inMemoryAuthentication()
        	.withUser("admin")
        	.password("{noop}password")
        	.roles("USER");
    }
}

3. Spring boot security rest basic authentication demo

For demo purpose, we can write a simple REST API given below.

3.1. REST API

@RestController
@RequestMapping(path = "/employees")
public class EmployeeController 
{
    @Autowired
    private EmployeeDAO employeeDao;
    
    @GetMapping(path="/", produces = "application/json")
    public Employees getEmployees() 
    {
        return employeeDao.getAllEmployees();
    }
}

3.2. Access rest api without ‘authorization’ header

Access rest api at URL : HTTP GET http://localhost:8080/employees/

Require username and password
Require username and password

3.3. Access rest api with ‘authorization’ header

Upon passing authorization request header with encoded basic-auth user name and password combination, we will be able to access the rest api response.

Access rest api at URL : HTTP GET http://localhost:8080/employees/

Successful api call
Successful api call

4. Conclusion

In this spring boot security rest basic authentication example, we learned to secure rest apis with basic authentication. It is done in two steps. First step is to include required dependencies e.g. spring-boot-starter-security. Second step is to configure WebSecurityConfigurerAdapter and add auth details.

References:

Spring security reference
HTTP Basic Auth

Was this post helpful?

Join 7000+ Fellow Programmers

Subscribe to get new post notifications, industry updates, best practices, and much more. Directly into your inbox, for free.

22 thoughts on “Spring boot security rest basic authentication example”

  1. Actually its working fine but if i changed password to incorrect and run 2nd time, it should give status that 401 unauthorization. but its not happening.can u explain me y?

    Reply
  2. Hello,

    Based on your sample, I made a similar project. Without Basic Auth security, all works. When I put the security, for all GET requests, it works fine. But, when I try a POST command, I receive a 403 Forbidden. I use the same SecurityConfig.java as yours.

    Have you any idea? I try with antMatchers(HttpMethod.POST, /**).hasRole(“USER”) without success.

    Thank you in advance.

    Try with Postman with URL http://localhost:8080/customer-consent/v1/consents/create. Here is the log I recieve. I got an “invalid CSRF token found” while in SpringConfig, the CSRF is disabled.

    o.s.security.web.FilterChainProxy        : /consents/create at position 1 of 15 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
    o.s.security.web.FilterChainProxy        : /consents/create at position 2 of 15 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
    w.c.HttpSessionSecurityContextRepository : Obtained a valid SecurityContext from SPRING_SECURITY_CONTEXT: 'org.springframework.security.core.context.SecurityContextImpl@6b9bf486: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@6b9bf486: Principal: org.springframework.security.core.userdetails.User@94640b42: Username: s-dsd-baseauth-d; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Not granted any authorities; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@0: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: AA69303F705025CC2C3D9B677625B41A; Not granted any authorities'
    o.s.security.web.FilterChainProxy        : /consents/create at position 3 of 15 in additional filter chain; firing Filter: 'HeaderWriterFilter'
    o.s.security.web.FilterChainProxy        : /consents/create at position 4 of 15 in additional filter chain; firing Filter: 'CsrfFilter'
    o.s.security.web.csrf.CsrfFilter         : Invalid CSRF token found for http://localhost:8080/customer-consent/v1/consents/create
    o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@75246835
    s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
    o.s.security.web.FilterChainProxy        : /error at position 1 of 15 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
    o.s.security.web.FilterChainProxy        : /error at position 2 of 15 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
    w.c.HttpSessionSecurityContextRepository : Obtained a valid SecurityContext from SPRING_SECURITY_CONTEXT: 'org.springframework.security.core.context.SecurityContextImpl@6b9bf486: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@6b9bf486: Principal: org.springframework.security.core.userdetails.User@94640b42: Username: s-dsd-baseauth-d; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Not granted any authorities; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@0: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: AA69303F705025CC2C3D9B677625B41A; Not granted any authorities'
    o.s.security.web.FilterChainProxy        : /error at position 3 of 15 in additional filter chain; firing Filter: 'HeaderWriterFilter'
    o.s.security.web.FilterChainProxy        : /error at position 4 of 15 in additional filter chain; firing Filter: 'CsrfFilter'
    o.s.security.web.FilterChainProxy        : /error at position 5 of 15 in additional filter chain; firing Filter: 'LogoutFilter'
    o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/error'; against '/logout'
    o.s.security.web.FilterChainProxy        : /error at position 6 of 15 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
    o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/error'; against '/login'
    o.s.security.web.FilterChainProxy        : /error at position 7 of 15 in additional filter chain; firing Filter: 'DefaultLoginPageGeneratingFilter'
    o.s.security.web.FilterChainProxy        : /error at position 8 of 15 in additional filter chain; firing Filter: 'DefaultLogoutPageGeneratingFilter'
    o.s.security.web.FilterChainProxy        : /error at position 9 of 15 in additional filter chain; firing Filter: 'BasicAuthenticationFilter'
    o.s.security.web.FilterChainProxy        : /error at position 10 of 15 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
    o.s.s.w.s.DefaultSavedRequest            : pathInfo: both null (property equals)
    o.s.s.w.s.DefaultSavedRequest            : queryString: both null (property equals)
    o.s.s.w.s.DefaultSavedRequest            : requestURI: arg1=/customer-consent/v1/error; arg2=/customer-consent/v1/error (property equals)
    o.s.s.w.s.HttpSessionRequestCache        : saved request doesn't match
    o.s.security.web.FilterChainProxy        : /error at position 11 of 15 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
    o.s.security.web.FilterChainProxy        : /error at position 12 of 15 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
    o.s.s.w.a.AnonymousAuthenticationFilter  : SecurityContextHolder not populated with anonymous token, as it already contained: 'org.springframework.security.authentication.UsernamePasswordAuthenticationToken@6b9bf486: Principal: org.springframework.security.core.userdetails.User@94640b42: Username: s-dsd-baseauth-d; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Not granted any authorities; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@0: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: AA69303F705025CC2C3D9B677625B41A; Not granted any authorities'
    o.s.security.web.FilterChainProxy        : /error at position 13 of 15 in additional filter chain; firing Filter: 'SessionManagementFilter'
    o.s.security.web.FilterChainProxy        : /error at position 14 of 15 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
    o.s.security.web.FilterChainProxy        : /error at position 15 of 15 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
    o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/actuator/health/**']
    o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/error'; against '/actuator/health/**'
    o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/actuator/info/**']
    o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/error'; against '/actuator/info/**'
    o.s.s.web.util.matcher.OrRequestMatcher  : No matches found
    o.s.s.w.a.i.FilterSecurityInterceptor    : Secure object: FilterInvocation: URL: /error; Attributes: [authenticated]
    o.s.s.w.a.i.FilterSecurityInterceptor    : Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@6b9bf486: Principal: org.springframework.security.core.userdetails.User@94640b42: Username: s-dsd-baseauth-d; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Not granted any authorities; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@0: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: AA69303F705025CC2C3D9B677625B41A; Not granted any authorities
    o.s.s.access.vote.AffirmativeBased       : Voter: org.springframework.security.web.access.expression.WebExpressionVoter@219b15f9, returned: 1
    o.s.s.w.a.i.FilterSecurityInterceptor    : Authorization successful
    o.s.s.w.a.i.FilterSecurityInterceptor    : RunAsManager did not change Authentication object
    o.s.security.web.FilterChainProxy        : /error reached end of additional filter chain; proceeding with original chain
    o.s.s.w.a.ExceptionTranslationFilter     : Chain processed normally
    s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
    
    Reply
  3. Hello Lokesh,

    Thank you for this simple example it’s really helpful. I tried using it to easily protect an endpoint in a simple Java Spring Boot project but it seems I the password is not verified, only a correct username seems to be necessary, any idea why?

    Thank you,

    Reply
  4. This example is good. I am working on microservices and few calls with token but how to bypass few internal urls (interservice calls) which will not carry token info in header.

    Reply
  5. Instead of going through the pain of encoding username and passwords, you could always select ‘auth’ as ‘Basic auth’ in the postman/Insomnia UI and add in username and password in plain text itself.

    Reply
  6. What is “.pa” in the exaple above…

    auth.inMemoryAuthentication()
                .pa
                .withUser("admin")
                .password("{noop}password")
                .roles("USER");
    
    Reply
  7. Good example for basic understanding of spring security.

    It would be better if you can add on more step on how to create authorization request header with encoded basic-auth user name and password combination. It will help.

    Reply

Leave a Comment

HowToDoInJava

A blog about Java and its related technologies, the best practices, algorithms, interview questions, scripting languages, and Python.