精通Spring Boot 3 : 9. Spring Boot 安全 (4)

整合所有内容:用户界面、用户应用和复古应用

为了将所有内容整合在一起,我们将使用 My Retro App UI(前端应用),您可以在 09-security/myretro-ui 文件夹中找到它。请在运行之前仔细审查和分析。

首先,让我们回顾一下包含安全配置的重要文件。在 myretro-ui 文件夹中打开或创建 login.js 文件。请参见列表 9-15。

$(function() {
  $('#btn__login').click(function(e) {
    e.preventDefault();
    let user_email = $('input[name="user_email"]').val();
    let user_password = $('input[name="user_password"]').val();
    $.ajax({
      url: 'http://localhost:8081/login',
      method: 'POST',
          data: {
                username: user_email,
                password: user_password
          },
        xhrFields: {
            withCredentials: true
        },
      headers:{
        'Access-Control-Allow-Origin': '*'
      }
    })
        .done(function( response, textStatus, xhr ) {
          console.log(response)
          if(xhr.status === 200 &&
              response.active === true &&
              response.email === user_email &&
              response.password === user_password ) {
            let isAdmin = response.userRole.includes("ADMIN");
            localStorage.setItem('ls_name', response.name);
            localStorage.setItem('ls_email', response.email);
            localStorage.setItem('ls_gravatar', response.gravatarUrl);
            localStorage.setItem('ls_role', response.userRole);
            document.cookie = xhr.getResponseHeader('x-myretro');
            console.log(xhr.getAllResponseHeaders());
            if (isAdmin){
              window.location.href = "admin-dashboard.html";
            }else {
              window.location.href = "admin-users.html";
            }
            return;
          }
        });
  });
});

9-15 myretro-ui/assets/js/login.js

在 login.js 中的$.ajax 调用中,我们使用 POST HTTP 方法并指向/login 端点。虽然我们没有在 My Retro App 中明确指定这个端点,但 Spring Security 默认会定义它;/logout 端点也是如此。此外,请注意,我们使用 xhrFields.withCredentials,这样可以在跨站点访问控制请求中发送凭据(如 cookies、headers 和 TLS 客户端证书)(请记住,这是一个外部应用程序的 UI,我们需要这样做)。成功登录后,它将保存会话 ID 以供后续调用。

现在是时候运行应用程序,看看它是否能正常工作了。以下是使其正常工作的主要步骤:


  1. 启动用户应用程序,它将在 8080 端口上运行。
  2. 启动我的复古应用程序,它将在 8081 端口运行。
  3. 使用 index.html 文件启动 myretro-ui。您可以选择使用 IDE(如 VS Code 或 WebStorm)或使用 Python。
  4. 如果你使用 Python,请在 myretro-ui 文件夹的根目录下执行以下命令:
python3 -m http.server

这将打开 8000 端口。现在你可以在浏览器中访问它。你应该能看到图 9-7 中显示的登录页面。

在邮箱字段中输入 manager@email.com,在密码字段中输入 aw2s0meR!,然后点击登录按钮。

你应该能看到我的复古应用程序的仪表板,如图 9-8 所示,在这里你可以管理复古会议或用户。


使用 OAuth2 添加社交登录功能

目前,许多应用程序要求用户通过第三方机构进行身份验证和授权,这些机构通常是社交媒体平台,如 Facebook、Google、X(前身为 Twitter)、GitHub 等。在本节中,我们将配置用户应用程序以通过 GitHub 进行身份验证。我将逐步引导您完成配置,您会发现使用 Spring Boot 使这个过程变得非常简单,它将帮助您将所有内容连接起来,以便您可以使用任何社交媒体平台。

使用社交媒体平台,您为应用程序提供了单点登录(SSO)功能,从而简化了最终用户的注册和登录流程。在进行社交媒体登录时,您将获得一些实用的功能:

  • 电子邮件验证
  • 用户资料
  • 一键式体验

如果你仔细想想,社交媒体是与世界沟通的最受欢迎方式,这种能力将为你的应用带来更多注册和流量。那么,让我们开始使用用户应用吧。

用户应用的社交登录功能

你可以访问代码,所以请将 09-security-social/users 中的 Users App 导入到你喜欢的 IDE 中。接着,打开 build.gradle 文件。请查看清单 9-16。

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.2.3'
    id 'io.spring.dependency-management' version '1.1.4'
}
group = 'com.apress'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
repositories {
    mavenCentral()
}
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-validation'
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
    runtimeOnly 'com.h2database:h2'
    runtimeOnly 'org.postgresql:postgresql'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    // Web
    implementation 'org.webjars:bootstrap:5.2.3'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'org.springframework.security:spring-security-test'
}
tasks.named('test') {
    useJUnitPlatform()
}
test {
    testLogging {
        events "passed", "skipped", "failed" //, "standardOut", "standardError"
        showExceptions true
        exceptionFormat "full"
        showCauses true
        showStackTraces true
        // Change to `true` for more verbose test output
        showStandardStreams = false
    }
}

9-16 build.gradle

列表 9-16 显示,我们正在将熟悉的 spring-boot-starter-security 和新的依赖 spring-boot-starter-oauth2-client 添加到依赖项中。一旦 Spring Boot 通过自动配置识别到这个新依赖,它将为您的应用程序配置使用 OAuth 2 协议和客户端进行社交登录所需的一切。

接下来,打开或创建 UserSecurityConfig 类。请参阅清单 9-17。

package com.apress.users.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@Configuration
public class UserSecurityConfig {
    @Bean
    SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                .csrf( c -> c.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()))
                .authorizeHttpRequests( auth ->
                        auth
                                .requestMatchers(new AntPathRequestMatcher("/index.html"),
                                        new AntPathRequestMatcher("/webjars/**"),
                                        new AntPathRequestMatcher("/error")).permitAll()
                                .anyRequest().authenticated()
                        )
                .logout( l -> l.logoutSuccessUrl("/index.html").permitAll() )
                .oauth2Login(Customizer.withDefaults());
        return http.build();
    }
}

9-17 src/main/java/apress/com/users/security/UserSecurityConfig.java

让我们回顾一下 UserSecurityConfig 类。首先,我们通过提供 SecurityFilterChain bean 并配置 HttpSecurity 类来定义安全过滤器。我们正在配置 CSRF 过滤器,并将令牌存储库设置为 HttpOnlyFalse,这在 Spring Security OAuth 2 客户端中是必要的,因为它允许 JavaScript 读取 CSRF 令牌,并且由于我们将使用 GitHub,其页面需要此功能。此外,我们允许访问 index.html 页面、/error 端点以及位于/webjars/**路径中的资源;其他所有内容都需要身份验证。我们声明了/logout 并重定向到主页(/index.html),没有安全限制。最后,aouth2Login(Customizer。使用 withDefaults() 配置将通过社交登录机制访问 GitHub 页面,告知您即将使用用户应用程序,并需要授予相应的权限。在您接受访问后,便可以使用用户应用程序的端点。在后台,Spring Boot 已经配置好了启动与 GitHub 的 OAuth 2 协议所需的一切。请参见图 9-9。

接下来,打开或创建 application.yaml 文件。请参阅列表 9-18。

spring:
  h2:
    console:
      enabled: true
  datasource:
    generate-unique-name: false
    name: test-db
  jpa:
    show-sql: true
  security:
       oauth2:
         client:
           registration:
             github:
              client-id: ${GITHUB_CLIENT_ID}
              client-secret: ${GITHUB_CLIENT_SECRET}
              scope:
                - user:email
                - https://github.com/login/oauth/authorize
logging:
  level:
    org:
      springframework:
        security: DEBUG

9-18 src/main/resources/application.yaml

列表 9-18 展示了 application.yaml 的属性。关键部分是 security.oauth2.client.*属性。我们使用 registration.github.*,并需要提供 client-id 和 client-secret 密钥。在这种情况下,Spring Boot 会读取该文件,并通过环境变量 GITHUB_CLIENT_ID 和 GITHUB_CLIENT_SECRET 查找这些密钥。如果您使用 Kubernetes 基础设施进行部署,将密钥和密码作为环境变量或秘密保存是一种最佳实践。

获取这些密钥的步骤如下:

  1. 请访问 https://github.com/settings/developers 并登录。如果您还没有账户,请点击链接创建一个免费的账户,完成注册后再登录。请确保您已成功登录。
  2. 点击右侧的“新建 OAuth 应用”按钮,然后在注册表单中填写以下信息(参见图 9-10):
  • 应用程序名称:myretro-users
  • 主页链接:http://localhost:8080
  • 授权回调网址: http://localhost:8080/login/oauth2/code/github

点击“注册应用”按钮,您将看到如图 9-11 所示的页面。

在客户端密钥部分,点击“生成新的客户端密钥”按钮。复制窗格中显示的密钥。请参见图 9-12。


所以,复制两个密钥:客户端 ID 和您的新客户端密钥(这些密钥会在我们之前提到的环境变量中)。然后,点击“更新应用程序”按钮,就完成了。用户应用程序将更新,以将 GitHub 添加为社交登录提供者。

通过社交登录运行用户应用程序

现在是时候运行用户应用程序,查看新登录功能的结果了。在运行用户应用程序之前,请确保您已设置 GITHUB_CLIENT_ID 和 GITHUB_CLIENT_SECRET 环境变量,这些变量的值来自 GitHub 页面上的客户端 ID 和客户端密钥。您可以使用您的 IDE 来设置这些变量(请参考您 IDE 的文档以获取有关如何设置环境变量的帮助)。

如果你在命令行中工作,可以使用类似于以下的命令:

GITHUB_CLIENT_ID=xxxx GITHUB_CLIENT_SECRET=xxxx ./gradlew clean bootRUN

例如,我正在使用 IntelliJ,因此可以在运行/调试配置对话框中设置环境变量,如图 9-13 所示。

启动用户应用程序后,请打开浏览器并访问:http://localhost:8080;您将看到如图 9-14 所示的主页。


点击“获取所有用户”链接后,您将被重定向到一个与图 9-15 类似的 GitHub 页面。

您需要先登录,然后授权应用程序 myretro-users 以只读方式使用您的电子邮件。接受授权后,您将看到包含所有用户的 JSON 响应。

就这样!现在你为用户应用程序设置了社交登录。如果你仔细看看,这非常简单。请记住,在后台,Spring Boot 利用 Spring Security 框架完成了大部分繁重的工作。