Test Spring Security Auth with JUnit

Learn to test Spring security authentication using JUnit testcase using InMemoryDaoImpl. Also learn to build fully populated authentication object programmatically and then use it in application.

SecurityContextHolder

Spring security is based on security context, which is kind of static in nature. This essentially means that your do not need to inject its reference into your beans or classes in spring container. You can access the spring context anytime simply using SecurityContextHolder.getContext() method.

This context has the reference of actual principal or user which we have to validate for its access permissions.

Unit test Spring Security

I am creating a very simple maven project and will write minimal code so that I can focus on testing only what is in scope of this post i.e. authentication. Then I will write a demo service class with a single method which required “ROLE_USER” to access it. If you try to access this method and you do not have “ROLE_USER“, you will get the expected AccessDeniedException. Simple enough, isn’t it?

Step 1) Project setup

Lets create the java project using below command:

$ mvn archetype:generate -DgroupId=com.howtodoinjava 
							-DartifactId=SpringPasswordHashingDemo
							-DarchetypeArtifactId=maven-archetype-quickstart 
							-DinteractiveMode=false

Now update the pom.xml with below dependencies and run command mvn:eclipse:eclipse to make project eclipse supported.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.howtodoinjava</groupId>
  <artifactId>SpringPasswordHashingDemo</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>SpringPasswordHashingDemo</name>
  <url>http://maven.apache.org</url>
  <properties>
    <org.springframework.version>3.0.5.RELEASE</org.springframework.version>
  </properties>
  <dependencies>
  	 <!-- Spring Core -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.4</version>
      <scope>test</scope>
    </dependency>
    <dependency>
		<groupId>cglib</groupId>
		<artifactId>cglib</artifactId>
		<version>2.2</version>
	</dependency>
	<dependency>
	  <groupId>org.springframework</groupId>
	  <artifactId>spring-core</artifactId>
	  <version>${org.springframework.version}</version>
	</dependency>
	<dependency>
	  <groupId>org.springframework</groupId>
	  <artifactId>spring-expression</artifactId>
	  <version>${org.springframework.version}</version>
	</dependency>
	<dependency>
	  <groupId>org.springframework</groupId>
	  <artifactId>spring-beans</artifactId>
	  <version>${org.springframework.version}</version>
	</dependency>
	<dependency>
	  <groupId>org.springframework</groupId>
	  <artifactId>spring-context</artifactId>
	  <version>${org.springframework.version}</version>
	</dependency>
	<dependency>
	  <groupId>org.springframework</groupId>
	  <artifactId>spring-context-support</artifactId>
	  <version>${org.springframework.version}</version>
	</dependency>
	<dependency>
	  <groupId>org.springframework</groupId>
	  <artifactId>spring-test</artifactId>
	  <version>${org.springframework.version}</version>
	  <scope>test</scope>
	</dependency>
    
    <!-- Spring Security -->
	<dependency>
		<groupId>org.springframework.security</groupId>
		<artifactId>spring-security-core</artifactId>
		<version>${org.springframework.version}</version>
		<type>jar</type>
		<scope>compile</scope>
	</dependency>
	<dependency>
		<groupId>org.springframework.security</groupId>
		<artifactId>spring-security-web</artifactId>
		<version>${org.springframework.version}</version>
		<type>jar</type>
		<scope>compile</scope>
	</dependency>
	<dependency>
		<groupId>org.springframework.security</groupId>
		<artifactId>spring-security-config</artifactId>
		<version>${org.springframework.version}</version>
		<type>jar</type>
		<scope>compile</scope>
	</dependency>
	<dependency>
		<groupId>org.springframework.security</groupId>
		<artifactId>spring-security-taglibs</artifactId>
		<version>${org.springframework.version}</version>
		<type>jar</type>
		<scope>compile</scope>
	</dependency>
	
  </dependencies>
</project>

Step 2) Create security configuration file

I have create application-security.xml file and put the security configuration inside it.

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
	xmlns:beans="http://www.springframework.org/schema/beans" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
	http://www.springframework.org/schema/security/
	http://www.springframework.org/schema/security/spring-security-3.0.3.xsd">
	
	<global-method-security secured-annotations="enabled" />

	<authentication-manager alias="authenticationManager">
        <authentication-provider>
            <user-service>
                <user name="lokesh" password="password1" authorities="ROLE_USER" />
                <user name="admin" password="password2" authorities="ROLE_ADMIN" />
            </user-service>
        </authentication-provider>
    </authentication-manager>
    
    <beans:bean id="demoService" class="com.howtodoinjava.DemoService"/>
    
</beans:beans>

Step 3) Create secured method

package com.howtodoinjava;

import org.springframework.security.access.annotation.Secured;

public class DemoService
{
	@Secured("ROLE_USER")
	public void method()
	{
		System.out.println("Method called");
	}
}

Step 4) Test the authentication with JUnit test

In junit tests, we will configure the spring context programmatically and then will access the users by username from default user details service. In out case, it is in-memory implementation which in your case might differ to some jdbc based user details service or some other custom user detail service also. So, please modify the look up accordingly.

We will test various scenarios like valid user, invalid user, invalid role etc. You can add/ remove scenarios based on your choice.

package com.howtodoinjava;

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

import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.memory.InMemoryDaoImpl;

public class TestDemoService {
	
	static ApplicationContext applicationContext = null;
	static InMemoryDaoImpl userDetailsService = null;
	
	/**
	 * Initialize the application context to re-use in all test cases
	 * */
	@BeforeClass
	public static void setup()
	{
		//Create application context instance
		applicationContext = new ClassPathXmlApplicationContext("application-security.xml");
		//Get user details service configured in configuration 
		userDetailsService = applicationContext.getBean(InMemoryDaoImpl.class);
	}
	
	/**
	 * Test the valid user with valid role
	 * */
	@Test 
	public void testValidRole()
	{
		//Get the user by username from configured user details service
		UserDetails userDetails = userDetailsService.loadUserByUsername ("lokesh");
		Authentication authToken = new UsernamePasswordAuthenticationToken (userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities());
		SecurityContextHolder.getContext().setAuthentication(authToken);
		DemoService service = (DemoService) applicationContext.getBean("demoService");
		service.method();
	}
	
	/**
	 * Test the valid user with INVALID role
	 * */
	@Test (expected = AccessDeniedException.class)
	public void testInvalidRole()
	{
		UserDetails userDetails = userDetailsService.loadUserByUsername ("lokesh");
		List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
		authorities.add(new GrantedAuthorityImpl("ROLE_INVALID"));
		Authentication authToken = new UsernamePasswordAuthenticationToken (userDetails.getUsername(), userDetails.getPassword(), authorities);
		SecurityContextHolder.getContext().setAuthentication(authToken);
		DemoService service = (DemoService) applicationContext.getBean("demoService");
		service.method();
	}
	
	/**
	 * Test the INVALID user 
	 * */
	@Test (expected = AccessDeniedException.class)
	public void testInvalidUser()
	{
		UserDetails userDetails = userDetailsService.loadUserByUsername ("admin");
		List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
		authorities.add(new GrantedAuthorityImpl("ROLE_INVALID"));
		Authentication authToken = new UsernamePasswordAuthenticationToken (userDetails.getUsername(), userDetails.getPassword(), authorities);
		SecurityContextHolder.getContext().setAuthentication(authToken);
		DemoService service = (DemoService) applicationContext.getBean("demoService");
		service.method();
	}
	
}

As you can see that all the test cases are passing as expected.

Happy Leaning !!

Comments

Subscribe
Notify of
guest
7 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.

Our Blogs

REST API Tutorial

Dark Mode

Dark Mode