Spring Security中permitAll()和anonymous()之間的區別
1. 概述
在本教學中,我們將了解 Spring Security 框架中HttpSecurity
類別的permitAll()
和anonymous()
方法。 Spring Security Framework 有助於防止漏洞攻擊並支援 Web 應用程式的身份驗證和授權。利用它,Web 應用程式可以控制對伺服器資源(例如 HTML 表單、CSS 檔案、JS 檔案、Web 服務端點等)的存取。它還有助於啟用 RBAC(基於角色的存取控制)來存取伺服器資源。
Web 應用程式的某些部分始終是使用者只能在身份驗證後才能存取的。然而,也有一些部分用戶身份驗證並不重要。有趣的是,在某些情況下,經過身份驗證的使用者無法存取某些伺服器資源。
我們很快就會討論所有這些,並了解permitAll()
和anonymous() methods
如何幫助使用Spring安全表達式定義這些類型的安全存取。
2. 安全要求
在我們繼續之前,讓我們想像一個有以下要求的電子商務網站:
- 匿名用戶和經過身份驗證的用戶都可以查看網站上的產品
- 匿名和經過身份驗證的使用者請求的審核條目
- 匿名使用者可以存取使用者註冊表單,而經過驗證的使用者則無法存取
- 只有經過身份驗證的用戶才能查看他們的購物車
3. 控制器和WebSecurity配置
首先,讓我們定義具有電子商務網站端點的控制器類別:
@RestController
public class EcommerceController {
@GetMapping("/private/showCart")
public @ResponseBody String showCart() {
return "Show Cart";
}
@GetMapping("/public/showProducts")
public @ResponseBody String listProducts() {
return "List Products";
}
@GetMapping("/public/registerUser")
public @ResponseBody String registerUser() {
return "Register User";
}
}
早些時候,我們討論了網站的安全要求。讓我們在EcommerceWebSecruityConfig
類別中實作這些:
@Configuration
@EnableWebSecurity
public class EcommerceWebSecurityConfig {
@Bean
public InMemoryUserDetailsManager userDetailsService(PasswordEncoder passwordEncoder) {
UserDetails user = User.withUsername("spring")
.password(passwordEncoder.encode("secret"))
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.addFilterAfter(new AuditInterceptor(), AnonymousAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/private/**").authenticated().and().httpBasic()
.and().authorizeRequests()
.antMatchers("/public/showProducts").permitAll()
.antMatchers("/public/registerUser").anonymous();
return http.build();
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
基本上,我們定義了以下內容:
-
AnonymousAuthenticationFilter
之後的AuditInterceptor
過濾器,用於記錄匿名和經過驗證的使用者發出的請求 - 使用者必須強制進行身份驗證才能存取路徑為
/private
URL - 所有使用者都可以存取路徑
/public/showProducts
- 只有匿名使用者才能存取路徑
/public/registerUser
我們也配置了一個使用者spring
,它將在整篇文章中用於呼叫EcommerceController
中定義的 Web 服務端點。
HttpSecurity
中的permitAll()
方法
基本上,在EcommerceWebSecurityConfig,
我們使用permitAll()
/public/showProducts for
.
現在,讓我們看看這是否有效:
@WithMockUser(username = "spring", password = "secret")
@Test
public void givenAuthenticatedUser_whenAccessToProductLinePage_thenAllowAccess() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/public/showProducts"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().string("List Products"));
}
@WithAnonymousUser
@Test
public void givenAnonymousUser_whenAccessToProductLinePage_thenAllowAccess() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/public/showProducts"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().string("List Products"));
}
正如預期的那樣,匿名用戶和經過身份驗證的用戶都可以訪問該頁面。
此外,在 Spring Security 6 中, permitAll()
有助於非常有效地保護 JS 和 CSS 檔案等靜態資源。此外, 我們應該始終選擇使用permitAll()
,而不是忽略Spring Security過濾器鏈中的靜態資源。因為過濾器鏈將無法在被忽略的靜態資源上設定安全標頭。
HttpSecurity
中的anonymous()
方法
在我們開始實現電子商務網站的要求之前,了解anonymous()
表達式背後的想法很重要。
符合Spring Security原則,我們需要為所有使用者定義權限和限制。這對於匿名用戶也有效。因此,它們與 ROLE_ANONYMOUS 相關聯。
5.1.實施AuditInterceptor
Spring Security 在[AnonymousAuthenticationFilter](https://docs.spring.io/spring-security/reference/6.1-SNAPSHOT/servlet/authentication/anonymous.html#anonymous-config)
中填入匿名使用者的Authentication
物件。它有助於透過電子商務網站上的攔截器審核匿名用戶和註冊用戶執行的操作。
以下是我們之前在EcommerceWebSecurityConfig
類別中配置的AuditInterceptor,
的摘要:
public class AuditInterceptor extends OncePerRequestFilter {
private final Logger logger = LoggerFactory.getLogger(AuditInterceptor.class);
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication instanceof AnonymousAuthenticationToken) {
logger.info("Audit anonymous user");
}
if (authentication instanceof UsernamePasswordAuthenticationToken) {
logger.info("Audit registered user");
}
filterChain.doFilter(request, response);
}
}
即使對於匿名用戶, Authentication
物件也不為 null。這導致了AuditInterceptor
的穩健實作。它具有單獨的流程用於審核匿名用戶和經過身份驗證的用戶。
5.2.拒絕經過身份驗證的使用者存取註冊用戶螢幕
在EcommerceWebSecurityConfig,
使用表達式[anonymous()](https://docs.spring.io/spring-security/reference/6.1-SNAPSHOT/servlet/authentication/anonymous.html#page-title) ,
我們確保只有匿名使用者才能存取端點public/registerUser
。而經過身份驗證的用戶無法存取它。
我們來看看是否達到了預期的效果:
@WithAnonymousUser
@Test
public void givenAnonymousUser_whenAccessToUserRegisterPage_thenAllowAccess() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/public/registerUser"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().string("Register User"));
}
因此,匿名用戶可以訪問用戶註冊頁面。
同樣,它是否能夠拒絕經過身份驗證的用戶的存取?讓我們來了解一下:
@WithMockUser(username = "spring", password = "secret")
@Test
public void givenAuthenticatedUser_whenAccessToUserRegisterPage_thenDenyAccess() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/public/registerUser"))
.andExpect(MockMvcResultMatchers.status().isForbidden());
}
上述方法成功拒絕經過驗證的使用者存取使用者註冊頁面。
與permitAll()
方法不同, anonymous()
也可用於在不需要身份驗證的情況下為靜態資源提供服務。
六,結論
在本教程中,我們藉助範例示範了permitAll()
和anonymous()
方法之間的差異.
當我們擁有隻能由匿名使用者存取的公開內容時,請使用anonymous()
。相反,當我們希望允許所有使用者存取特定 URL 而不區分他們的身份驗證狀態時,請使用permitAll()
。
最後,可以 在 GitHub 上找到範例。