Spring Security 表達式入門

Spring Security
Remote
1
09:52 PM · Nov 29 ,2025

1. 簡介

在本教程中,我們將重點介紹 Spring Security 表達式及其在實踐中的使用示例。 Spring Security 表達式提供了一種聲明式定義授權規則的方式。

它們允許在指定誰可以訪問特定 URL 或執行某些方法時具有靈活性。 這些表達式對於在我們的應用程序的各個級別處理安全至關重要,從 Web 請求授權到方法級別安全。

在查看更復雜的實現,例如 ACL,之前,重要的是要對安全表達式有堅實的基礎,因為它們在正確使用時可能非常靈活和強大。

2. Maven 依賴項

為了在我們的項目中使用 Spring Security,我們在 pom.xml 中包含了以下依賴項:

<dependencies>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-web</artifactId>
        <version>6.1.5</version>
   </dependency>
</dependencies>

spring-security-web 依賴項對於 Spring Security 至關重要,而 spring-core 和 spring-context 則對於一個功能齊全的 Web 應用程序是必需的。

3. 配置

Spring Security 可以使用基於 Java 的配置進行配置,這提供了更好的靈活性,並且是大多數現代 Spring 應用中推薦的方法。 下面是一個示例:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@ComponentScan("com.baeldung.security")
public class SecurityJavaConfig {
    ...
}

@EnableGlobalMethodSecurity(prePostEnabled = true) 註解使@PreAuthorize@PostAuthorize 註解可以使用,從而允許對方法級別進行安全設置。 通過這些註解,我們可以應用在方法執行之前或之後的安全邏輯。

4. Web Security Expressions

Let’s dive into some common Spring Security expressions:

4.1. hasRole(), hasAnyRole()

These expressions are used to specify which roles are required to access specific URLs or methods:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    return http
      .antMatchers("/auth/admin/*").hasRole("ADMIN")
      .antMatchers("/auth/*").hasAnyRole("ADMIN", "USER")
      .build();
}

The above example ensures that only users with the ADMIN role can access URLs starting with/auth/admin/, while both ADMIN and USER roles are allowed access to URLs starting with /auth/.

4.2. hasAuthority(), hasAnyAuthority()

Roles and authorities are both used for access control, but they have slightly different semantics. Authorities don’t require the ROLE_ prefix, which Spring adds automatically for roles. Using authorities instead of roles is recommended, as it allows more flexibility.

Here’s a quick example of defining users with specific authorities:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    return http
      .authorizeHttpRequests(auth -> auth
        .requestMatchers("/auth/admin/*").hasAuthority("ADMIN")
        .requestMatchers("/auth/*").hasAnyAuthority("ADMIN", "USER")
      )
      .build();
}

4.3. permitAll(), denyAll()

These expressions allow us to explicitly permit or deny access to specific URLs. Let’s have a look at the example:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    return http
      .authorizeHttpRequests(auth -> auth
        .requestMatchers("/public/**").permitAll()
        .requestMatchers("/private/**").denyAll()
      )
      .build();
}

This configuration uses permitAll() to allow anyone to access URLs starting with /public/, making them publicly available, while denyAll() completely blocks access to URLs beginning with /private/, effectively disabling those resources.

4.4. isAnonymous(), isRememberMe(), isAuthenticated(), isFullyAuthenticated()

These expressions control access based on the user’s authentication status:

  • isAnonymous(): Allows access to unauthenticated users.
  • isAuthenticated(): Restricts access to authenticated users.
  • isFullyAuthenticated(): Requires users to re-authenticate for sensitive operations.
  • isRememberMe(): Allows access to users authenticated via “remember-me” functionality.
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    return http
      .authorizeHttpRequests(auth -> auth
        .requestMatchers("/login").anonymous()
        .requestMatchers("/dashboard").authenticated()
        .requestMatchers("/settings").fullyAuthenticated()
      )
      .build();
}

In this example, we use anonymous() to ensure only unauthenticated users can reach /login, authenticated() requires a user to be logged in for /dashboard, and fullyAuthenticated() mandates a fresh login for sensitive /settings access, enhancing security.

4.5. principal, authentication

Spring Security provides access to the current user’s authentication details through the Principal and Authentication objects. These objects contain information about the authenticated user, such as their username and roles.

For instance, when a user makes a request to a protected endpoint, we can retrieve their details using the Principal object:

@GetMapping("/profile")
public String profile(Principal principal) {
    return "Hello, " + principal.getName();
}

In this example, the profile() method extracts the logged-in user’s name from the Principal object and returns a greeting message.

Alternatively, if we need more details about the authentication, such as roles or authentication type, we can use the Authentication object instead:

@GetMapping("/user-details")
public String userDetails(Authentication authentication) {
    return "User: " + authentication.getName() + ", Roles: " + authentication.getAuthorities();
}

4.6. hasPermission() APIs

The hasPermission expression is part of Spring Security’s ACL system, allowing fine-grained permission checks on domain objects. Instead of just checking roles (like hasRole(‘ADMIN’)), it verifies specific permissions for a given resource.

For example, we can enforce that only users with the isEditor permission can approve articles:

@PreAuthorize("hasPermission(#article, 'isEditor')")
public void acceptArticle(Article article) {
   ...
}

To use hasPermission, we must configure a custom PermissionEvaluator. This evaluator defines how permissions are checked for different objects:

@Override
protected MethodSecurityExpressionHandler expressionHandler() {
    DefaultMethodSecurityExpressionHandler expressionHandler = 
      new DefaultMethodSecurityExpressionHandler();
    expressionHandler.setPermissionEvaluator(new CustomInterfaceImplementation());
    return expressionHandler;
}

5. 方法級別安全,使用 @PreAuthorize@PostAuthorize

Spring Security 也允許我們在方法級別應用安全表達式。 使用 @PreAuthorize@PostAuthorize,我們可以執行方法執行之前或之後的安全檢查。 這在基於角色或其他條件來保護服務時尤其有用。

此示例確保只有具有 ADMIN 角色的用户才能執行 deleteUser() 方法:

@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(User user) {
    // 方法邏輯
}

與之相反,@PostAuthorize() 確保用户只能在方法執行之後查看自己的用户對象:

@PostAuthorize("returnObject.owner == authentication.name")
public User getUser(Long id) {
    // 檢索用户並返回
}

6. 路徑變量和 @PreFilter / @PostFilter

Spring Security 還支持使用 @PreFilter@PostFilter 註解在方法中過濾集合。這在我們需要在方法執行之前或之後,根據已登錄用户的權限或其他標準進行過濾時非常有用。

@PreFilter 註解確保僅將集合中的某些元素傳遞給方法,在執行之前過濾掉未授權的元素:

@PreFilter("filterObject.owner == authentication.name")
public void updatePosts(List<Post> posts) {
    // 更新用户發佈的帖子
}

表達式 filterObject.owner == authentication.name 確保僅處理屬於已登錄用户 (authentication.name) 的帖子。

@PostFilter 註解確保執行完成後僅返回允許的元素給調用者。這在檢索數據時很有用,但可以限制訪問僅授權的項目。 讓我們來看一個例子:

@PostFilter("filterObject.owner == authentication.name")
public List<Post> getPosts() {
    // 獲取所有帖子和
}

@PostFilter 註解過濾掉不屬於當前認證用户的所有帖子。

7. 結論

在本文中,我們探討了關鍵的 Spring Security 表達式及其在保護您的 Web 應用程序中的應用。通過利用諸如 hasRole()hasAuthority()permitAll() 等表達式,以及各種認證相關的表達式,我們可以對應用程序的安全性進行精細調整。此外,使用 @PreAuthorize@PostAuthorize@PreFilter@PostFilter 允許對方法級別的安全性以及數據過濾進行更精細的控制。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.