@[toc] Spring Security 是一個功能強大且可高度定製的安全框架,它提供了一套完整的解決方案,用於保護基於 Spring 的應用程序。在 Spring Security 中,路徑匹配是權限控制的核心部分,它決定了哪些請求可以訪問特定的資源。本文將詳細介紹 Spring Security 中的路徑匹配策略,並提供相應的代碼示例。
在舊版的 Spring Security 中,路徑匹配方法有很多,但是新版 Spring Security 對這些方法進行了統一的封裝,都是調用 requestMatchers 方法進行處理:
public C requestMatchers(RequestMatcher... requestMatchers) {
Assert.state(!this.anyRequestConfigured, "Can't configure requestMatchers after anyRequest");
return chainRequestMatchers(Arrays.asList(requestMatchers));
}
requestMatchers 方法接收一個 RequestMatcher 類型的參數,RequestMatcher 是一個接口,這個接口是一個用來確定 HTTP 請求是否與給定的模式匹配的工具。這個接口提供了一種靈活的方式來定義請求的匹配規則,從而可以對不同的請求執行不同的安全策略。
所以在新版 Spring Security 中,不同的路徑匹配分方案實際上就是不同的 RequestMatcher 的實現類。
1. AntPathRequestMatcher
AntPathRequestMatcher
是 Spring 中最常用的請求匹配器之一,它使用 Ant 風格的路徑模式來匹配請求的 URI。
1.1 什麼是 Ant 風格的路徑模式
Ant 風格的路徑模式(Ant Path Matching)是一種用於資源定位的模式匹配規則,它源自 Apache Ant 這個 Java 構建工具。在 Ant 中,這種模式被用來指定文件系統中的文件和目錄。由於其簡單性和靈活性,Ant 風格的路徑模式也被其他許多框架和應用程序所採用,包括 Spring Security。
Ant 風格的路徑模式使用了一些特殊的字符來表示不同級別的路徑匹配:
-
?
:匹配任何單個字符(除了路徑分隔符)。 -
*
:匹配任何字符的序列(除了路徑分隔符),但不包括空字符串。 -
**
:匹配任何字符的序列,包括空字符串。至少匹配一個字符的序列,並且可以跨越路徑分隔符。 -
{}
:表示一個通配符的選擇,可以匹配多個逗號分隔的模式。例如,{,春夏秋冬}
可以匹配任何以春夏秋冬開頭的字符串。 -
[]
:在某些實現中,可以用於匹配括號內的單個字符。 -
()
:在某些實現中,可以用於分組匹配。
在 Spring Security 中,Ant 風格的路徑模式通常用於定義 URL 路徑和安全配置之間的映射關係。例如,你可以使用 Ant 風格的路徑模式來指定哪些 URL 路徑需要特定的權限或角色。
以下是一些 Ant 風格路徑模式的例子:
-
/users/*
:匹配以/users/
開始的任何路徑,如/users/123
或/users/profile
。 -
/users/**
:匹配以/users/
開始的任何路徑,包括子路徑,如/users/123
或/users/profile/picture
. -
/users/123
:精確匹配/users/123
。 -
/users/{id}
:雖然這不是 Ant 風格的模式,但它展示了路徑參數匹配,可以匹配/users/123
、/users/456
等。 -
/files/**.{jpg,png}
:匹配/files/
下所有以.jpg
或.png
結尾的文件路徑,如/files/image1.jpg
或/files/folder/image.png
。
通過使用 Ant 風格的路徑模式,你可以靈活地定義複雜的 URL 匹配規則,以適應不同的安全需求。
1.2 基本用法
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
// 創建 AntPathRequestMatcher 實例
RequestMatcher antMatcher = new AntPathRequestMatcher("/users/**", "GET");
// 使用 matcher 進行匹配
boolean isMatch = antMatcher.matches(request);
1.3 通配符
?
匹配任何單字符。*
匹配任何字符序列(但不包括目錄分隔符)。**
匹配任何字符序列,包括目錄分隔符。
// 匹配 /admin 下的任何資源,包括子目錄
RequestMatcher adminMatcher = new AntPathRequestMatcher("/admin/**");
// 匹配 /files 目錄下的任何 HTML 文件
RequestMatcher fileMatcher = new AntPathRequestMatcher("/files/*.{html,htm}", "GET");
2. RegexRequestMatcher
RegexRequestMatcher
使用正則表達式來匹配請求的 URI 和 HTTP 方法。
2.1 基本用法
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
// 創建 RegexRequestMatcher 實例
RequestMatcher regexMatcher = new RegexRequestMatcher("^/api/.*", "GET");
// 使用 matcher 進行匹配
boolean isMatch = regexMatcher.matches(request);
2.2 使用正則表達式
// 匹配任何以 /api 開頭的 URI
RequestMatcher apiMatcher = new RegexRequestMatcher("^/api/.*");
// 匹配任何 HTTP 方法
RequestMatcher anyMethodMatcher = new RegexRequestMatcher("^/.*", "GET|POST|PUT|DELETE");
2.3 結合 Spring Security
下面這段代碼,表示攔截所有以 html、css 以及 js 結尾的請求,這些請求可以直接訪問:
@Configuration
public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(a -> a.requestMatchers(new RegexRequestMatcher("^.*\\.(htm|css|js)$","GET")).permitAll())
.formLogin(Customizer.withDefaults())
.csrf(c -> c.disable());
return http.build();
}
}
3. RequestHeaderRequestMatcher
RequestHeaderRequestMatcher
用來匹配請求頭中的鍵和值。
import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;
// 創建 RequestHeaderRequestMatcher 實例
RequestMatcher headerMatcher = new RequestHeaderRequestMatcher("User-Agent", "Mozilla.*");
// 使用 matcher 進行匹配
boolean isMatch = headerMatcher.matches(request);
具體到 Spring Security 中,用法如下:
@Configuration
public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(a -> a.requestMatchers(new RequestHeaderRequestMatcher("User-Agent","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36")).permitAll())
.formLogin(Customizer.withDefaults())
.csrf(c -> c.disable());
return http.build();
}
}
4. NegatedRequestMatcher
NegatedRequestMatcher
允許你否定一個已有的 RequestMatcher
的匹配結果。
import org.springframework.security.web.util.matcher.NegatedRequestMatcher;
// 創建一個 matcher,然後否定它的匹配結果
RequestMatcher notAdminMatcher = new NegatedRequestMatcher(adminMatcher);
// 使用 negated matcher 進行匹配
boolean isNotMatch = notAdminMatcher.matches(request);
例如下面這段代碼表示除了 /hello
之外的地址,全都可以直接訪問:
@Configuration
public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(a -> a.requestMatchers(new NegatedRequestMatcher(new AntPathRequestMatcher("/hello"))).permitAll())
.formLogin(Customizer.withDefaults())
.csrf(c -> c.disable());
return http.build();
}
}
5. AndRequestMatcher 和 OrRequestMatcher
AndRequestMatcher
和 OrRequestMatcher
分別用來組合多個 RequestMatcher
實例,進行“與”或“或”的邏輯匹配。
5.1 AndRequestMatcher
import org.springframework.security.web.util.matcher.AndRequestMatcher;
// 組合多個 matcher 進行“與”匹配
RequestMatcher andMatcher = new AndRequestMatcher(apiMatcher, headerMatcher);
// 使用 andMatcher 進行匹配
boolean isMatch = andMatcher.matches(request);
5.2 OrRequestMatcher
import org.springframework.security.web.util.matcher.OrRequestMatcher;
// 組合多個 matcher 進行“或”匹配
RequestMatcher orMatcher = new OrRequestMatcher(adminMatcher, fileMatcher);
// 使用 orMatcher 進行匹配
boolean isMatch = orMatcher.matches(request);
6. 總結
Spring 提供了多種 RequestMatcher
實現類,以滿足不同的請求匹配需求。通過合理地使用這些匹配器,可以靈活地定義和實施安全策略。在實際應用中,你可能需要根據業務需求選擇合適的匹配器,並結合 Spring Security 的配置來實現細粒度的訪問控制。