1. Overview
1.概述
In this tutorial, we’ll learn about the methods permitAll() and anonymous() of the class HttpSecurity from Spring Security Framework. Spring Security Framework helps prevent vulnerability attacks and enables authentication and authorization of web applications. Leveraging it, web applications can control access to the server resources such as HTML forms, CSS files, JS files, Web Service endpoints, etc. It also helps enable RBAC (Role-Based Access Control) to access server resources.
在本教程中,我们将学习 Spring 安全框架中 HttpSecurity 类的 permitAll() 和 anonymous() 方法。Spring安全框架有助于防止漏洞攻击,并实现网络应用程序的身份验证和授权。利用它,网络应用程序可以控制对 HTML 表单、CSS 文件、JS 文件、Web 服务端点等服务器资源的访问。它还有助于启用 RBAC(基于角色的访问控制)来访问服务器资源。
There are always some parts of a web application that users can access only after authentication. However, there are also some parts where user authentication doesn’t matter. Interestingly, there are also scenarios where authenticated users cannot access certain server resources.
网络应用中总会有一些部分需要用户通过身份验证后才能访问。但是,也有一些部分的用户身份验证并不重要。有趣的是,在某些情况下,通过身份验证的用户也无法访问某些服务器资源。
We’ll shortly discuss all of them and see how permitAll() and anonymous() methods help define these kinds of security accesses with Spring Security Expressions.
我们将很快讨论所有这些方法,并了解 permitAll() 和 anonymous() 方法如何通过 Spring安全表达式来帮助定义这类安全访问。
2. Security Requirements
2.安保要求
Before we move ahead, let’s imagine an e-commerce website with the following requirements:
在继续讨论之前,让我们设想一个电子商务网站有以下要求:
- Both anonymous and authenticated users can view the products on the website
- Audit entry for anonymous and authenticated user requests
- Anonymous users can access the user registration form, whereas authenticated users cannot
- Only authenticated user can view their shopping cart
3. Controller and WebSecurity Configuration
3.控制器和网络安全配置
First, let’s define our controller class that has the endpoints of the e-commerce website:
首先,让我们定义拥有电子商务网站端点的控制器类:
@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";
}
}
Earlier, we discussed the security requirements of the website. Let’s implement those in the class EcommerceWebSecruityConfig:
前面,我们讨论了网站的安全要求。让我们在类 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 {
return http.addFilterAfter(new AuditInterceptor(), AnonymousAuthenticationFilter.class)
.authorizeHttpRequests(request -> request.requestMatchers(new AntPathRequestMatcher("/private/**"))
.authenticated())
.httpBasic(Customizer.withDefaults())
.authorizeHttpRequests(request -> request.requestMatchers(new AntPathRequestMatcher("/public/showProducts"))
.permitAll())
.authorizeHttpRequests(request -> request.requestMatchers(new AntPathRequestMatcher("/public/registerUser"))
.anonymous())
.build();
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Basically, we’ve defined the following:
基本上,我们已经定义了以下内容:
- An AuditInterceptor filter after AnonymousAuthenticationFilter to log requests made by anonymous and authenticated users
- Users have to authenticate mandatorily to access URLs with the path /private
- All users can access the path /public/showProducts
- Only anonymous users can access the path /public/registerUser
We’ve also configured a user spring that would be used throughout the article to invoke the web service endpoints defined in EcommerceController.
我们还配置了一个用户 spring,它将在整篇文章中用于调用 EcommerceController 中定义的网络服务端点。
4. Method permitAll() in HttpSecurity
4.HttpSecurity中的方法 permitAll()</em
Basically, in the class EcommerceWebSecurityConfig, we used permitAll() to open endpoints /public/showProducts for all. Now, let’s see if that works:
基本上,在类 EcommerceWebSecurityConfig 中, 我们使用 permitAll() 打开了 /public/showProducts for all 的端点。现在,让我们看看这是否有效:
@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"));
}
As expected, both anonymous and authenticated users can access the page.
不出所料,匿名用户和认证用户都可以访问该页面。
Also, with Spring Security 6, permitAll() helps in securing static resources like JS and CSS files quite efficiently. Moreover, we should always prefer permitAll() over ignoring the static resources in the Spring Security Filter Chain. Because the Filter Chain will not be able to set the security headers on the ignored static resources.
此外,在 Spring Security 6 中,permitAll() 可有效地保护 JS 和 CSS 文件等静态资源。此外,在 Spring Security 筛选器链中,permitAll() 比忽略静态资源更受我们青睐。因为过滤器链将无法在忽略的静态资源上设置安全标头。
5. Method anonymous() in HttpSecurity
5.HttpSecurity中的方法 anonymous()</em
Before we start implementing the requirement of the e-commerce website, it’s important to understand the idea behind the expression anonymous().
在开始实现电子商务网站的要求之前,我们有必要了解 anonymous() 表达式背后的想法。
In line with the Spring Security principle, we need to define the permissions and limitations for all users. This stands valid for an anonymous user as well. Hence, they are associated with ROLE_ANONYMOUS.
根据 Spring 安全原则,我们需要定义所有用户的权限和限制。这也适用于匿名用户。因此,他们与 ROLE_ANONYMOUS 关联。
5.1. Implement AuditInterceptor
5.1.实施审计拦截器</em
Spring Security populates the Authentication object of an anonymous user in the AnonymousAuthenticationFilter. It’s helpful in auditing the operations performed by anonymous and registered users through an interceptor on the e-commerce website.
Spring Security 会在 AnonymousAuthenticationFilter 中填充匿名用户的 Authentication 对象。这有助于通过电子商务网站上的拦截器审计匿名用户和注册用户执行的操作。
Here is the outline of the AuditInterceptor, which we configured earlier in the class EcommerceWebSecurityConfig:
下面是 AuditInterceptor 的大纲,我们之前在类 EcommerceWebSecurityConfig 中配置了该拦截器:
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);
}
}
Even for an anonymous user, the Authentication object is not null. This leads to a robust implementation of the AuditInterceptor. It has separate flows for auditing anonymous and authenticated users.
即使是匿名用户,Authentication 对象也不会为空。这导致了 AuditInterceptor 的强大实现。它具有用于审核匿名用户和已验证用户的独立流程。
5.2. Deny Access to Register User Screen to Authenticated Users
5.2.拒绝已通过身份验证的用户访问注册用户屏幕
In the class EcommerceWebSecurityConfig, with the expression anonymous(), we ensured that only anonymous users can access the endpoint public/registerUser. Whereas the authenticated users cannot access it.
在类 EcommerceWebSecurityConfig 中,通过表达式 匿名(),我们确保只有匿名用户才能访问端点 public/registerUser。而已通过身份验证的用户则无法访问。
Let’s see if it achieves the expected result:
让我们看看它是否达到了预期效果:
@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"));
}
So, an anonymous user could access the user registration page.
因此,匿名用户可以访问用户注册页面。
Similarly, will it be able to deny access to an authenticated user? Let’s find out:
同样,它能否拒绝已通过身份验证的用户访问?让我们一探究竟:
@WithMockUser(username = "spring", password = "secret")
@Test
public void givenAuthenticatedUser_whenAccessToUserRegisterPage_thenDenyAccess() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/public/registerUser"))
.andExpect(MockMvcResultMatchers.status().isForbidden());
}
The above method successfully denies access to the user registration page to an authenticated user.
上述方法成功地拒绝了已通过身份验证的用户访问用户注册页面。
Unlike the permitAll() method, anonymous() can also be used to serve static resources when authentication is not needed for them.
与permitAll() 方法不同,anonymous() 还可用于在静态资源不需要身份验证时为其提供服务。
6. Conclusion
6.结论
In this tutorial, with the help of examples, we demonstrated the difference between permitAll() and anonymous() methods.
在本教程中,我们通过示例演示了 permitAll() 和 anonymous() 方法.之间的区别。
anonymous() is used when we’ve public content that should be accessible only to anonymous users. In contrast, permitAll() is used when we want to allow access to specific URLs for all users without distinguishing between their authentication statuses.
anonymous() 用于只允许匿名用户访问的公开内容。相反,permitAll() 用于允许所有用户访问特定 URL,而不区分他们的身份验证状态。
Finally, the examples can be found over on GitHub.
最后,您可以在 GitHub 上找到这些示例。