Spring Boot Security Example – Single Sign On Using OAuth 2

Introduction

Here you will see Spring Boot Security Example – Single Sign On using OAuth 2. Single Sign On or simply SSO is a property of access control of multiple related, yet independent, software systems, where a user logs in with a single set of credentials (username and password) to gain access. Here I will use Spring Boot 2.14/2.7.0.

I will let client access my App by granting access through Github OAuth 2 API. You can also use other OAuth 2 API, such as, Google, Facebook etc. Even you can create your own OAuth 2 server.

For this I need to first register an App into Github. It is assumed that you have a Github account.

You may also like to read Spring Security Examples.

What is OAuth 2?

OAuth is a protocol with which a 3-party app can access your data stored in another website without your account and password. For more information on OAuth 2 then you can read https://oauth.net/2/.

Prerequisites

Knowledge on Java, Spring Boot, Spring Security

Github Account, Spring Boot Security, JSP

Java 8+, Spring Boot 2.1.4/2.7.0, Gradle 4.10.2, Maven 3.8.5

Configure OAuth2 App in Github

Login to your Github profile section .

Then click on Settings from dropdown menu by clicking on your profile picture on top right corner. Next click on Developer settings. The below figure shows the highlighted menu:

spring boot security example - single sign on using oauth 2

Now click on New OAuth App as shown in the below figure to register a new application to use OAuth:

spring boot oauth2 sso

Now below screen appears where you have to put the values for the input fields as per your requirements:

spring boot security example single sign on using oauth 2

Remember for Spring Boot 2.7.0, you need to change the Authorization callback URL (in the format of : "${base url}/login/oauth2/code/${registration id}") as follows:

spring boot 2 oauth2 sso

As you see in the above figure I have entered values for the input fields and as I want to show you how it works, so I have just created this app to test our example.

The most of the fields are self-explanatory. The Authorization callback URL is the URL, where you want to redirect your users after authorization was successful.

Once you click on Register application, your application will be created and you will be redirected to a page, where Client ID and Client Secret get generated. The below figure shows similar page:

spring boot security example single sign on using oauth 2

The Client ID and Client Secret are required to authenticate your app. So, copy these two values and keep those values handy because these two values will be configured in application properties/yml file.

Project Setup

Create a gradle or maven based project in your favorite IDE or tool. The project name for Spring Boot Security Example – Single Sign On using OAuth 2 is spring-boot-security-sso-oauth2.

For maven based project, use the following pom.xml build file:

<?xml version="1.0" encoding="UTF-8"?>

<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/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.roytuts</groupId>
	<artifactId>spring-boot-security-sso-oauth2</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<maven.compiler.source>11</maven.compiler.source>
		<maven.compiler.target>11</maven.compiler.target>
	</properties>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.6.7</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-security</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-oauth2-client</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webflux</artifactId>
		</dependency>

		<dependency>
			<groupId>io.projectreactor.netty</groupId>
			<artifactId>reactor-netty</artifactId>
		</dependency>

		<dependency>
			<groupId>org.apache.tomcat.embed</groupId>
			<artifactId>tomcat-embed-jasper</artifactId>
		</dependency>

		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

For gradle based project, use the following build.script file, you can change the spring boot version according to your requirements.

buildscript {
	ext {
		springBootVersion = '2.1.4.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.security.oauth.boot:spring-security-oauth2-autoconfigure:2.1.4.RELEASE")
	compile('org.apache.tomcat.embed:tomcat-embed-jasper:8.5.14')
    compile('javax.servlet:jstl:1.2')
}

In the above build script I have added Spring Boot Security, OAuth2 for using OAuth 2 API in Spring Boot Security.

I have added tomcat-embed-jasper and servlet APIs for JSP support. In this example I will use JSP as the view technology.

Application Config – application.yml

Add application.yml file under classpath directory src/main/resources and modify as below configurations:

Spring Boot 2.7.0:

spring:
  security:
    oauth2:
      client:
        registration:
          github:
            client-id: <client id>
            client-secret: <client secret>
  mvc:
    view:
       prefix: /view/
       suffix: .jsp

server:
  port: 9000

Spring Boot 2.1.4:

#server port
server:
   port: 9000
#OAuth2
security:
   oauth2:
      client:
         clientId: <app's client id>
         clientSecret: <app's client secret>
         accessTokenUri: https://github.com/login/oauth/access_token
         userAuthorizationUri: https://github.com/login/oauth/authorize
         clientAuthenticationScheme: form
      resource:
         userInfoUri: https://api.github.com/user
         preferTokenInfo: false
#view resolver
spring:
   mvc:
      view:
         prefix: /view/
         suffix: .jsp

In the above configuration file, I have let Tomcat server know on which it has to start.

Then we configure OAuth 2 using Github’s Client ID, Client Secret and various other URLs.

I want to use JSP as a view technology, so I have also configured for it. You can use any view technology, such as, HTML.

Security Config Class

You need to create Java config class to configure Spring Security for our OAuth 2. I don’t want to use Spring basic security to authenticate users. I want to use only OAuth 2 API to authenticate our application.

Spring Boot 2.7.0:

For spring boot 2.7.0, @EnableOAuth2Sso has been deprecated, so I am using oauth2Login() method in the security configuration.

package com.roytuts.sso.oauth2.config;

//import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.HttpStatusEntryPoint;

@Configuration
//@EnableOAuth2Sso //deprecated
public class SsoConfig extends WebSecurityConfigurerAdapter {

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.authorizeRequests(a -> a.antMatchers("/", "/error**", "/login**").permitAll().anyRequest().authenticated())
				.exceptionHandling(e -> e.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)))
				.csrf(c -> c.disable()).logout(l -> l.logoutSuccessUrl("/").permitAll()).oauth2Login();
	}

}

Spring Boot 2.1.4:

I have enabled OAuth 2 using @EnableOAuth2Sso annotation on our config class:

package com.roytuts.sso.oauth2.config;
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableOAuth2Sso
public class SsoConfig extends WebSecurityConfigurerAdapter {
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.authorizeRequests().antMatchers("/", "/user", "/login**", "/error**").permitAll().anyRequest()
				.authenticated().and().logout().logoutSuccessUrl("/").permitAll();
		http.csrf().disable();
	}
}

In the above config class, I have defined URL patterns, such as, “/”, “/user”, “/login”, “/error” to avoid authentication.

I have called built-in logout functionality to logout the users.

I have also disabled csrf tokens. If you want to use csrf tokens then you don’t need to disable it because by default it is enabled.

REST Controller

I will create Spring REST Controller to retrieve authenticated user’s information.

Spring Boot 2.7.0:

package com.roytuts.sso.oauth2.controller;

import java.util.Collections;
import java.util.Map;

import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

	@GetMapping("/user")
	public Map<String, Object> user(@AuthenticationPrincipal OAuth2User principal) {
		return Collections.singletonMap("name", principal.getAttribute("name"));
	}
}

Spring Boot 2.1.4:

package com.roytuts.sso.oauth2.controller;
import java.security.Principal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
	@GetMapping("/user")
	public Principal user(Principal principal) {
		return principal;
	}
}

Because the “home” controller needs an endpoint at “/user” that describes the currently authenticated user as shown in next section.

Controller Class

I will create Spring Controller to show the home page in the browser.

package com.roytuts.sso.oauth2.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HomeController {
	@GetMapping("/")
	public String home() {
		return "index";
	}
}

The above controller method when URI matches to “/”, it calls the index page and displays its content on the browser.

View

Create below view called index.jsp and put it under src/main/webapp/view directory.

Spring Boot 2.7.0:

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
	pageEncoding="ISO-8859-1"%>
<!DOCTYPE html>
<html>
<title>Spring Boot OAuth2 SSO Example</title>
<head>
	<!-- <script src="http://code.jquery.com/jquery-3.3.1.min.js"></script> -->
	<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
	<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
	<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
	<script type="text/javascript">
	    $.get("/user", function(data) {
	        $("#user").html(data.name);
	        $(".unauthenticated").hide()
	        $(".authenticated").show()
	    });
	    function logout() {
	        $.post("/logout", function() {
	            $("#user").html('');
	            $(".unauthenticated").show();
	            $(".authenticated").hide();
	        })
	        return true;
	    }
	</script>
</head>
<body>
	<div class="container unauthenticated">
		Login With Github <a href="/oauth2/authorization/github">click here</a>
	</div>
	<div class="container authenticated" style="display: none">
		Logged in as: <span id="user"></span>
		<div>
			<button onClick="logout()" class="btn btn-primary">Logout</button>
		</div>
	</div>
</body>
</html>

Spring Boot 2.1.4:

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
	pageEncoding="ISO-8859-1"%>
<!DOCTYPE html>
<html>
<title>Spring Boot OAuth2 SSO Example</title>
<head>
	<script src="http://code.jquery.com/jquery-3.3.1.min.js"></script>
	<script type="text/javascript">
	    $.get("/user", function(data) {
	        $("#user").html(data.userAuthentication.details.name);
	        $(".unauthenticated").hide()
	        $(".authenticated").show()
	    });
	    function logout() {
	        $.post("/logout", function() {
	            $("#user").html('');
	            $(".unauthenticated").show();
	            $(".authenticated").hide();
	        })
	        return true;
	    }
	</script>
</head>
<body>
	<div class="container unauthenticated">
		Login With Github <a href="/login">click here</a>
	</div>
	<div class="container authenticated" style="display: none">
		Logged in as: <span id="user"></span>
		<div>
			<button onClick="logout()" class="btn btn-primary">Logout</button>
		</div>
	</div>
</body>
</html>

The above code is self-explanatory. I am showing the Login link or Logout button depending upon whether a user has been authenticated or not.

I have included jQuery library to work with the jQuery API. I am using jQuery’s get method – $.get() – to get the authenticated user’s information and I show the Logout button with user’s name.

I have added logout() function with onClick event on Logout button and call the standard “/logout” URI from Spring Security’s built-in API.

Spring Boot Main Class

A class having main method and @SpringBootApplication annotation will be enough to deploy the spring boot application into the embedded Tomcat server.

Spring Boot 2.7.0:

package com.roytuts.sso.oauth2;

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

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

Spring Boot 2.1.4:

package com.roytuts.sso.oauth2.main;

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

@SpringBootApplication(scanBasePackages = "com.roytuts.sso.oauth2")
public class SsoOAuth2Application {

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

Console Output

Run the above main class and when server is up then you will following output in the console:

[           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 9000 (http) with context path ''
[           main] c.j.s.oauth2.main.SsoOAuth2Application   : Started SsoOAuth2Application in 7.696 seconds (JVM running for 8.568)

Testing OAuth 2 SSO Security

When you hit the URL http://localhost:9000 then user will see below page:

single sign on using oauth2

So user is not able to see the home page of the URL and user needs to authenticate using Github account.

Now when user clicks on click here link, then user will be redirected to the Github login page:

spring boot oauth 2 sso security example

Once user enters username/password, then user needs to authorize the page:

single sign on using oauth 2

Now app will ask user (for example, here roytuts) to authorize in order to access your application. Therefore user needs to click on Authorize roytuts (user) and the user will be redirected to the below page:

spring boot security example

So now user will be able to see the Logout button as the user has been authorized.

Hope you got idea on Spring Boot Security Example – Single Sing On using OAuth 2.

Source Code

source code

Leave a Reply

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