java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Spring Security @PreAuthorize 注解使用

Spring Security中的 @PreAuthorize 注解使用方法和示例代码

作者:小猿、

@PreAuthorize是SpringSecurity中用于方法级权限控制的重要注解,本文详细介绍了@PreAuthorize的使用方法,通过合理使用@PreAuthorize,可以实现细粒度的权限控制,确保应用程序的安全性,感兴趣的朋友跟随小编一起看看吧

概述

在 Spring Security 框架中,@PreAuthorize注解是实现方法级权限控制的重要工具。它提供了灵活而强大的方式来保护应用程序中的方法,确保只有具备特定权限的用户才能访问。本文将详细介绍@PreAuthorize注解的使用方法、应用场景和示例代码。

什么是 @PreAuthorize 注解

@PreAuthorize是 Spring Security 提供的一个方法级安全注解,用于在方法执行前进行权限检查。它可以基于表达式来定义访问规则,只有当表达式计算结果为true时,方法才会被执行;否则将拒绝访问并抛出AccessDeniedException

与传统的 URL 级别的安全控制相比,@PreAuthorize提供了更细粒度的权限控制,能够直接作用于服务层或控制器层的方法。

启用 @PreAuthorize 注解

要使用@PreAuthorize注解,首先需要在 Spring 配置类中启用全局方法安全:

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
    // 配置内容
}

Spring Boot 3.4.3 中的配置方式

Spring Boot 3.x 中,@PreAuthorize的启用方式与之前版本不同,主要变化是使用@EnableMethodSecurity替代了旧的@EnableGlobalMethodSecurity

基础配置

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@Configuration
@EnableMethodSecurity(prePostEnabled = true) // 启用@PreAuthorize支持
public class SecurityConfig {
    // 密码编码器
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    // 配置用户信息(示例使用内存用户)
    @Bean
    public UserDetailsService userDetailsService() {
        return new InMemoryUserDetailsManager(
            User.withUsername("admin")
                .password(passwordEncoder().encode("admin123"))
                .roles("ADMIN")
                .build(),
            User.withUsername("user")
                .password(passwordEncoder().encode("user123"))
                .roles("USER")
                .build(),
            User.withUsername("editor")
                .password(passwordEncoder().encode("editor123"))
                .roles("EDITOR")
                .build()
        );
    }
}

注意:Spring Security 6.x 默认不再自动添加 "ROLE_" 前缀,hasRole('ADMIN')实际检查的是 "ADMIN" 权限,而非旧版本的 "ROLE_ADMIN"。

常用表达式语法

@PreAuthorize注解的值是一个 SpEL 表达式,常用表达式包括:

实际应用场景与示例

1. 基础访问控制

在控制器层使用@PreAuthorize控制 API 访问权限:

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ApiController {
    // 公开接口,所有人可访问
    @GetMapping("/public")
    @PreAuthorize("permitAll()")
    public String publicResource() {
        return "This is a public resource";
    }
    // 需认证后访问
    @GetMapping("/protected")
    @PreAuthorize("isAuthenticated()")
    public String protectedResource() {
        return "This is a protected resource";
    }
    // 仅管理员可访问
    @GetMapping("/admin")
    @PreAuthorize("hasRole('ADMIN')")
    public String adminResource() {
        return "This is an admin resource";
    }
    // 管理员或编辑可访问
    @GetMapping("/editor")
    @PreAuthorize("hasAnyRole('ADMIN', 'EDITOR')")
    public String editorResource() {
        return "This is an editor resource";
    }
}

2. 服务层方法权限控制

在服务层对业务方法进行权限控制:

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
@Service
public class DocumentService {
    // 需要文档创建权限
    @PreAuthorize("hasAuthority('DOCUMENT_CREATE')")
    public String createDocument(String content) {
        // 业务逻辑:创建文档
        return "Document created with content: " + content;
    }
    // 需要文档删除权限或管理员角色
    @PreAuthorize("hasAuthority('DOCUMENT_DELETE') or hasRole('ADMIN')")
    public void deleteDocument(Long documentId) {
        // 业务逻辑:删除文档
        System.out.println("Deleting document with ID: " + documentId);
    }
}

3. 基于方法参数的权限验证

确保用户只能操作自己有权限的数据:

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
@Service
public class UserService {
    // 确保用户只能更新自己的信息
    @PreAuthorize("#userId == authentication.principal.id")
    public void updateUserProfile(Long userId, String newName) {
        // 业务逻辑:更新用户信息
        System.out.println("Updating profile for user " + userId + " to " + newName);
    }
    // 管理员可以查看任何用户,普通用户只能查看自己
    @PreAuthorize("hasRole('ADMIN') or #userId == authentication.principal.id")
    public String getUserDetails(Long userId) {
        // 业务逻辑:获取用户详情
        return "Details for user " + userId;
    }
}

4. 调用自定义 Bean 进行复杂权限判断

对于复杂的权限逻辑,可以封装到专门的安全服务中:

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/projects")
public class ProjectController {
    // 项目所有者或管理员可以更新项目
    @PutMapping("/{projectId}")
    @PreAuthorize("@projectSecurityService.isOwner(#projectId, authentication.principal) or hasRole('ADMIN')")
    public String updateProject(@PathVariable Long projectId, @RequestBody String projectData) {
        return "Project " + projectId + " updated successfully";
    }
}

对应的自定义安全服务:

import org.springframework.stereotype.Component;
@Component("projectSecurityService")
public class ProjectSecurityService {
    /**
     * 检查用户是否是项目的所有者
     * @param projectId 项目ID
     * @param principal 当前用户主体
     * @return 是否为所有者
     */
    public boolean isOwner(Long projectId, Object principal) {
        // 在实际应用中,这里会查询数据库验证项目所有权
        // 这里仅作示例:假设用户"user"拥有ID小于100的项目
        String username = principal.toString();
        return "user".equals(username) && projectId < 100;
    }
}

处理权限不足的情况

@PreAuthorize表达式返回false时,Spring Security 会抛出AccessDeniedException。可以通过全局异常处理器统一处理:

import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(AccessDeniedException.class)
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public String handleAccessDenied(AccessDeniedException ex) {
        return "Access denied: You don't have permission to perform this action";
    }
}

注意事项

总结

在 Spring Boot 3.4.3 中,@PreAuthorize注解通过@EnableMethodSecurity启用,提供了强大而灵活的方法级权限控制能力。它支持复杂的 SpEL 表达式,能够实现基于角色、权限、用户属性和业务逻辑的细粒度权限判断。

合理使用@PreAuthorize可以显著提高应用程序的安全性,确保敏感操作和数据得到适当的保护。在实际开发中,应根据业务需求选择合适的权限控制策略,并遵循最小权限原则,仅为用户分配必要的权限。

到此这篇关于Spring Security中的 @PreAuthorize 注解使用方法和示例代码的文章就介绍到这了,更多相关Spring Security @PreAuthorize 注解使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文