精通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 以供后续调用。
现在是时候运行应用程序,看看它是否能正常工作了。以下是使其正常工作的主要步骤:
- 启动用户应用程序,它将在 8080 端口上运行。
- 启动我的复古应用程序,它将在 8081 端口运行。
- 使用 index.html 文件启动 myretro-ui。您可以选择使用 IDE(如 VS Code 或 WebStorm)或使用 Python。
- 如果你使用 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 基础设施进行部署,将密钥和密码作为环境变量或秘密保存是一种最佳实践。
获取这些密钥的步骤如下:
- 请访问 https://github.com/settings/developers 并登录。如果您还没有账户,请点击链接创建一个免费的账户,完成注册后再登录。请确保您已成功登录。
- 点击右侧的“新建 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 框架完成了大部分繁重的工作。