The new Certification Class of Learn Spring Security is out:

>> CHECK OUT THE COURSE

1. Overview

In this tutorial, we’ll discuss how to implement SSO – Single Sign On – using Spring Security OAuth and Spring Boot.

We’ll use three separate applications:

  • An Authorization Server – which is the central authentication mechanism
  • Two Client Applications: the applications using SSO

Very simply put, when a user tries to access a secured page in the client app, they’ll be redirected to authenticate first, via the Authentication Server.

And we’re going to use the Authorization Code grant type out of OAuth2 to drive the delegation of authentication.

2. The Client App

Let’s start with our Client Application; we’ll, of course, use Spring Boot to minimize the configuration:

2.1. Maven Dependencies

First, we will need the following dependencies in our pom.xml:

<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.oauth</groupId>
    <artifactId>spring-security-oauth2</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>

2.2. Security Configuration

Next, the most important part, the security configuration of our client application:

@EnableOAuth2Sso
@SpringBootApplication
public class UiApplication extends WebSecurityConfigurerAdapter {
    
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.antMatcher("/**")
            .authorizeRequests()
            .antMatchers("/", "/login**").permitAll()
            .anyRequest().authenticated();
    }

    @Bean
    public RequestContextListener requestContextListener() {
        return new RequestContextListener();
    }

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

The core part of this configuration is, of course, the @EnableOAuth2Sso annotation we’re using to enable Single Sign On.

Note that we need to extend the WebSecurityConfigurerAdapter – without it, all the paths will be secured – so the users will be redirected to login when they try to access any page. In our case here, the index and login pages are the only pages that can be accessed without authentication.

Finally, we also defined a RequestContextListener bean to handle requests scopes.

And the application.yml:

server:
    port: 8082
    context-path: /ui
    session:
      cookie:
        name: UISESSION
security:
  basic:
    enabled: false
  oauth2:
    client:
      clientId: SampleClientId
      clientSecret: secret
      accessTokenUri: http://localhost:8081/auth/oauth/token
      userAuthorizationUri: http://localhost:8081/auth/oauth/authorize
    resource:
      userInfoUri: http://localhost:8081/auth/user/me
spring:
  thymeleaf:
    cache: false

A few quick notes:

  • we disabled the default Basic Authentication
  • accessTokenUri is the URI to obtain the Access Tokens
  • userAuthorizationUri is the authorization URI that users will be redirected to
  • userInfoUri the URI of user endpoint to obtain current user details

Also note that, in our example here, we rolled out our own Authorization Server, but of course we can also use other, third-party providers such as Facebook or GitHub.

2.3. Front End

Now, let’s take a look at the front-end configuration of our client application. We’re not going to focus on that here, mainly because we already covered in on the site.

Our client application here has a very simple front-end; here’s the index.html:

<h1>Spring Security SSO</h1>
<a href="securedPage">Login</a>

And the securedPage.html:

<h1>Secured Page</h1>
Welcome, <span th:text="${#authentication.name}">Name</span>

The securedPage.html page needed the users to be authenticated. If a non-authenticated user tries to access securedPage.html, they’ll be redirected to the login page first.

3. The Auth Server

Now let’s discuss our Authorization Server here.

3.1. Maven Dependencies

First, we need to define the dependencies in our pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security.oauth</groupId>
    <artifactId>spring-security-oauth2</artifactId>
</dependency>

3.2. OAuth Configuration

It’s important to understand that we’re going to run the Authorization Server and the Resource Server together here, as a single deployable unit.

Let’s start with the configuration of our Resource Server – which doubles as our main Boot application:

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

Then, we’ll configure our Authorization server:

@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private AuthenticationManager authenticationManager;

    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        oauthServer.tokenKeyAccess("permitAll()")
                   .checkTokenAccess("isAuthenticated()");
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
               .withClient("SampleClientId")
               .secret("secret")
               .authorizedGrantTypes("authorization_code")
               .scopes("user_info")
               .autoApprove(true) ; 
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager);
    }
}

Note how we’re only enabling a simple client using the authorization_code grant type.

Also, note how autoApprove is set to true so that we’re not redirected and promoted to manually approve any scopes.

3.3. Security Configuration

First, we’ll disable the default Basic Authentication, via our application.properties:

server.port=8081
server.context-path=/auth
security.basic.enabled=false

Now, let’s move to the configuration and define a simple form login mechanism:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.requestMatchers()
            .antMatchers("/login", "/oauth/authorize")
            .and()
            .authorizeRequests()
            .anyRequest().authenticated()
            .and()
            .formLogin().permitAll();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.parentAuthenticationManager(authenticationManager)
            .inMemoryAuthentication()
            .withUser("john").password("123").roles("USER");
    }
}

Note that we used simple in-memory authentication, but we can simply replace it with a custom userDetailsService.

3.4. User Endpoint

Finally, we will create our user endpoint we used earlier in our configuration:

@RestController
public class UserController {
    @RequestMapping("/user/me")
    public Principal user(Principal principal) {
        return principal;
    }
}

Naturally, this will return the user data with a JSON representation.

4. Conclusion

In this quick tutorial, we focused on implementing Single Sign On using Spring Security Oauth2 and Spring Boot.

As always, the full source code can be found over on GitHub.

Go deeper into Spring Security with the course:

>> LEARN SPRING SECURITY