
- Spring Security 教程
- Spring Security - 首頁
- Spring Security - 簡介
- Spring Security - 架構
- Spring Security - 專案模組
- Spring Security - 環境設定
- Spring Security - 表單登入
- Spring Security - 自定義表單登入
- Spring Security - 登出
- Spring Security - 記住我
- Spring Security - 重定向
- Spring Security - 標籤庫
- Spring Security - XML 配置
- Spring Security - 認證提供者
- Spring Security - 基本認證
- Spring Security - AuthenticationFailureHandler
- Spring Security - JWT
- Spring Security - 獲取使用者資訊
- Spring Security - Maven
- Spring Security - 預設密碼編碼器
- Spring Security – 密碼編碼
- Spring Security - 方法級
- Spring Security 有用資源
- Spring Security - 快速指南
- Spring Security - 有用資源
- Spring Security - 討論
Spring Security - 方法級訪問控制
Spring Security 提供請求級別和方法級別的訪問控制。我們可以在任何配置中使用 @EnableMethodSecurity 註解啟用方法級安全性,如下所示
@Controller @EnableMethodSecurity // Enable method level security public class AuthController { ... }
預設情況下,方法級安全性是關閉的。Spring Security 為方法級安全性提供以下選項。
@PreAuthorize − 如果提供的條件為假,則阻止方法呼叫。我們可以傳遞一個表示式來控制訪問,如下所示
// Accessible to user with Admin role @PreAuthorize("hasRole('ROLE_ADMIN')") public String update() { return "Details Updated."; }
@PostAuthorize − 如果提供的條件為真,則方法可以返回值。我們可以傳遞一個表示式來控制訪問,如下所示
class Account { string owner; ... } ... @PostAuthorize("returnObject.owner == authentication.name") public Account readAccount(Long id) { ... return account; }
如果使用者是賬戶所有者,則將返回賬戶詳細資訊,否則將丟擲 AccessDeniedException 異常,並返回 403 狀態碼。
@PreFilter − 如果提供的條件為真,則方法會過濾任何值。我們可以傳遞一個表示式來過濾返回的值,如下所示
class Account { string owner; ... } ... @PreFilter("filterObject.owner == authentication.name") public Colletion<Account> updateAccounts(Account... accounts) { ... return updatedAccounts; }
以上方法將僅根據提供的條件過濾屬於登入使用者的那些賬戶,並僅更新它們。
@PostFilter − 如果提供的條件為真,則方法會過濾任何值。我們可以傳遞一個表示式來過濾返回的值,如下所示
class Account { string owner; ... } ... @PostFilter("filterObject.owner == authentication.name") public Colletion<Account> readAccounts(Long... ids) { ... return accounts; }
以上方法將僅根據提供的條件過濾屬於登入使用者的那些賬戶。
示例
讓我們開始使用 Spring Security 進行實際程式設計。在開始使用 Spring 框架編寫示例之前,您必須確保已正確設定 Spring 環境,如 Spring Security - 環境設定 章節中所述。我們還假設您對 Spring Tool Suite IDE 有基本的瞭解。
現在讓我們繼續編寫一個基於 Spring MVC 的應用程式,該應用程式由 Maven 管理,它將要求使用者登入、對使用者進行身份驗證,然後使用 Spring Security 表單登入功能提供登出選項。
使用 Spring Initializr 建立專案
Spring Initializr 是開始 Spring Boot 專案的好方法。它提供了一個易於使用的使用者介面來建立專案、新增依賴項、選擇 Java 執行時等。它生成一個骨架專案結構,下載後可以匯入到 Spring Tool Suite 中,然後我們可以繼續使用我們的現成專案結構。
我們選擇一個 Maven 專案,將專案命名為 formlogin,Java 版本為 21。新增以下依賴項
Spring Web
Spring Security
Spring Boot DevTools

Thymeleaf 是一個用於 Java 的模板引擎。它允許我們快速開發靜態或動態網頁以在瀏覽器中呈現。它具有極強的可擴充套件性,允許我們詳細定義和自定義模板的處理過程。此外,我們可以點選此 連結 瞭解更多關於 Thymeleaf 的資訊。
讓我們繼續生成專案並下載它。然後,我們將其解壓縮到我們選擇的資料夾中,並使用任何 IDE 開啟它。我將使用 Spring Tools Suite 4。它可以從 https://springframework.tw/tools 網站免費下載,並且針對 Spring 應用程式進行了最佳化。
包含所有相關依賴項的 pom.xml
讓我們看看我們的 pom.xml 檔案。它應該看起來類似於以下內容 -
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.1</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.tutorialspoint.security</groupId> <artifactId>formlogin</artifactId> <version>0.0.1-SNAPSHOT</version> <name>formlogin</name> <description>Demo project for Spring Boot</description> <url/> <licenses> <license/> </licenses> <developers> <developer/> </developers> <scm> <connection/> <developerConnection/> <tag/> <url/> </scm> <properties> <java.version>21</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <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.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity6</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
Spring Security 配置類
在我們的 config 包中,我們建立了 WebSecurityConfig 類。我們將使用此類進行我們的安全配置,因此讓我們使用 @Configuration 註解和 @EnableWebSecurity 註解對其進行註釋。因此,Spring Security 知道將此類視為配置類。如我們所見,Spring 使配置應用程式變得非常容易。
WebSecurityConfig
package com.tutorialspoint.security.formlogin.config; 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.config.annotation.web.configurers.AbstractHttpConfigurer; 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.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.web.SecurityFilterChain; @Configuration @EnableWebSecurity public class WebSecurityConfig { @Bean protected UserDetailsService userDetailsService() { UserDetails user = User.builder() .username("user") .password(passwordEncoder().encode("user123")) .roles("USER") .build(); UserDetails admin = User.builder() .username("admin") .password(passwordEncoder().encode("admin123")) .roles("USER", "ADMIN") .build(); return new InMemoryUserDetailsManager(user, admin); } @Bean protected PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception { return http .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests( request -> request.requestMatchers("/login").permitAll() .requestMatchers("/**").authenticated() ) .formLogin(form -> form.loginPage("/login") .defaultSuccessUrl("/") .failureUrl("/login?error=true") .permitAll()) .logout(config -> config .logoutUrl("/logout") .logoutSuccessUrl("/login") .build(); } }
控制器類
在此類中,我們為 "/" 端點和 "/login" 端點建立了對映,分別用於此應用程式的首頁和登入頁面。我們使用了 @EnableMethodSecurity 註解來啟用方法級安全性,因為它預設是關閉的。
AuthController.java
package com.tutorialspoint.security.formlogin.controllers; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; @Controller @EnableMethodSecurity public class AuthController { @GetMapping("/") public String home() { return "index"; } @GetMapping("/login") public String login() { return "login"; } // User with Admin role only can access this method @GetMapping("/update") @ResponseBody @PreAuthorize("hasRole('ROLE_ADMIN')") public String update() { return "Details Updated."; } }
檢視
讓我們在 /src/main/resources/templates 資料夾中建立 index.html 檔案,其內容如下,用作主頁並顯示登入使用者的名稱。
index.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-springsecurity3"> <head> <title> Hello World! </title> </head> <body> <h1 th:inline="text">Hello <span sec:authentication="name"></span>!</h1> <a href="/update" alt="update details">Update Details</a> <form th:action="@{/logout}" method="post"> <input type="submit" value="Sign Out"/> </form> </body> <html>
login.html
讓我們在 /src/main/resources/templates 資料夾中建立 login.html 檔案,其內容如下,用作登入頁面。我們使用預設名稱 username、password 和 remember-me 作為文字欄位。如果使用其他名稱,則也需要在 Spring Security 配置類中設定相同的名稱。
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org" xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3"> <head> <title>Spring Security Example</title> </head> <body> <div th:if="${param.error}"> <p>Bad Credentials</p> </div> <div th:if="${param.logout}">You have been logged out.</div> <form th:action="@{/login}" method="post"> <h1>Please sign in</h1> <table> <tr> <td><label for="username"><b>Username</b></label></td> <td><input type="text" placeholder="Enter Username" name="username" id="username" required></td> </tr> <tr> <td><label for="password"><b>Password</b></label></td> <td><input type="password" placeholder="Enter Password" name="password" id="password" required></td> </tr> <tr> <td><label for="remember-me"><b>Remember Me</b></label> </td> <td><input type="checkbox" name="remember-me" /></td> </tr> <tr> <td> </td> <td><input type="submit" value="Sign In" /></td> </tr> </table> </form> </body> </html>
在登入表單中,我們使用 POST 方法登入,同時使用名稱和 ID 為 username、password 和 remember-me 複選框的輸入欄位。如果選中複選框,則使用者登入應用程式時將建立記住我的 Cookie。
執行應用程式
由於我們所有元件都已準備就緒,因此讓我們執行應用程式。右鍵單擊專案,選擇 Run As,然後選擇 Spring Boot App。
它將啟動應用程式,並且應用程式啟動後,我們可以執行 localhost:8080 以檢查更改。
輸出
現在開啟 localhost:8080,您可以看到我們的登入頁面。使用 user 憑據登入
使用者登入頁面

使用者主頁
使用使用者的有效憑據,將顯示一個使用者頁面,如下所示,其中包含一個“更新詳細資訊”連結

單擊“更新詳細資訊”連結,它將顯示“無效憑據”,並且此方法對使用者不可用。

使用管理員使用者登入
現在使用瀏覽器後退按鈕返回。單擊登出按鈕,這將再次載入登入頁面。現在使用管理員憑據登入。

管理員使用者主頁
使用管理員的有效憑據,將顯示一個管理員頁面,如下所示,其中包含一個“更新詳細資訊”連結

單擊“更新詳細資訊”連結,它將顯示一條訊息“詳細資訊已更新”,並且此方法對管理員可用。
