Spring Boot Security + REST + Basic Authentication

By Dhiraj Ray, 16 December,2016   | Last updated on: 12 August,2017 20955

In the last post we tried securing our Spring MVC app using spring security Spring Boot Security Login Example.We protected our app against CSRF attack too. Today we will see how to secure REST Api using Basic Authentication with Spring security features.Here we will be using Spring boot to avoid basic configurations and complete java config.We will try to perform simple CRUD operation using Spring REST and user requires to provide username and password to access these resources.At the end, we will be testing our app using postman.

What is Basic Authentication

Basic authentification is a standard HTTP header with the user and password encoded in base64 : Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==.The userName and password is encoded in the format username:password. This is one of the simplest technique to protect the REST resources because it does not require cookies. session identifiers or any login pages.

In case of basic authentication, the username and password is only encoded with Base64, but not encrypted or hashed in any way. Hence, it can be compromised by any man in the middle. Hence, it is always recommended to authenticate rest API calls by this header over a ssl connection.

BasicAuthenticationFilter in Spring

BasicAuthenticationFilter in Spring is the class which is responsible for processing basic authentication credentials presented in HTTP Headers and putting the result into the SecurityContextHolder. The standard governing HTTP Basic Authentication is defined by RFC 1945, Section 11, and BasicAuthenticationFilter confirms with this RFC.

Environment Setup

1. JDK 8 2. Spring Boot 3. Intellij Idea/ eclipse 4. Maven

Maven Dependencies

spring-boot-starter-parent: provides useful Maven defaults. It also provides a dependency-management section so that you can omit version tags for existing dependencies.

spring-boot-starter-web: includes all the dependencies required to create a web app. This will avoid lining up different spring common project versions.

spring-boot-starter-tomcat: enable an embedded Apache Tomcat 7 instance, by default. We have overriden this by defining our version. This can be also marked as provided if you wish to deploy the war to any other standalone tomcat.

spring-boot-starter-security: take care of all the required dependencies related to spring security.

<properties> <tomcat.version>8.0.3</tomcat.version> <start-class>spring-boot-example.Application</start-class> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.3.RELEASE</version> </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-tomcat</artifactId> <!--<scope>provided</scope>--> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> </dependencies>

Spring Bean Configuration

SpringBootServletInitializer enables process used in Servlet 3.0 using web.xml

@SpringBootApplication: This is a convenience annotation that is equivalent to declaring @Configuration

@EnableAutoConfiguration and @ComponentScan.

package com.developerstack.config; import org.springframework.boot.SpringApplication; import org.springframework.boot.context.web.SpringBootServletInitializer; import org.springframework.context.annotation.ComponentScan; import org.springframework.boot.autoconfigure.SpringBootApplication; @ComponentScan(basePackages = "com.developerstack") @SpringBootApplication public class Application extends SpringBootServletInitializer { private static Class applicationClass = Application.class; public static void main(String[] args) { SpringApplication.run(Application.class, args); } }

Now let us define our main configuration for spring security - SpringSecurityConfig.java.class is annotated with @EnableWebSecurity to enable Spring Security web security support. we have extended WebSecurityConfigurerAdapter to override spring features with our custom requirements.Here we want our every request to be authenticated using HTTP Basic authentication. If authentication fails, the configured AuthenticationEntryPoint will be used to retry the authentication process.

package com.developerstack.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.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; @Configuration @EnableWebSecurity public class SpringSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private AuthenticationEntryPoint authEntryPoint; @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable().authorizeRequests() .anyRequest().authenticated() .and().httpBasic() .authenticationEntryPoint(authEntryPoint); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication().withUser("john123").password("password").roles("USER"); } }

Now let us define our authentication entry point.This class will be responsible to send response when the credentials are no longer authorized. If the authentication event was successful, or authentication was not attempted because the HTTP header did not contain a supported authentication request, the filter chain will continue as normal. The only time the filter chain will be interrupted is if authentication fails and the AuthenticationEntryPoint is called.

package com.developerstack.config; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint; import org.springframework.stereotype.Component; @Component public class AuthenticationEntryPoint extends BasicAuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authEx) throws IOException, ServletException { response.addHeader("WWW-Authenticate", "Basic realm=" +getRealmName()); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); PrintWriter writer = response.getWriter(); writer.println("HTTP Status 401 - " + authEx.getMessage()); } @Override public void afterPropertiesSet() throws Exception { setRealmName("DeveloperStack"); super.afterPropertiesSet(); } }

Following is the controller class where we have exposed our APIs.

package com.developerstack.controller; import java.util.Arrays; import java.util.List; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import com.developerstack.model.User; @Controller public class UserController { @RequestMapping(path="/user", method = RequestMethod.GET) public ResponseEntity> listUser(){ return new ResponseEntity>(getUsers(), HttpStatus.OK); } @RequestMapping(path="/user/{id}", method = RequestMethod.GET) public ResponseEntity listUser(@PathVariable(value = "id") String id){ return new ResponseEntity(getUsers().stream().filter(user -> user.getId().equals(id)).findFirst().orElse(null), HttpStatus.OK); } @RequestMapping(path="/user", method = RequestMethod.POST) public ResponseEntity listUser(@RequestBody User user){ return new ResponseEntity("18", HttpStatus.OK); }

Logout Implementation

After a succesdfull authentication, Spring updates the security context with an authentication object that contains credentials, roles, principal etc.So, while logging out we need to clear this context and spring provides SecurityContextLogoutHandler which performs a logout by modifying the SecurityContextHolder.Following is the implementation.

Following logout implementation is one way to logout programatically.You can also configure logout in SpringSecurityConfig.java.You can use any one of them but not both.Once basic authentication is successfull, browser automatically sends basic auth header in every request following successfull authentication.Hence, if you are using this authentication mechanism in a browser based application, it is required to clear the Basic auth cache in the browser too after logout.

@RequestMapping(value="/logmeout", method = RequestMethod.POST) public String logoutPage (HttpServletRequest request, HttpServletResponse response) { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null){ new SecurityContextLogoutHandler().logout(request, response, auth); } return "redirect:/login"; }

Handling Cross Origin Requests

To handle Cross Origin requests add following configuration in the bean definition.

@Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurerAdapter() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**").allowedMethods("GET", "POST", "PUT", "DELETE").allowedOrigins("*") .allowedHeaders("*"); } }; }

Run Application

1. Run Application.java as a java application

2. Launch postman

3. Make a get request against http://localhost:8080/user/ with empty authoriztion and a 401 - unauthorised response can be noticed as below


4. Now enter username/password as john123/password and make the get request again and you can see the following result.



I hope this article served you that you were looking for. If you have anything that you want to add or share then please share it below in the comment section.

Download the source

Further Reading on Spring Security

1. Spring Boot Security Oauth2 Example

2. Angular Jwt Authentication

3. Spring Boot Security Custom Form Login Example

4. Spring Boot Security Hibernate Login Example

5. Spring Boot Mvc Example

6. Spring Hibernate Integration Example Javaconfig

7. Spring Jms Activemq Integration Example

8. Spring Mvc Angularjs Integration Example

Suggest more topics in suggestion section or write your own article and share with your colleagues.