使用受 Spring 安全性保护的资源创建简单 Web 应用程序

时间:2022-12-22 11:27:27

使用受 Spring 安全性保护的资源创建简单 Web 应用程序

本指南将引导您完成使用受 Spring 安全性保护的资源创建简单 Web 应用程序的过程。

您将构建什么

您将构建一个Spring MVC应用程序,该应用程序使用由固定用户列表支持的登录表单来保护页面。

你需要什么

  • 约15分钟
  • 最喜欢的文本编辑器或 IDE
  • JDK 17或以后
  • 格拉德尔 4+​或梅文 3.2+
  • 您也可以将代码直接导入到 IDE 中:
  • 弹簧工具套件 (STS)
  • 智能理念
  • VSCode

如何完成本指南

像大多数春天一样入门指南,您可以从头开始并完成每个步骤,也可以绕过您已经熟悉的基本设置步骤。无论哪种方式,您最终都会得到工作代码。

要从头开始,请继续从 Spring 初始化开始.

要跳过基础知识,请执行以下操作:

  • 下载​并解压缩本指南的源存储库,或使用吉特:git clone https://github.com/spring-guides/gs-securing-web.git
  • 光盘成gs-securing-web/initial
  • 跳转到创建不安全的 Web 应用程序.

完成后,您可以根据 中的代码检查结果。​​gs-securing-web/complete​

从 Spring 初始化开始

你可以使用这个预初始化项目,然后单击生成以下载 ZIP 文件。此项目配置为适合本教程中的示例。

手动初始化项目:

  1. 导航到https://start.spring.io.此服务拉入应用程序所需的所有依赖项,并为您完成大部分设置。
  2. 选择 Gradle 或 Maven 以及您要使用的语言。本指南假定您选择了 Java。
  3. 单击“依赖项”,然后选择“Spring Web”和“Thymeleaf”。
  4. 单击生成
  5. 下载生成的 ZIP 文件,该文件是配置了您选择的 Web 应用程序的存档。

如果您的 IDE 集成了 Spring Initializr,则可以从 IDE 完成此过程。

您也可以从 Github 分叉项目,然后在 IDE 或其他编辑器中打开它。

创建不安全的 Web 应用程序

在将安全性应用于 Web 应用程序之前,您需要一个要保护的 Web 应用程序。本节将引导您创建一个简单的 Web 应用程序。然后,您将在下一节中使用Spring Security来保护它。

Web 应用程序包括两个简单视图:主页和“你好,世界”页面。主页在以下百里香叶模板中定义(来自):​​src/main/resources/templates/home.html​

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
<head>
<title>Spring Security Example</title>
</head>
<body>
<h1>Welcome!</h1>

<p>Click <a th:href="@{/hello}">here</a> to see a greeting.</p>
</body>
</html>

此简单视图包含指向页面的链接,该链接在以下 Thymeleaf 模板(来自):​​/hello​​​​src/main/resources/templates/hello.html​

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
<head>
<title>Hello World!</title>
</head>
<body>
<h1>Hello world!</h1>
</body>
</html>

Web应用程序基于Spring MVC。因此,您需要配置 Spring MVC 并设置视图控制器以公开这些模板。下面的清单(来自)显示了在应用程序中配置 Spring MVC 的类:​​src/main/java/com/example/securingweb/MvcConfig.java​

package com.example.securingweb;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MvcConfig implements WebMvcConfigurer {

public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/home").setViewName("home");
registry.addViewController("/").setViewName("home");
registry.addViewController("/hello").setViewName("hello");
registry.addViewController("/login").setViewName("login");
}

}

该方法(重写 中同名的方法)添加了四个视图控制器。其中两个视图控制器引用名称为 (在 中定义) 的视图,另一个引用名为 (在 中定义) 的视图。第四个视图控制器引用另一个名为 的视图。您将在下一节中创建该视图。​​addViewControllers()​​​​WebMvcConfigurer​​​​home​​​​home.html​​​​hello​​​​hello.html​​​​login​

此时,您可以跳到”运行应用程序“并运行应用程序,而无需登录任何内容。

现在您有一个不安全的 Web 应用程序,您可以为其添加安全性。

设置 Spring 安全性

假设您要阻止未经授权的用户查看 上的问候语页面。就像现在一样,如果访问者单击主页上的链接,他们会看到没有障碍的问候语来阻止他们。您需要添加一个障碍,强制访问者登录,然后才能看到该页面。​​/hello​

您可以通过在应用程序中配置 Spring 安全性来做到这一点。如果 Spring 安全性在类路径上,Spring 引导​​自动保护所有 HTTP 端点​​使用“基本”身份验证。但是,您可以进一步自定义安全设置。您需要做的第一件事是将 Spring 安全性添加到类路径中。

使用 Gradle,您需要在 的闭包中添加三行(一行用于应用程序,一行用于 Thymeleaf & Spring Security 集成,一行用于测试),如以下列表所示:​​dependencies​​​​build.gradle​

implementation 'org.springframework.boot:spring-boot-starter-security'
// Temporary explicit version to fix Thymeleaf bug
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6:3.1.1.RELEASE'
implementation 'org.springframework.security:spring-security-test'

以下清单显示了完成的文件:​​build.gradle​

plugins {
id 'java'
id 'org.springframework.boot' version '3.0.0'
id 'io.spring.dependency-management' version '1.1.0'
}

group = 'com.example'
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-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-security'
// Temporary explicit version to fix Thymeleaf bug
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6:3.1.1.RELEASE'
implementation 'org.springframework.security:spring-security-test'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

test {
useJUnitPlatform()
}

使用 Maven,您需要向 中的元素添加两个额外的条目(一个用于应用程序,一个用于测试),如以下清单所示:​​<dependencies>​​​​pom.xml​

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity6</artifactId>
<!-- Temporary explicit version to fix Thymeleaf bug -->
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>

以下清单显示了完成的文件:​​pom.xml​

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>securing-web-complete</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>securing-web-complete</name>
<description>Demo project for Spring Boot</description>

<properties>
<java.version>17</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<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.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity6</artifactId>
<!-- Temporary explicit version to fix Thymeleaf bug -->
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

</project>

以下安全配置(来自)可确保只有经过身份验证的用户才能看到秘密问候语:​​src/main/java/com/example/securingweb/WebSecurityConfig.java​

package com.example.securingweb;

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.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((requests) -> requests
.requestMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
)
.formLogin((form) -> form
.loginPage("/login")
.permitAll()
)
.logout((logout) -> logout.permitAll());

return http.build();
}

@Bean
public UserDetailsService userDetailsService() {
UserDetails user =
User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();

return new InMemoryUserDetailsManager(user);
}
}

该类被注释为启用Spring Security的Web安全支持并提供Spring MVC集成。它还公开了两个 bean 来设置 Web 安全配置的一些细节:​​WebSecurityConfig​​​​@EnableWebSecurity​

Bean 定义了哪些 URL 路径应该受到保护,哪些不应该受到保护。具体而言,和 路径配置为不需要任何身份验证。所有其他路径都必须经过身份验证。​​SecurityFilterChain​​​​/​​​​/home​

用户成功登录后,将重定向到之前请求的需要身份验证的页面。有一个自定义页面(由 指定),每个人都可以查看它。​​/login​​​​loginPage()​

Bean 使用单个用户设置内存中用户存储。该用户的用户名为 、 的密码和 的角色。​​UserDetailsService​​​​user​​​​password​​​​USER​

现在您需要创建登录页面。视图已经有一个视图控制器,因此您只需创建登录视图本身,如以下清单(来自)所示:​​login​​​​src/main/resources/templates/login.html​

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
<head>
<title>Spring Security Example </title>
</head>
<body>
<div th:if="${param.error}">
Invalid username and password.
</div>
<div th:if="${param.logout}">
You have been logged out.
</div>
<form th:action="@{/login}" method="post">
<div><label> User Name : <input type="text" name="username"/> </label></div>
<div><label> Password: <input type="password" name="password"/> </label></div>
<div><input type="submit" value="Sign In"/></div>
</form>
</body>
</html>

此百里香叶模板提供了一个表单,用于捕获用户名和密码并将其发布到 .按照配置,Spring 安全性提供了一个过滤器,用于拦截该请求并对用户进行身份验证。如果用户身份验证失败,页面将重定向到 ,并且您的页面将显示相应的错误消息。成功注销后,您的应用程序将发送到 ,并且您的页面将显示相应的成功消息。​​/login​​​​/login?error​​​​/login?logout​

最后,您需要为访问者提供一种显示当前用户名和注销的方法。为此,请更新 以向当前用户问好并包含一个表单,如以下列表 (from ) 所示:​​hello.html​​​​Sign Out​​​​src/main/resources/templates/hello.html​

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"
xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity6">
<head>
<title>Hello World!</title>
</head>
<body>
<h1 th:inline="text">Hello <span th:remove="tag" sec:authentication="name">thymeleaf</span>!</h1>
<form th:action="@{/logout}" method="post">
<input type="submit" value="Sign Out"/>
</form>
</body>
</html>

我们使用Thymeleaf与Spring Security的集成来显示用户名。“注销”表单将开机自检提交到 。成功注销后,它将用户重定向到 。​​/logout​​​​/login?logout​

Thymeleaf 3.1 不再提供访问,因此不能用于访问当前经过身份验证的用户。​​HttpServletRequest​​​​HttpServletRequest#getRemoteUser()​

运行应用程序

Spring 初始化器会为您创建一个应用程序类。在这种情况下,您无需修改类。以下清单(来自 )显示了应用程序类:​​src/main/java/com/example/securingweb/SecuringWebApplication.java​

package com.example.securingweb;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SecuringWebApplication {

public static void main(String[] args) throws Throwable {
SpringApplication.run(SecuringWebApplication.class, args);
}

}

构建可执行的 JAR

您可以使用 Gradle 或 Maven 从命令行运行应用程序。您还可以构建一个包含所有必需依赖项、类和资源的可执行 JAR 文件并运行该文件。通过构建可执行 jar,可以轻松地在整个开发生命周期中跨不同环境等将服务作为应用程序进行交付、版本控制和部署。

如果使用 Gradle,则可以使用 .或者,您可以使用 JAR 文件生成 JAR 文件,然后运行该文件,如下所示:​​./gradlew bootRun​​​​./gradlew build​

java -jar build/libs/gs-securing-web-0.1.0.jar

如果使用 Maven,则可以使用 运行应用程序。或者,您可以使用 JAR 文件生成 JAR 文件,然后运行该文件,如下所示:​​./mvnw spring-boot:run​​​​./mvnw clean package​

java -jar target/gs-securing-web-0.1.0.jar

此处描述的步骤将创建一个可运行的 JAR。你也可以构建经典 WAR 文件.

应用程序启动后,将浏览器指向 。您应该会看到主页,如下图所示:​​http://localhost:8080​

当您单击该链接时,它会尝试将您带到 的问候页面。但是,由于该页面是安全的,并且您尚未登录,因此它会将您带到登录页面,如下图所示:​​/hello​

使用受 Spring 安全性保护的资源创建简单 Web 应用程序

如果您使用不安全的版本跳到此处,则看不到登录页面。您应该备份并编写其余的基于安全性的代码。

在登录页面上,分别输入用户名和密码字段,以测试用户身份登录。提交登录表单后,您将进行身份验证,然后转到问候页面,如下图所示:​​user​​​​password​

如果单击“注销”按钮,您的身份验证将被撤销,并且您将返回到登录页面,并显示一条消息,指示您已注销

总结

祝贺!您已经开发了一个简单的 Web 应用程序,该应用程序使用 Spring 安全性进行保护。