Spring Security Pre-authentication Example

Introduction

Here we will see an example on Spring Security Pre-authentication. There are situations where you want to use Spring Security for authorization, but the user has already been reliably authenticated by some external system prior to accessing the application. In such situations where Spring Security Pre-authentication comes into picture we refer to these situations as “pre-authenticated” scenarios. Examples include X.509, Siteminder and authentication by the J2EE container in which the application is running. When using spring security pre-authentication, Spring Security has to

  • Identify the user making the request
  • Obtain the authorities for the user

The details will depend on the external authentication mechanism. A user might be identified by their certificate information in the case of X.509, or by an HTTP request header in the case of Siteminder. If relying on container authentication, the user will be identified by calling the getUserPrincipal() method on the incoming HTTP request. In some cases, the external mechanism may supply role/authority information for the user but in others the authorities must be obtained from a separate source, such as a UserDetailsService.

Assumption

In this example, it is assumed that the user has been authenticated using Siteminder SSO credentials and user no longer needs to be authenticated but has to be authorized only to perform the activities using REST URL.

Prerequisites

The following configurations are required in order to run the application

Eclipse, Java 1.8, Spring Boot 2.1.5, Gradle 4.10.2

Example with Source Code

Creating Project

Create a gradle project in Eclipse, the project structure looks similar to the below image:

spring security pre-authentication

Updating Build Script

We will add the required dependencies for our Spring Security Pre-authentication example.

We have added dependencies for spring security, spring jdbc, we are using here in-memory h2 database. You can use any database as per your choice.

buildscript {
	ext {
		springBootVersion = '2.1.5.RELEASE'
	}
    repositories {
    	mavenLocal()
    	mavenCentral()
    }
    dependencies {
    	classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'org.springframework.boot'
    
sourceCompatibility = 1.8
targetCompatibility = 1.8

repositories {
	mavenLocal()
    mavenCentral()
}

dependencies {
	compile("org.springframework.boot:spring-boot-starter-web:${springBootVersion}")
	compile("org.springframework.boot:spring-boot-starter-security:${springBootVersion}")
	compile("org.springframework.boot:spring-boot-starter-jdbc:${springBootVersion}")
	runtime("com.h2database:h2:1.4.196")
}

Creating SQL Scripts

We need to create SQL scripts in order to create tables in h2 in-memory database under src/main/resources directory. We will create two SQL scripts – one for creating tables and another for inserting data into the tables.

Creating Table Script

We have created two table to map role with a user.

CREATE TABLE `user` (
  `user_name` varchar(30) NOT NULL,
  `user_pass` varchar(255) NOT NULL,
  `enable` tinyint(1) NOT NULL DEFAULT '1',
  PRIMARY KEY (`user_name`)
);

DROP TABLE IF EXISTS `user_role`;

CREATE TABLE `user_role` (
  `user_name` varchar(30) NOT NULL,
  `user_role` varchar(15) NOT NULL,
  FOREIGN KEY (`user_name`) REFERENCES `user` (`user_name`)
);

Inserting Data Script

We will insert only one user to show our example with simplicity.

insert  into `user`(`user_name`,`user_pass`,`enable`) values ('roy','ae685575101ee7165c90a8f2c30c6e60cdd9e482',1);
insert  into `user_role`(`user_name`,`user_role`) values ('roy','ROLE_ADMIN');

The above scripts will be executed when the application will be started using Spring Boot main class.

Creating Configuration Classes

We need configuration classes in order to initialize annotation based bean configurations.

Configuring DataSource

We have created below Spring Configuration class to define DataSource and JdbcTemplate beans.

package com.jeejava.spring.security.preauth.config;

import javax.sql.DataSource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;

@Configuration
public class SpringPreAuthenticationConfig {

	@Bean
	public DataSource dataSource() {
		EmbeddedDatabaseBuilder embeddedDatabaseBuilder = new EmbeddedDatabaseBuilder();
		return embeddedDatabaseBuilder.addScript("classpath:create-table.sql").addScript("insert-data.sql")
				.setType(EmbeddedDatabaseType.H2).build();
	}

	@Bean
	public JdbcTemplate jdbcTemplate(DataSource dataSource) {
		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
		return jdbcTemplate;
	}

}

In the above class I have used Spring JDBC API, you can also use Spring Data JPA API to perform database operations.

Configuring Spring Security

The below class configures Spring Security.

We need below security configuration using Java annotation in order to authorization. The below WebSecurityConfig class, which is responsible for all security configurations, extends WebSecurityConfigurerAdapter and overrides configure(HttpSecurity http) and authenticationManager() methods. We have also ignored security for static resources such as js, css, images etc. We have overridden method configure(HttpSecurity http) to apply security for all URLs including the URL which is having pattern /blogs with roles as ADMIN.

We have also defined some methods to return objects as spring beans for authentication purpose. We have assumed here that the user has already been authenticated using SiteMinder authentication and that’s why we are just checking for the http header SM_USER exists or not for further authentication.

package com.jeejava.spring.security.preauth.config;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter;

import com.jeejava.spring.security.preauth.service.CustomUserDetailsService;

@Configuration
@EnableWebSecurity
public class SpringPreAuthenticationSecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	private CustomUserDetailsService customUserDetailsService;

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.addFilterAfter(siteminderFilter(), RequestHeaderAuthenticationFilter.class).authorizeRequests()
				.antMatchers("/static/**").permitAll().antMatchers("/").permitAll().anyRequest().authenticated()
				.antMatchers("/blogs").access("hasRole('ADMIN')");
	}

	@Bean(name = "siteminderFilter")
	public RequestHeaderAuthenticationFilter siteminderFilter() throws Exception {
		RequestHeaderAuthenticationFilter requestHeaderAuthenticationFilter = new RequestHeaderAuthenticationFilter();
		requestHeaderAuthenticationFilter.setPrincipalRequestHeader("SM_USER");
		requestHeaderAuthenticationFilter.setAuthenticationManager(authenticationManager());
		return requestHeaderAuthenticationFilter;
	}

	@Bean
	@Override
	protected AuthenticationManager authenticationManager() throws Exception {
		final List<AuthenticationProvider> providers = new ArrayList<>(1);
		providers.add(preauthAuthProvider());
		return new ProviderManager(providers);
	}

	@Bean(name = "preAuthProvider")
	PreAuthenticatedAuthenticationProvider preauthAuthProvider() throws Exception {
		PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();
		provider.setPreAuthenticatedUserDetailsService(userDetailsServiceWrapper());
		return provider;
	}

	@Bean
	UserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken> userDetailsServiceWrapper() throws Exception {
		UserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken> wrapper = new UserDetailsByNameServiceWrapper<>();
		wrapper.setUserDetailsService(customUserDetailsService);
		return wrapper;
	}

}

Creating Model Class

We need to map our database table column to Java attribute, so we will create User class to represent it.

package com.jeejava.spring.security.preauth.model;

public class User {

	private String username;
	private String password;
	private String role;

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getRole() {
		return role;
	}

	public void setRole(String role) {
		this.role = role;
	}

}

Creating RowMapper Class

Create below class in order to map table row with User class. We use here spring’s RowMapper interface to define our own process for mapping database row Java class object.

package com.jeejava.spring.security.preauth.jdbc.row.mapper;

import java.sql.ResultSet;
import java.sql.SQLException;

import org.springframework.jdbc.core.RowMapper;

import com.jeejava.spring.security.preauth.model.User;

public class UserRowMapper implements RowMapper<User> {

	@Override
	public User mapRow(ResultSet rs, int row) throws SQLException {
		User userDetails = new User();
		userDetails.setUsername(rs.getString("user_name"));
		userDetails.setPassword(rs.getString("user_pass"));
		userDetails.setRole(rs.getString("user_role"));
		return userDetails;
	}

}

Creating Spring DAO

We need DAO or Repository class to perform database operations.

We have used here Spring JDBC API to perform our database operation. If you want you can use Spring Data JPA API to perform database activities. We are using Spring JdbcTemplate to query our database.

package com.jeejava.spring.security.preauth.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import com.jeejava.spring.security.preauth.jdbc.row.mapper.UserRowMapper;
import com.jeejava.spring.security.preauth.model.User;

@Repository
public class UserDao {

	@Autowired
	private JdbcTemplate jdbcTemplate;

	public User getUser(String username) {
		try {
			final String sql = "select u.user_name user_name, u.user_pass user_pass, ur.user_role user_role from user u, user_role ur where u.user_name = ? and u.user_name = ur.user_name";
			User userDetails = jdbcTemplate.queryForObject(sql, new Object[] { username }, new UserRowMapper());
			return userDetails;
		} catch (EmptyResultDataAccessException ex) {
			return null;// should have proper handling of Exception
		}
	}

}

In the above we have annotated the DAO class with @Repository to segregate the layer for DAO. We have used here spring’s JdbcTemplate to query the database.

Creating VO Class

We will create DTO or VO class to return as a response object in JSON format to the end users or clients. It’s not a good idea to use Entity or Model classes to send as a response object to end users.

The below class represents a blog object, which will be finally returned in JSON format.

package com.jeejava.spring.security.preauth.vo;

import java.util.Date;

import com.fasterxml.jackson.annotation.JsonFormat;

public class BlogVo {

	private String title;
	private String author;
	@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm a z")
	private Date date;
	private String text;

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getAuthor() {
		return author;
	}

	public void setAuthor(String author) {
		this.author = author;
	}

	public Date getDate() {
		return date;
	}

	public void setDate(Date date) {
		this.date = date;
	}

	public String getText() {
		return text;
	}

	public void setText(String text) {
		this.text = text;
	}

}

Creating Spring Service

Create service layer code to process business logic.

In the below service layer we are using spring’s UserDetails, UserDetailsService, GrantedAuthority to handle authentication by spring itself and we are just letting spring know what data spring has to use, nothing else.

package com.jeejava.spring.security.preauth.service;

import java.util.Arrays;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import com.jeejava.spring.security.preauth.dao.UserDao;
import com.jeejava.spring.security.preauth.model.User;

@Service
public class CustomUserDetailsService implements UserDetailsService {

	@Autowired
	private UserDao userDao;

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		User user = userDao.getUser(username);
		if (user == null) {// should have proper handling of Exception
			throw new UsernameNotFoundException("User '" + username + "' not found.");
		}
		GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(user.getRole());
		UserDetails details = new org.springframework.security.core.userdetails.User(user.getUsername(),
				user.getPassword(), Arrays.asList(grantedAuthority));
		return details;
	}

}

Creating Spring REST Controller

Create REST controller class using Spring framework. We have exposed two endpoints in the spring rest controller – one is with base URL (/) and another one with pattern /blogs. So when you hit the base URL with header SM_USER, you should not be asked for credentials but when you hit the URL that is having /blogs with SM_USER, you should have role as ADMIN otherwise you will get Access denied – 403 error.

package com.jeejava.spring.security.preauth.rest.controller;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import com.jeejava.spring.security.preauth.vo.BlogVo;

@RestController
public class UserRestController {

	@GetMapping("/")
	public ResponseEntity<String> defaultPage(Model model) {
		return new ResponseEntity<String>("Welcome to Spring Security PreAuthentication", HttpStatus.OK);
	}

	@GetMapping("/blogs")
	public ResponseEntity<List<BlogVo>> getAllBlogs(Model model) {
		return new ResponseEntity<List<BlogVo>>(getBlogs(), HttpStatus.OK);
	}

	private List<BlogVo> getBlogs() {
		List<BlogVo> blogs = new ArrayList<>();

		BlogVo b1 = new BlogVo();
		b1.setTitle("Spring Security using XML");
		b1.setAuthor("https://www.jeejava.com");
		b1.setText("Spring Security. Autherntication. Authorization.");
		b1.setDate(new Date());

		BlogVo b2 = new BlogVo();
		b2.setTitle("Spring Security using Annotation");
		b2.setAuthor("https://www.jeejava.com");
		b2.setText("Spring Security. Autherntication. Authorization.");
		b2.setDate(new Date());

		BlogVo b3 = new BlogVo();
		b3.setTitle("Spring Security using UserDetailsService");
		b3.setAuthor("https://www.jeejava.com");
		b3.setText("Spring Security. Autherntication. Authorization.");
		b3.setDate(new Date());

		BlogVo b4 = new BlogVo();
		b4.setTitle("Spring MVC using XML");
		b4.setAuthor("https://www.jeejava.com");
		b4.setText("Spring Model-View-Controller.");
		b4.setDate(new Date());

		BlogVo b5 = new BlogVo();
		b5.setTitle("Spring MVC using Annotation");
		b5.setAuthor("https://www.jeejava.com");
		b5.setText("Spring Model-View-Controller.");
		b5.setDate(new Date());

		blogs.add(b1);
		blogs.add(b2);
		blogs.add(b3);
		blogs.add(b4);
		blogs.add(b5);

		return blogs;
	}

}

In the above class we have also defined one private method to declare some dummy data for testing purpose.

Creating Main Class

Spring Boot application created as standalone application and can be easily deployed into embedded Tomcat server using main class.

package com.jeejava.spring.security.preauth;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication(scanBasePackages = "com.jeejava.spring.security.preauth")
public class SpringPreAuthentication {

	public static void main(String[] args) {
		SpringApplication.run(SpringPreAuthentication.class, args);
	}

}

Testing the Application

Run the main class to deploy your application into Tomcat server.

Now when you try to access the URL http://localhost:8080/ without setting SM_USER header, you will see below exception.

spring security pre-authentication example

What went wrong? If you check the console you will see the following error:

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception

org.springframework.security.web.authentication.preauth.PreAuthenticatedCredentialsNotFoundException: SM_USER header not found in request.
	at org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter.getPreAuthenticatedPrincipal

When you access URL http://localhost:8080/ by setting SM_USER:jeejava.com at header.

spring security pre-authentication

When you access URL http://localhost:8080/blogs by setting SM_USER:jeejava.com at header.

spring security pre-authentication

You see above error because the above URL is accessible only for those who have role ADMIN Now set SM_USER:roy and access the same URL.

pre-authentication

The application does not require you to authenticate because it is assumed that the user is already authenticated using Siteminder authentication. So the application requires only authorization using SM_USER header.

It is assumed that the application has authenticated first using Siteminder authentication and ideally the SM_USER header should come after Siteminder authentication.

Siteminder Authentication

Source Code

You can download source code.

Thanks for reading.

Spring Security Pre-authentication Example

You may also like to read:

I am a professional Web developer, Enterprise Application developer, Software Engineer and Blogger and writing blogs is my passion. Connect me on Roy Tutorials | Twitter | Facebook | Linkedin | Reddit | Ello | Tumblr

5 thoughts on “Spring Security Pre-authentication Example

  1. Thanks for this post. I was struggling to implement a solution for SSO with Siteminder and your post helped me a lot. I do have a question though, how can I check for and re-authenticate when SMSESSION expires?

  2. Thanks for the Post. I was working on implement the same thing for my application.
    In case. i have login credentials page where user enter username and password, then spring security authenticate the username and password through LDAP and allow user to access the application.
    But now i have to implement pre-authentication in my current application. so that side minder can pre-authenticate it. I able to create a POC for same. but my question is how my application page will talk with site minder. It would be very helpful if you help me to under stand how site minder is implement for J2EE application.

Leave a Reply

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

Scroll to top