<!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>
保護 Web 應用程式
本指南將引導您完成建立一個簡單 Web 應用程式的過程,該應用程式的資源受 Spring Security 保護。
您將構建什麼
您將構建一個 Spring MVC 應用程式,該應用程式透過一個由固定使用者列表支援的登入表單來保護頁面。
您需要準備什麼
-
大約 15 分鐘
-
趁手的文字編輯器或 IDE
-
Java 17 或更高版本
-
您也可以直接將程式碼匯入到您的 IDE 中
如何完成本指南
與大多數 Spring 入門指南一樣,您可以從頭開始完成每個步驟,也可以跳過已經熟悉的基本設定步驟。無論哪種方式,您最終都會得到可以執行的程式碼。
要從頭開始,請轉到使用 Spring Initializr 開始。
要跳過基礎部分,請執行以下操作
-
下載並解壓本指南的原始碼倉庫,或使用 Git 克隆:
git clone https://github.com/spring-guides/gs-securing-web.git
-
進入目錄
gs-securing-web/initial
完成時,您可以對照 gs-securing-web/complete
中的程式碼檢查結果。
使用 Spring Initializr 開始
您可以使用這個預初始化專案並點選 Generate 下載一個 ZIP 檔案。該專案已配置好,適用於本教程中的示例。
手動初始化專案
-
導航到https://start.spring.io。此服務將引入您應用程式所需的所有依賴項,併為您完成大部分設定。
-
選擇 Gradle 或 Maven 以及您想使用的語言。本指南假設您選擇了 Java。
-
點選 Dependencies,然後選擇 Spring Web 和 Thymeleaf。
-
點選 Generate。
-
下載生成的 ZIP 檔案,它是一個根據您的選擇配置好的 Web 應用程式歸檔檔案。
如果您的 IDE 集成了 Spring Initializr,您可以直接在 IDE 中完成此過程。 |
您也可以從 Github Fork 專案並在您的 IDE 或其他編輯器中開啟。 |
建立未受保護的 Web 應用程式
在將安全性應用於 Web 應用程式之前,您需要一個需要保護的 Web 應用程式。本節將引導您建立一個簡單的 Web 應用程式。然後,您將在下一節中使用 Spring Security 對其進行保護。
該 Web 應用程式包含兩個簡單的檢視:主頁和“Hello, World”頁面。主頁在以下 Thymeleaf 模板中定義(來自 src/main/resources/templates/home.html
)
這個簡單的檢視包含一個指向 /hello
頁面的連結,該頁面在以下 Thymeleaf 模板中定義(來自 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 並設定檢視控制器來暴露這些模板。以下列表(來自 src/main/java/com/example/securingweb/MvcConfig.java
)展示了一個在應用程式中配置 Spring MVC 的類
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 Security
假設您想阻止未經授權的使用者檢視 /hello
的問候頁面。現在,如果訪問者點選主頁上的連結,他們會看到問候語,沒有任何阻止他們的障礙。您需要新增一個障礙,強制訪問者在看到該頁面之前先登入。
您可以透過在應用程式中配置 Spring Security 來實現這一點。如果 Spring Security 在類路徑中,Spring Boot 會自動使用“基本”身份驗證保護所有 HTTP 端點。但是,您可以進一步自定義安全設定。您需要做的第一件事是將 Spring Security 新增到類路徑中。
對於 Gradle,您需要在 build.gradle
的 dependencies
閉包中新增三行(一行用於應用程式,一行用於 Thymeleaf & Spring Security 整合,一行用於測試),如下所示
implementation 'org.springframework.boot:spring-boot-starter-security'
// Temporary explicit version to fix Thymeleaf bug
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6:3.1.2.RELEASE'
testImplementation 'org.springframework.security:spring-security-test'
以下列表顯示了完整的 build.gradle
檔案
plugins {
id 'java'
id 'org.springframework.boot' version '3.3.0'
}
apply plugin: 'io.spring.dependency-management'
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.2.RELEASE'
testImplementation 'org.springframework.security:spring-security-test'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
test {
useJUnitPlatform()
}
對於 Maven,您需要在 pom.xml
的 <dependencies>
元素中新增兩個額外條目(一個用於應用程式,一個用於測試),如下所示
<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.3.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);
}
}
WebSecurityConfig
類使用 @EnableWebSecurity
註解,以啟用 Spring Security 的 Web 安全支援並提供 Spring MVC 整合。它還暴露了兩個 Bean,用於設定 Web 安全配置的一些具體資訊
SecurityFilterChain
Bean 定義了哪些 URL 路徑應該受到保護,哪些不應該。具體來說,/
和 /home
路徑被配置為不需要任何身份驗證。所有其他路徑都必須經過身份驗證。
使用者成功登入後,將被重定向到之前請求的需要身份驗證的頁面。有一個自定義的 /login
頁面(由 loginPage()
指定),並且允許所有人檢視該頁面。
UserDetailsService
Bean 設定了一個記憶體中的使用者儲存,其中包含一個使用者。該使用者的使用者名稱為 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>
這個 Thymeleaf 模板提供了一個表單,用於捕獲使用者名稱和密碼並將其釋出到 /login
。按照配置,Spring Security 提供一個過濾器來攔截該請求並驗證使用者。如果使用者身份驗證失敗,頁面將重定向到 /login?error
,您的頁面將顯示相應的錯誤訊息。成功退出後,您的應用程式將被髮送到 /login?logout
,您的頁面將顯示相應的成功訊息。
最後,您需要為訪問者提供一種方式來顯示當前使用者名稱和退出。為此,更新 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 的整合來顯示使用者名稱。“Sign Out”表單向 /logout
提交 POST 請求。成功退出後,它會將使用者重定向到 /login?logout
。
Thymeleaf 3.1 不再提供對 HttpServletRequest 的訪問,因此不能使用 HttpServletRequest#getRemoteUser() 來訪問當前透過身份驗證的使用者。 |
執行應用程式
Spring Initializr 為您建立了一個應用程式類。在這種情況下,您不需要修改該類。以下列表(來自 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,可以使用 ./gradlew bootRun
執行應用程式。或者,您可以使用 ./gradlew build
構建 JAR 檔案,然後按如下方式執行 JAR 檔案
如果您使用 Maven,可以使用 ./mvnw spring-boot:run
執行應用程式。或者,您可以使用 ./mvnw clean package
構建 JAR 檔案,然後按如下方式執行 JAR 檔案
此處描述的步驟建立了一個可執行的 JAR 檔案。您還可以構建一個經典的 WAR 檔案。 |
應用程式啟動後,將瀏覽器指向 https://:8080
。您應該會看到主頁,如下圖所示

當您點選連結時,它會嘗試將您帶到 /hello
的問候頁面。然而,由於該頁面是受保護的且您尚未登入,它會將您帶到登入頁面,如下圖所示

如果您跳到這裡使用了未受保護的版本,您將看不到登入頁面。您應該返回並編寫其餘的基於安全性的程式碼。 |
在登入頁面,作為測試使用者登入,分別在使用者名稱和密碼欄位輸入 user
和 password
。提交登入表單後,您將透過身份驗證,然後被帶到問候頁面,如下圖所示

如果您點選 Sign Out 按鈕,您的身份驗證將被撤銷,並且您將返回到登入頁面,並顯示一條訊息,表明您已退出登入。
總結
恭喜!您已經開發了一個使用 Spring Security 保護的簡單 Web 應用程式。