Spring中使用@EnableMethodSecurity时静态方法无法注入Repository的问题
解决@EnableMethodSecurity下自定义表达式根注入Bean的问题
核心思路
不用纠结官方文档提到的静态方法,通过自定义MethodSecurityExpressionHandler+构造器注入依赖的方式,就能在自定义表达式根中使用Repository等Bean,同时兼容@EnableMethodSecurity。
步骤1:实现自定义方法安全表达式根
创建继承SecurityExpressionRoot并实现MethodSecurityExpressionOperations的类,注入所需Repository,并编写自定义权限校验方法:
public class CustomMethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations { private final UserRepository userRepository; private Object filterObject; private Object returnObject; // 通过构造器注入Repository public CustomMethodSecurityExpressionRoot(Authentication authentication, UserRepository userRepository) { super(authentication); this.userRepository = userRepository; } // 自定义校验:当前用户是否为指定ID的用户 public boolean isUser(Long userId) { Authentication auth = getAuthentication(); if (auth == null || !auth.isAuthenticated()) { return false; } User user = userRepository.findByUsername(auth.getName()); return user != null && user.getId().equals(userId); } // 自定义校验:当前用户是否为指定资源的成员 public boolean isMember(Long resourceId) { Authentication auth = getAuthentication(); if (auth == null || !auth.isAuthenticated()) { return false; } User user = userRepository.findByUsername(auth.getName()); // 根据实际业务实现成员校验逻辑 return user != null && user.getAssociatedResources().contains(resourceId); } // 实现MethodSecurityExpressionOperations接口的必要方法 @Override public void setFilterObject(Object filterObject) { this.filterObject = filterObject; } @Override public Object getFilterObject() { return filterObject; } @Override public void setReturnObject(Object returnObject) { this.returnObject = returnObject; } @Override public Object getReturnObject() { return returnObject; } @Override public Object getThis() { return this; } }
步骤2:自定义方法安全表达式处理器
继承DefaultMethodSecurityExpressionHandler,重写createSecurityExpressionRoot方法,返回我们自定义的表达式根实例:
@Component public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler { private final UserRepository userRepository; // 构造器注入Repository,Spring会自动处理依赖初始化顺序 public CustomMethodSecurityExpressionHandler(UserRepository userRepository) { this.userRepository = userRepository; } @Override protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, MethodInvocation invocation) { CustomMethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(authentication, userRepository); // 继承父类的权限评估器、角色层级等配置 root.setPermissionEvaluator(getPermissionEvaluator()); root.setTrustResolver(getTrustResolver()); root.setRoleHierarchy(getRoleHierarchy()); return root; } }
步骤3:启用方法安全并配置Security链
在Security配置类上标注@EnableMethodSecurity,Spring会自动识别并使用我们自定义的MethodSecurityExpressionHandler:
@Configuration @EnableMethodSecurity public class SecurityConfig { // 配置HttpSecurity(根据实际需求调整) @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .anyRequest().authenticated() ) .formLogin(withDefaults()); return http.build(); } }
步骤4:在业务代码中使用自定义表达式
直接在Service/Controller方法上用@PreAuthorize调用自定义的校验方法:
@Service public class UserService { @PreAuthorize("isUser(#userId)") public void updateUserProfile(Long userId, UserProfile profile) { // 执行用户信息更新逻辑 } } @RestController @RequestMapping("/resources") public class ResourceController { @PreAuthorize("isMember(#resourceId)") @GetMapping("/{resourceId}") public Resource getResource(@PathVariable Long resourceId) { // 返回指定资源 return new Resource(); } }
关键说明
- 无需静态方法:通过将
CustomMethodSecurityExpressionHandler注册为Spring Bean,并使用构造器注入依赖,Spring的Bean生命周期管理会确保Repository先于Handler初始化,避免依赖问题。 - 兼容官方推荐:完全基于
@EnableMethodSecurity实现,弃用的注解和类都不会被用到。
内容的提问来源于stack exchange,提问作者pincoin
相关产品推荐
相关产品推荐

