<!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
-
cd 進入
gs-securing-web/initial
-
跳到 建立未受保護的 Web 應用程式。
當您完成時,您可以對照 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 分支專案,並在您的 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 (登出)」表單將 POST 提交到 /logout
。成功登出後,它會將使用者重新導向到 /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://127.0.0.1:8080
。您應該會看到首頁,如下圖所示

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

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

如果您按一下 Sign Out (登出) 按鈕,您的驗證將被撤銷,並且您將返回到登入頁面,並顯示您已登出的訊息。
摘要
恭喜!您已開發出一個使用 Spring Security 保護安全的簡單 Web 應用程式。