Spring Security:升級已棄用的 WebSecurityConfigurerAdapter
一、概述
Spring Security 允許通過擴展WebSecurityConfigurerAdapter
類為端點授權或身份驗證管理器配置等特性定制 HTTP 安全性。然而,從最近的版本開始,Spring 棄用了這種方法並鼓勵基於組件的安全配置。
在本教程中,我們將看到一個示例,說明如何在 Spring Boot 應用程序中替換此棄用並運行一些 MVC 測試。
2. 沒有WebSecurityConfigurerAdapter
的 Spring Security
我們通常會看到擴展WebSecurityConfigureAdapter
類的 Spring HTTP 安全配置類。
但是,從 5.7.0-M2 版本開始,Spring 不贊成使用WebSecurityConfigureAdapter
並建議在沒有它的情況下創建配置。
我們將使用內存中身份驗證創建一個示例 Spring Boot 應用程序來展示這種新型配置。
首先,讓我們定義我們的配置類:
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class SecurityConfig {
// config
}
我們正在添加方法安全註釋以啟用基於不同角色的處理。
2.1。配置身份驗證
使用WebSecurityConfigureAdapter,
我們使用AuthenticationManagerBuilder
來設置我們的身份驗證上下文。
現在,如果我們想避免棄用,我們可以定義一個UserDetailsManager
或UserDetailsService
組件:
@Bean
public UserDetailsService userDetailsService(BCryptPasswordEncoder bCryptPasswordEncoder) {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("user")
.password(bCryptPasswordEncoder.encode("userPass"))
.roles("USER")
.build());
manager.createUser(User.withUsername("admin")
.password(bCryptPasswordEncoder.encode("adminPass"))
.roles("USER", "ADMIN")
.build());
return manager;
}
或者,給定我們的UserDetailService
,我們甚至可以設置一個AuthenticationManager
:
@Bean
public AuthenticationManager authManager(HttpSecurity http, BCryptPasswordEncoder bCryptPasswordEncoder, UserDetailService userDetailService)
throws Exception {
return http.getSharedObject(AuthenticationManagerBuilder.class)
.userDetailsService(userDetailsService)
.passwordEncoder(bCryptPasswordEncoder)
.and()
.build();
}
同樣,如果我們使用 JDBC 或 LDAP 身份驗證,這將起作用。
2.2.配置 HTTP 安全性
更重要的是,如果我們想避免棄用 HTTP 安全性,我們現在可以創建一個SecurityFilterChain
bean。
例如,假設我們想根據角色保護端點,並留下一個匿名入口點僅用於登錄。我們還將任何刪除請求限制為管理員角色。我們將使用基本身份驗證:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf()
.disable()
.authorizeRequests()
.antMatchers(HttpMethod.DELETE)
.hasRole("ADMIN")
.antMatchers("/admin/**")
.hasAnyRole("ADMIN")
.antMatchers("/user/**")
.hasAnyRole("USER", "ADMIN")
.antMatchers("/login/**")
.anonymous()
.anyRequest()
.authenticated()
.and()
.httpBasic()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
return http.build();
}
HTTP 安全將構建一個DefaultSecurityFilterChain
對象來加載請求匹配器和過濾器。
2.3.配置網絡安全
此外,對於 Web 安全,我們現在可以使用回調接口WebSecurityCustomizer.
讓我們添加一個調試級別並忽略一些路徑,例如圖像或腳本:
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.debug(securityDebug)
.ignoring()
.antMatchers("/css/**", "/js/**", "/img/**", "/lib/**", "/favicon.ico");
}
3.端點控制器
讓我們為我們的應用程序定義一個簡單的 REST 控制器類:
@RestController
public class ResourceController {
@GetMapping("/login")
public String loginEndpoint() {
return "Login!";
}
@GetMapping("/admin")
public String adminEndpoint() {
return "Admin!";
}
@GetMapping("/user")
public String userEndpoint() {
return "User!";
}
@GetMapping("/all")
public String allRolesEndpoint() {
return "All Roles!";
}
@DeleteMapping("/delete")
public String deleteEndpoint(@RequestBody String s) {
return "I am deleting " + s;
}
}
正如我們之前在定義 HTTP 安全性時提到的,我們將添加一個任何人都可以訪問的通用/login
端點、管理員和用戶的特定端點,以及一個不受角色保護但仍需要身份驗證的/all
端點。
4. 測試端點
讓我們使用 MVC 模擬將我們的新配置添加到 Spring Boot Test 以測試我們的端點。
4.1。測試匿名用戶
匿名用戶可以訪問/login
端點。如果他們嘗試訪問其他內容,他們將未經授權( 401
):
@Test
@WithAnonymousUser
public void whenAnonymousAccessLogin_thenOk() throws Exception {
mvc.perform(get("/login"))
.andExpect(status().isOk());
}
@Test
@WithAnonymousUser
public void whenAnonymousAccessRestrictedEndpoint_thenIsUnauthorized() throws Exception {
mvc.perform(get("/all"))
.andExpect(status().isUnauthorized());
}
此外,對於除/login
之外的所有端點,我們總是需要身份驗證,就像/all
端點一樣。
4.2.測試用戶角色
用戶角色可以訪問通用端點以及我們為此角色授予的所有其他路徑:
@Test
@WithUserDetails()
public void whenUserAccessUserSecuredEndpoint_thenOk() throws Exception {
mvc.perform(get("/user"))
.andExpect(status().isOk());
}
@Test
@WithUserDetails()
public void whenUserAccessRestrictedEndpoint_thenOk() throws Exception {
mvc.perform(get("/all"))
.andExpect(status().isOk());
}
@Test
@WithUserDetails()
public void whenUserAccessAdminSecuredEndpoint_thenIsForbidden() throws Exception {
mvc.perform(get("/admin"))
.andExpect(status().isForbidden());
}
@Test
@WithUserDetails()
public void whenUserAccessDeleteSecuredEndpoint_thenIsForbidden() throws Exception {
mvc.perform(delete("/delete"))
.andExpect(status().isForbidden());
}
值得注意的是,如果用戶角色嘗試訪問受管理員保護的端點,用戶會收到“禁止”( 403
)錯誤。
相反,沒有憑據的人(例如前面示例中的匿名用戶)將收到“未經授權”錯誤( 401
)。
4.3.測試管理員角色
如我們所見,具有管理員角色的人可以訪問任何端點:
@Test
@WithUserDetails(value = "admin")
public void whenAdminAccessUserEndpoint_thenOk() throws Exception {
mvc.perform(get("/user"))
.andExpect(status().isOk());
}
@Test
@WithUserDetails(value = "admin")
public void whenAdminAccessAdminSecuredEndpoint_thenIsOk() throws Exception {
mvc.perform(get("/admin"))
.andExpect(status().isOk());
}
@Test
@WithUserDetails(value = "admin")
public void whenAdminAccessDeleteSecuredEndpoint_thenIsOk() throws Exception {
mvc.perform(delete("/delete").content("{}"))
.andExpect(status().isOk());
}
5. 結論
在本文中,我們了解瞭如何在不使用WebSecurityConfigureAdapter
的情況下創建 Spring Security 配置,並在創建用於身份驗證、HTTP 安全和 Web 安全的組件時替換它。
與往常一樣,我們可以在 GitHub 上找到工作代碼示例。