Simple Single Sign-On with Spring Security OAuth2 (legacy stack) – 使用Spring Security OAuth2的简单单点登录(传统栈)

最后修改: 2020年 7月 1日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

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

在本教程中,我们将讨论如何使用Spring Security OAuth和Spring Boot实现SSO–单点登录。

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.

我们将使用OAuth2中的授权代码赠与类型来驱动认证的委托。

Note: this article is using the Spring OAuth legacy project. For the version of this article using the new Spring Security 5 stack, have a look at our article Simple Single Sign-On with Spring Security OAuth2.

注意:本文使用的是Spring OAuth 传统项目。对于使用新的 Spring Security 5 堆栈的本文版本,请查看我们的文章使用 Spring Security OAuth2 的简单单点登录

2. The Client App

2.客户应用

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

让我们从我们的客户应用开始;当然,我们将使用Spring Boot来最小化配置。

2.1. Maven Dependencies

2.1.Maven的依赖性

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

首先,我们在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.boot</groupId>
    <artifactId>spring-security-oauth2-autoconfigure</artifactId>
    <version>2.0.1.RELEASE</version>
</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

2.2.安全配置

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.

当然,这个配置的核心部分是@EnableOAuth2Sso注解,我们要用它来启用单点登录。

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.

请注意,我们需要扩展WebSecurityConfigurerAdapter – 没有它,所有的路径都是安全的 – 所以当用户试图访问任何页面时,都会被重定向到登录。在我们的案例中,索引和登录页面是唯一可以不经认证而访问的页面。

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

最后,我们还定义了一个RequestContextListener bean来处理请求范围。

And the application.yml:

还有application.yml

server:
    port: 8082
    servlet:
        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.

还要注意的是,在我们这里的例子中,我们推出了我们的授权服务器,当然我们也可以使用其他的、第三方的供应商,如Facebook或GitHub。

2.3. Front End

2.3.前端

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:

我们的客户应用在这里有一个非常简单的前端;这里是index.html

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

And the securedPage.html:

还有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.

securedPage.html页面需要用户进行认证。如果一个未经认证的用户试图访问securedPage.html,他们将首先被重定向到登录页面。

3. The Auth Server

3.认证服务器

Now let’s discuss our Authorization Server here.

现在让我们在这里讨论一下我们的授权服务器。

3.1. Maven Dependencies

3.1.Maven的依赖性

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

首先,我们需要在我们的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>
    <version>2.3.3.RELEASE</version>
</dependency>

3.2. OAuth Configuration

3.2.OAuth配置

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:

让我们从资源服务器的配置开始–它是我们主要的Boot应用程序。

@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 BCryptPasswordEncoder passwordEncoder;

    @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(passwordEncoder.encode("secret"))
          .authorizedGrantTypes("authorization_code")
          .scopes("user_info")
          .autoApprove(true) 
          .redirectUris(
            "http://localhost:8082/ui/login","http://localhost:8083/ui2/login"); 
    }
}

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

请注意,我们只启用了一个使用authorization_code授予类型的简单客户端。

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

另外,请注意autoApprove是如何被设置为 “true “的,这样我们就不会被重定向和推广,以手动批准任何作用域。

3.3. Security Configuration

3.3.安全配置

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

首先,我们将通过我们的application.properties,禁用默认的基本认证。

server.port=8081
server.servlet.context-path=/auth

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

现在,让我们转到配置,定义一个简单的表单登录机制。

@Configuration
@Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @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.inMemoryAuthentication()
            .withUser("john")
            .password(passwordEncoder().encode("123"))
            .roles("USER");
    }
    
    @Bean 
    public BCryptPasswordEncoder passwordEncoder(){ 
        return new BCryptPasswordEncoder(); 
    }
}

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

请注意,我们使用了简单的内存认证,但我们可以简单地用一个自定义的userDetailsService来代替它。

3.4. User Endpoint

3.4.用户端点

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.

当然,这将返回具有JSON表示的用户数据。

4. Conclusion

4.结论

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

在这个快速教程中,我们重点介绍了使用Spring Security Oauth2和Spring Boot实现单点登录。

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

一如既往,完整的源代码可以在GitHub上找到