If you have a few years of experience in the Java ecosystem, and you're interested in sharing that experience with the community (and getting paid for your work of course), have a look at the "Write for Us" page. Cheers. Eugen

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:

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

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 log in 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 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 primary 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 {
    @GetMapping("/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

Sort by:   newest | oldest | most voted
Stefano Cazzola
Guest

Very good tutorial, as usual. But what about not using SpringBoot? I mean, it can be very convenient, I don’t deny that, but it also hides what goes on under the wood giving a sense of automagical (or informagical…) that personally makes me get lost. And it’s days now that I’m trying to have this very same scenarion working without Spring Boot

Grzegorz Piwowarek
Guest

In this case, I would investigate what @EnableOAuth2Sso annotation actually does by looking what annotations it aggregates and then have a look at their implementations. After all, Spring Boot’s magic is simply default configuration classes that fire up when no other configs are present.

http://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/autoconfigure/security/oauth2/client/EnableOAuth2Sso.html

Stefano Cazzola
Guest

Thanks for the hint.

Jack
Guest

I have a question about `http.requestMatchers().antMatchers(“/login”, “/oauth/authorize”)`. When I include this it works fine, however I want to support both regular form login in addition to oAuth login. If I remove the above or change it to `”/**”` the sp gets an `org.springframework.security.oauth2.common.exceptions.InvalidTokenException`. Do you know how I can fix this?

Eugen Paraschiv
Guest

Hey @disqus_tFKAIUkKXT:disqus – it sounds like you need to support multiple authentication providers – have a look at this writeup.
Of course that’s not the only option to implement multiple logins, as you’re describing, but it’s a good, standard way to go.

Jack
Guest

I may be wrong but does the above link show how to use different user data stores, LDAP, database etc. In my case I want the user to use normal form login (all endpoints secured) or acting as an idP by using oauth for another SP. In both scenarios the same login form should be used and the same authentication provider (a database).

Eugen Paraschiv
Guest

Hey @disqus_tFKAIUkKXT:disqus – so the focus of that writeup is to show how you can use multiple auth providers. But, it sounds like that might not be your exact scenario.
So, in order for me to understand, you’ll have to explain your scenario in a lot more detail. Feel free to do that over email or simply in a StackOverflow question (follow up with the link here).

Cheers,
Eugen.

Jack
Guest
Hi @baeldung:disqus . Actually I solved the problem. I simply needed to add my apis to the matcher. (along with other stuff I wanted secured) http.requestMatchers().antMatchers("/login", "/oauth/authorize", "/apis/*") The key was the following statement on the documentation http://projects.spring.io/spring-security-oauth/docs/oauth2.html “Note: if your Authorization Server is also a Resource Server then there is another security filter chain with lower priority controlling the API resources. Fo those requests to be protected by access tokens you need their paths not to be matched by the ones in the main user-facing filter chain, so be sure to include a request matcher that picks out only… Read more »
Povilas
Guest

I don’t understand… I log in and access the securedPage.html successfully, when I open other browser and application asks me to log in again. I thought this is called “single sign on” and the second time client application should not ask me to log in in the same computer. Am I missing something?

Eugen Paraschiv
Guest

So, this is a very simple example, which is why there’s no automatic redirect. What you’re seeing is the standard login page. But – if, on your second visit (when you’re already logged in) – you try to log in again, you’ll noticed you’re not promoted to log in, you’re simply redirected to where you need to go.
Hope that clears things up.
Cheers,
Eugen.

Ruslan Stelmachenko
Guest
No, you understand SSO incorrectly. Single Sign On in web-page context is not means your computer remember you. It means your browser remember you and if you then log in into another application using same authorization server (and the same browser!), you will be logged in automatically (not asking login and password again). But it not means your another browser also logs you in. Another browser knows nothing about cookies of your first browser. And cookies is how it works. When your frist restricted resource redirects you to authorization server, and then you log in successfully, this authorization server creates… Read more »
Dinesh
Guest

Hi,
I am newbie for spring security & OAuth2. I download the project & run on my local. But I am unable to understand control flow of the application. For me thr is no login page when click on the login on the ui ‘http://localhost:8082/ui/’. Error page appear when click on the Longin button.

Please let me know where I am doing wrong.

Whitelabel Error Page

This application has no explicit mapping for /error, so you are seeing this as a fallback.
Sun Aug 13 23:12:29 IST 2017
There was an unexpected error (type=Forbidden, status=403).
Access Denied

Grzegorz Piwowarek
Editor

Dinesh, it looks like it’s our fault – please pull the newest changes and see if it works now

Diego
Guest

I have the same problem…. someone can solve this problem

Grzegorz Piwowarek
Editor

I believe it’s fixed now – check the latest version of the code and article

Mike
Guest

I am getting following error after login page.
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Wed Aug 16 11:54:43 CDT 2017
There was an unexpected error (type=Forbidden, status=403).
Access Denied

Once login I am expecting to see secured page however not getting that.
Any Idea what it could be ?

I have all three applications running. Is there anything else I need to have ?

Thanks
Mike

Grzegorz Piwowarek
Editor

Mike, I believe we might have misplaced one configuration during the refactor. Please pull the newest changes and see if it helps

wpDiscuz