前段时间有人问到单点登录如何实现,那本篇就来介绍一下单点登录的实现。由于单点登录涉及到多个客户端,本篇的客户端就以订单服务、商品服务为例。以下是单点登录中,订单服务、商品服务、认证服务器的交互时序图。

Spring Authorization Server (十一)单点登录-SSO_OAuth2.1

1. 认证服务器搭建

认证服务的搭建可以参照本系列中的《Spring Authorization Server (二)认证服务器搭建》一篇,将该章节所搭建的认证服务器直接启动即可作为SSO认证服务器。如果忍受不了框架自带的登录页面和授权确认页面卡顿的情况,也可以选择《Spring Authorization Server (五)设备授权码登录》这一篇所搭建的服务器作为SSO认证服务器,也是直接启动即可。本篇我们将认证服务器工程取名为spring-oauth-sso-server。

2. 订单服务搭建

订单服务作为客户端,在本系列的《Spring Authorization Server (三)客户端搭建》一篇中,我们也是介绍了客户端的搭建。其实在《Spring Authorization Server (三)客户端搭建》一篇中,已经完成了单点登录的一部分工作了,我们现在将该章节所搭建的客户端代码复制一份,工程取名为spring-oauth-sso-client-order,在此基础上进行修改。为了添加演示所需的交互页面,我们需要添加thymeleaf相关的maven依赖以及一些html页面。

pom.xml文件内容如下。

<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>
    <parent>
        <groupId>org.oauth</groupId>
        <artifactId>spring-oauth-parent</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <artifactId>spring-oauth-sso-client-order</artifactId>
    <name>SSO客户端-订单服务</name>

    <properties>
        <spring-security.version>6.1.2</spring-security.version>
    </properties>

    <dependencies>

        <!--spring-boot-starter-oauth2-client-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-oauth2-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- spring-boot-starter-thymeleaf -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <!-- webjars-locator-core -->
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>webjars-locator-core</artifactId>
        </dependency>
        <!-- bootstrap:5.2.3 -->
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>bootstrap</artifactId>
            <version>5.2.3</version>
        </dependency>
        <!-- jquery:3.6.4 -->
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>jquery</artifactId>
            <version>3.6.4</version>
        </dependency>

    </dependencies>

</project>

application.yml配置如下。

server:
  ip: spring-oauth-client-order
  port: 9003

logging:
  level:
    org.springframework.security: trace

spring:
  application:
    name: spring-oauth-client-order
  security:
    oauth2:
      client:
        provider:
          #认证服务器信息
          oauth-server:
            #授权地址
            issuer-uri: http://spring-oauth-server:9000
            authorizationUri: ${spring.security.oauth2.client.provider.oauth-server.issuer-uri}/oauth2/authorize
            #令牌获取地址
            tokenUri: ${spring.security.oauth2.client.provider.oauth-server.issuer-uri}/oauth2/token
        registration:
          messaging-client-oidc:
            #认证提供者,标识由哪个认证服务器进行认证,和上面的oauth-server进行关联
            provider: oauth-server
            #客户端名称
            client-name: web平台-SSO客户端-订单服务
            #客户端id,从认证平台申请的客户端id
            client-id: web-client-id-order
            #客户端秘钥
            client-secret: secret
            #客户端认证方式
            client-authentication-method: client_secret_basic
            #使用授权码模式获取令牌(token)
            authorization-grant-type: authorization_code
            #回调地址,接收认证服务器回传code的接口地址,之前我们是使用http://www.baidu.com代替
            redirect-uri: http://spring-oauth-client-order:9003/login/oauth2/code/messaging-client-oidc
            scope:
              - profile
              - openid

上面的配置中,我们将spring-oauth-client-order服务的端口设置为9003,为方便在本地进行测试,我们在C:\Windows\System32\drivers\etc目录下的hosts文件添加了127.0.0.1 spring-oauth-client-order spring-oauth-client-product域名映射.

在resource目录下创建templates文件夹,添加index.html、order_1.html、order_2.html三个测试页面用以测试。

index.html内容如下。

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Spring Authorization Server sample</title>
    <link rel="stylesheet" href="/webjars/bootstrap/css/bootstrap.css" th:href="@{/webjars/bootstrap/css/bootstrap.css}" />
  </head>
  <body>
    <div class="container" align="center">
      <h1>订单服务</h1>
      <h2 style="color: darkgoldenrod;">这是订单服务首页,访问该页面,无需登录。</h2>
    </div>
  </body>
</html>

order_1.html内容如下。

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Spring Authorization Server sample</title>
    <link rel="stylesheet" href="/webjars/bootstrap/css/bootstrap.css" th:href="@{/webjars/bootstrap/css/bootstrap.css}" />
  </head>
  <body>
    <div class="container" align="center">
      <h1>订单服务</h1>
      <h2 style="color: blue">订单页面1,认证信息如下。</h2>
      <div>
        <span th:text="${authentication}">认证信息:</span>
      </div>
      <h2><a href="http://spring-oauth-client-order:9003/order2">跳转到订单页面2</a></h2>
      <h2><a href="http://spring-oauth-client-product:9004/product2">跳转到商品页面2</a></h2>
    </div>
  </body>
</html>

order_2.html内容如下。

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Spring Authorization Server sample</title>
    <link rel="stylesheet" href="/webjars/bootstrap/css/bootstrap.css" th:href="@{/webjars/bootstrap/css/bootstrap.css}" />
  </head>
  <body>
    <div class="container" align="center"  >
      <h1>订单服务</h1>
      <h2 style="color: red">订单页面2,认证信息如下。</h2>
      <div>
        <span th:text="${authentication}">认证信息:</span>
      </div>
      <h2><a href="http://spring-oauth-client-order:9003/order1">跳转到订单页面1</a></h2>
      <h2><a href="http://spring-oauth-client-product:9004/product1">跳转到商品页面1</a></h2>

    </div>
  </body>
</html>

在org.oauth.client.controller目录下添加OrderController,代码如下。

@Controller
public class OrderController {

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

	@GetMapping("/order1")
	public String order1(Model model) {
		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
		System.out.println("authentication:"+ authentication);

		model.addAttribute("authentication",authentication);
		return "order_1";
	}

	@GetMapping("/order2")
	public String order2(Model model) {
		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
		System.out.println("authentication:"+ authentication);

		model.addAttribute("authentication",authentication);
		return "order_2";
	}

}

在org.oauth.client包路径下新建config目录,添加WebSecurityConfig配置类,内容如下。

package org.oauth.client.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.client.endpoint.DefaultAuthorizationCodeTokenResponseClient;
import org.springframework.security.web.SecurityFilterChain;

/**
 * @author Rommel
 * @version 1.0
 * @date 2023/8/13-21:51
 * @description TODO
 */
@Configuration
public class WebSecurityConfig {
    @Bean
    public SecurityFilterChain authorizationClientSecurityFilterChain(HttpSecurity http) throws Exception {

                http
                        // 请求路径管理
                        .authorizeHttpRequests(authorize -> authorize
                                .requestMatchers("/assets/**", "/webjars/**", "/index").permitAll()
                                .anyRequest().authenticated()
                        )
                        // Session会话管理
                        .sessionManagement(sessionManagementConfigurer->{
                            sessionManagementConfigurer.sessionCreationPolicy(SessionCreationPolicy.ALWAYS);
                        })
                        //OAuth2.0登录配置
                        .oauth2Login(oauth2LoginCustomizer->{
                            // 设置用以获取token请求的客户端
                            oauth2LoginCustomizer.tokenEndpoint(tokenEndpointCustomizer->{
                                //采用DefaultAuthorizationCodeTokenResponseClient默认处理
                                tokenEndpointCustomizer.accessTokenResponseClient(new DefaultAuthorizationCodeTokenResponseClient());
                            });
                        })
                ;

        return http.build();
    }
}

上面的配置信息中,我们对 "/assets/**", "/webjars/**", "/index" 路径做了放行,其他请求都需要进行认证;对session会话管理进行了设置,将session会话创建策略设置为SessionCreationPolicy.ALWAYS;对OAuth2.0登录配置进行手动设置。

说明:此处的OAuth2.0登录配置,如果是使用Spring Boot2.6.8及以下的版本,可以添加以下的依赖,

<dependency>
  <groupId>org.springframework.security.oauth.boot</groupId>
  <artifactId>spring-security-oauth2-autoconfigure</artifactId>
  <version>2.6.8</version>
</dependency>

然后使用@EnableOAuth2Sso注解即可,本客户端工程使用的是Spring Boot3.1.1版本,需要手动设置oauth2Login,否则请求受保护的资源将不会重定向到授权服务器进行认证,而是直接返回403拒绝。关于oauth2Login的配置,可以参照官方文档:https://docs.spring.io/spring-security/reference/servlet/oauth2/login/advanced.html

3. 订单服务测试

oauth2_registered_client表增加一条订单服务的客户端,记录如下。

INSERT INTO `oauth-server`.`oauth2_registered_client` (`id`, `client_id`, `client_id_issued_at`, `client_secret`, `client_secret_expires_at`, `client_name`, `client_authentication_methods`, `authorization_grant_types`, `redirect_uris`, `post_logout_redirect_uris`, `scopes`, `client_settings`, `token_settings`) VALUES ('3eacac0e-0de9-4727-9a64-6bdd4be2ee3', 'web-client-id-order', '2023-07-12 07:33:42', '$2a$10$.J0Rfg7y2Mu8AN8Dk2vL.eBFa9NGbOYCPOAFEw.QhgGLVXjO7eFDC', NULL, 'web平台-SSO客户端-订单服务', 'client_secret_basic', 'refresh_token,authorization_code', 'http://spring-oauth-client-order:9003/login/oauth2/code/messaging-client-oidc', 'http://127.0.0.1:9000/', 'openid,profile', '{\"@class\":\"java.util.Collections$UnmodifiableMap\",\"settings.client.require-proof-key\":false,\"settings.client.require-authorization-consent\":true}', '{\"@class\":\"java.util.Collections$UnmodifiableMap\",\"settings.token.reuse-refresh-tokens\":true,\"settings.token.id-token-signature-algorithm\":[\"org.springframework.security.oauth2.jose.jws.SignatureAlgorithm\",\"RS256\"],\"settings.token.access-token-time-to-live\":[\"java.time.Duration\",1800.000000000],\"settings.token.access-token-format\":{\"@class\":\"org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat\",\"value\":\"self-contained\"},\"settings.token.refresh-token-time-to-live\":[\"java.time.Duration\",3600.000000000],\"settings.token.authorization-code-time-to-live\":[\"java.time.Duration\",300.000000000],\"settings.token.device-code-time-to-live\":[\"java.time.Duration\",300.000000000]}');

启动认证服务器spring-oauth-sso-server,订单服务器spring-oauth-client-order,

3.1. 测试无需登录的请求

浏览器输入地址http://spring-oauth-client-order:9003/index,测试放开保护的资源,结果如下。

Spring Authorization Server (十一)单点登录-SSO_单点登录-SSO_02

3.2. 测试需要登录的请求

浏览器输入地址http://spring-oauth-client-order:9003/order1,测试受保护的资源,结果如下。

Spring Authorization Server (十一)单点登录-SSO_Spring Security_03

输入用户:user,密码:123456,提交登录,跳转到授权确认页面。

Spring Authorization Server (十一)单点登录-SSO_Spring Boot3_04

勾选授权信息,提交确认授权,则成功跳转到订单页面1,结果如下。

Spring Authorization Server (十一)单点登录-SSO_Spring Security_05

此时点击”跳转到订单页面2“,则亦能成功跳转过去,结果如下。

Spring Authorization Server (十一)单点登录-SSO_Spring Boot3_06

上面的操作都是订单服务与认证服务器之间的交互,从订单页面1跳转到订单页面2,属于同一个服务之间的跳转。从订单服务页面跳转到商品页面的功能尚未实现,下面需要搭建商品服务,实现订单服务到商品服务之间的跳转,这个跳转是跨客户端服务之间的跳转,这就涉及到单点登录了。

4. 商品服务搭建

将订单服务spring-oauth-client-order复制一份进行修改,取名为我们将spring-oauth-client-product,服务的端口设置为9004。

pom.xml文件内容如下。

<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>
  <parent>
    <groupId>org.oauth</groupId>
    <artifactId>spring-oauth-parent</artifactId>
    <version>1.0-SNAPSHOT</version>
  </parent>
  <artifactId>spring-oauth-sso-client-product</artifactId>
  <name>SSO客户端-商品服务</name>

  <dependencies>

    <!--spring-boot-starter-oauth2-client-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-oauth2-client</artifactId>
    </dependency>
    <!--spring-boot-starter-web-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- spring-boot-starter-thymeleaf -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <!-- webjars-locator-core -->
    <dependency>
      <groupId>org.webjars</groupId>
      <artifactId>webjars-locator-core</artifactId>
    </dependency>
    <!-- bootstrap:5.2.3 -->
    <dependency>
      <groupId>org.webjars</groupId>
      <artifactId>bootstrap</artifactId>
      <version>5.2.3</version>
    </dependency>
    <!-- jquery:3.6.4 -->
    <dependency>
      <groupId>org.webjars</groupId>
      <artifactId>jquery</artifactId>
      <version>3.6.4</version>
    </dependency>

  </dependencies>

</project>

application.yml配置如下。

server:
  ip: spring-oauth-client-product
  port: 9004

logging:
  level:
    org.springframework.security: trace

spring:
  application:
    name: spring-oauth-client-product
  security:
    oauth2:
      client:
        provider:
          #认证服务器信息
          oauth-server:
            #授权地址
            issuer-uri: http://spring-oauth-server:9000
            authorizationUri: ${spring.security.oauth2.client.provider.oauth-server.issuer-uri}/oauth2/authorize
            #令牌获取地址
            tokenUri: ${spring.security.oauth2.client.provider.oauth-server.issuer-uri}/oauth2/token
        registration:
          messaging-client-oidc:
            #认证提供者,标识由哪个认证服务器进行认证,和上面的oauth-server进行关联
            provider: oauth-server
            #客户端名称
            client-name: web平台-SSO客户端-商品服务
            #客户端id,从认证平台申请的客户端id
            client-id: web-client-id-product
            #客户端秘钥
            client-secret: secret
            #客户端认证方式
            client-authentication-method: client_secret_basic
            #使用授权码模式获取令牌(token)
            authorization-grant-type: authorization_code
            #回调地址,接收认证服务器回传code的接口地址,之前我们是使用http://www.baidu.com代替
            redirect-uri: http://spring-oauth-client-product:9004/login/oauth2/code/messaging-client-oidc
            scope:
              - profile
              - openid

index.html内容如下。

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Spring Authorization Server sample</title>
    <link rel="stylesheet" href="/webjars/bootstrap/css/bootstrap.css" th:href="@{/webjars/bootstrap/css/bootstrap.css}" />
</head>
<body>
<div class="container" align="center">
   <h1>商品服务</h1>
    <h2 style="color: darkgoldenrod;">这是商品服务首页,访问该页面,无需登录。</h2>
</div>
</body>
</html>

product_1.html内容如下。

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Spring Authorization Server sample</title>
    <link rel="stylesheet" href="/webjars/bootstrap/css/bootstrap.css" th:href="@{/webjars/bootstrap/css/bootstrap.css}" />
</head>
<body>
<div class="container" align="center">
    <h1>商品服务</h1>
    <h2 style="color: blue">商品页面1,认证信息如下。</h2>
    <div>
        <span th:text="${authentication}">认证信息:</span>
    </div>
    <h2><a href="http://spring-oauth-client-product:9004/product2">跳转到商品页面2</a></h2>
    <h2><a href="http://spring-oauth-client-order:9003/order2">跳转到订单页面2</a></h2>
</div>
</body>
</html>

product_2.html内容如下。

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Spring Authorization Server sample</title>
    <link rel="stylesheet" href="/webjars/bootstrap/css/bootstrap.css" th:href="@{/webjars/bootstrap/css/bootstrap.css}" />
</head>
<body>
<div class="container" align="center">
    <h1>商品服务</h1>
    <h2 style="color: red">商品页面2,认证信息如下。</h2>
    <div>
        <span th:text="${authentication}">认证信息:</span>
    </div>
    <h2><a href="http://spring-oauth-client-product:9004/product1">跳转到商品页面1</a></h2>
    <h2><a href="http://spring-oauth-client-order:9003/order1">跳转到订单页面1</a></h2>
</div>
</body>
</html>

ProductController,代码如下。

@Controller
public class ProductController {

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

    @GetMapping("/product1")
    public String product1(Model model) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        System.out.println("authentication:"+ authentication);

        model.addAttribute("authentication",authentication);
        return "product_1";
    }

    @GetMapping("/product2")
    public String product(Model model) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        System.out.println("authentication:"+ authentication);

        model.addAttribute("authentication",authentication);
        return "product_2";
    }

}

WebSecurityConfig配置类,内容如下。

package org.oauth.client.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.client.endpoint.DefaultAuthorizationCodeTokenResponseClient;
import org.springframework.security.web.SecurityFilterChain;

/**
 * @author Rommel
 * @version 1.0
 * @date 2023/8/13-21:51
 * @description TODO
 */
@Configuration
public class WebSecurityConfig {
    @Bean
    public SecurityFilterChain authorizationClientSecurityFilterChain(HttpSecurity http) throws Exception {

        http
                // 请求路径管理
                .authorizeHttpRequests(authorize -> authorize
                        .requestMatchers("/assets/**", "/webjars/**", "/index").permitAll()
                        .anyRequest().authenticated()
                )
                // Session会话管理
                .sessionManagement(sessionManagementConfigurer->{
                    sessionManagementConfigurer.sessionCreationPolicy(SessionCreationPolicy.ALWAYS);
                })
                //OAuth2.0登录配置
                .oauth2Login(oauth2LoginCustomizer->{
                    // 设置用以获取token请求的客户端
                    oauth2LoginCustomizer.tokenEndpoint(tokenEndpointCustomizer->{
                        //采用DefaultAuthorizationCodeTokenResponseClient默认处理
                        tokenEndpointCustomizer.accessTokenResponseClient(new DefaultAuthorizationCodeTokenResponseClient());
                    });
                })
        ;

        return http.build();
    }

}


5. 单点登录测试

oauth2_registered_client表增加一条商品服务的客户端,记录如下。

INSERT INTO `oauth-server`.`oauth2_registered_client` (`id`, `client_id`, `client_id_issued_at`, `client_secret`, `client_secret_expires_at`, `client_name`, `client_authentication_methods`, `authorization_grant_types`, `redirect_uris`, `post_logout_redirect_uris`, `scopes`, `client_settings`, `token_settings`) VALUES ('3eacac0e-0de9-4727-9a64-6bdd4be2ee4', 'web-client-id-product', '2023-07-12 07:33:42', '$2a$10$.J0Rfg7y2Mu8AN8Dk2vL.eBFa9NGbOYCPOAFEw.QhgGLVXjO7eFDC', NULL, 'web平台-SSO客户端-商品服务', 'client_secret_basic', 'refresh_token,authorization_code', 'http://spring-oauth-client-product:9004/login/oauth2/code/messaging-client-oidc', 'http://127.0.0.1:9000/', 'openid,profile', '{\"@class\":\"java.util.Collections$UnmodifiableMap\",\"settings.client.require-proof-key\":false,\"settings.client.require-authorization-consent\":true}', '{\"@class\":\"java.util.Collections$UnmodifiableMap\",\"settings.token.reuse-refresh-tokens\":true,\"settings.token.id-token-signature-algorithm\":[\"org.springframework.security.oauth2.jose.jws.SignatureAlgorithm\",\"RS256\"],\"settings.token.access-token-time-to-live\":[\"java.time.Duration\",1800.000000000],\"settings.token.access-token-format\":{\"@class\":\"org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat\",\"value\":\"self-contained\"},\"settings.token.refresh-token-time-to-live\":[\"java.time.Duration\",3600.000000000],\"settings.token.authorization-code-time-to-live\":[\"java.time.Duration\",300.000000000],\"settings.token.device-code-time-to-live\":[\"java.time.Duration\",300.000000000]}');

启动认证服务器spring-oauth-sso-server,订单服务器spring-oauth-client-order,商品服务spring-oauth-client-product。

浏览器输入地址http://spring-oauth-client-product:9004/index,测试商品服务放开保护的资源,结果如下。

Spring Authorization Server (十一)单点登录-SSO_OAuth2.1_07

浏览器输入地址http://spring-oauth-client-product:9004/product1,测试受保护的资源,结果如下。

Spring Authorization Server (十一)单点登录-SSO_Spring Security_08

输入用户:user,密码:123456,提交登录,跳转到授权确认页面。

Spring Authorization Server (十一)单点登录-SSO_Authorization Server_09

勾选授权信息,提交确认授权,则成功跳转到商品页面1,结果如下。

Spring Authorization Server (十一)单点登录-SSO_Authorization Server_10

点击”跳转到商品页面2“,结果如下。

Spring Authorization Server (十一)单点登录-SSO_OAuth2.1_11

点击”跳转到订单页面1“,结果如下。

Spring Authorization Server (十一)单点登录-SSO_Authorization Server_12

注意:此时我们从商品服务跳转到订单服务,属于跨客户端服务,由于我们在操作商品服务时已经输入用户名和密码进行了登录,session会话已存在,因此在此处就绕过了用户名、密码登录了。

勾选授权信息,提交确认授权,则成功跳转到订单页面1,结果如下。

Spring Authorization Server (十一)单点登录-SSO_Spring Boot3_13

点击”跳转到订单页面2“,结果如下。

Spring Authorization Server (十一)单点登录-SSO_单点登录-SSO_14

点击”跳转到商品页面1“,结果如下。

Spring Authorization Server (十一)单点登录-SSO_单点登录-SSO_15

点击”跳转到商品页面2“,结果如下。

Spring Authorization Server (十一)单点登录-SSO_Spring Boot3_16


说明:在首次访问订单服务或商品服务受保护的资源时,在登录过程中,都会跳转到授权确认页面进行授权确认,如果想去掉授权确认页面的这一步操作,实现无感确认,可以将oauth2_registered_client表中对应的客户端记录,client_settings字段的settings.client.require-authorization-consent值设置为false。将settings.client.require-authorization-consent值设置为false后,上面的测试,首次访问商品服务进行登录时,输入用户名、密码提交后,将不会跳转到授权确认页面,直接默认授权,从商品服务的页面首次跳转到订单服务的页面时,也不会先跳转到授权确认页面,而是直接跳转到订单服务的页面。

6. 总结

本篇先是用时序图介绍了单点登录的交互流程。接着介绍了单点登录中认证服务器、客户端商品服务的搭建,同时对客户端商品服务进行了测试。然后以客户端订单服务为样板搭建了一份客户端商品服务。最后对单点登录的全流程进行了闭环测试。


文章代码链接:链接地址