Spring Security 6中如何自定义expressionHandler与accessDecisionManager
Spring Boot 3.x/Spring Security 6.x 配置自定义ExpressionHandler与AccessDecisionManager
在Spring Security 6.x中,WebSecurityConfigurerAdapter被废弃,转而使用SecurityFilterChain bean配置安全规则。原有的ExpressionInterceptUrlRegistry设置expressionHandler和accessDecisionManager的方式不再适用,以下是两种可行的配置方案:
方案一:替换默认的FilterSecurityInterceptor(贴近原有配置逻辑)
FilterSecurityInterceptor是Spring Security中负责最终授权决策的核心过滤器,我们可以自定义该过滤器的实例,注入自己的expressionHandler和accessDecisionManager,然后替换掉默认的过滤器。
1. 自定义WebSecurityExpressionHandler
@Component public class CustomWebSecurityExpressionHandler extends DefaultWebSecurityExpressionHandler { @Override protected SecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, FilterInvocation fi) { WebSecurityExpressionRoot root = (WebSecurityExpressionRoot) super.createSecurityExpressionRoot(authentication, fi); // 这里添加自定义逻辑,比如设置自定义PermissionEvaluator root.setPermissionEvaluator(new CustomPermissionEvaluator()); return root; } }
2. 自定义AccessDecisionManager
@Component public class CustomAccessDecisionManager extends AffirmativeBased { public CustomAccessDecisionManager(List<AccessDecisionVoter<?>> decisionVoters) { super(decisionVoters); // 自定义配置,例如设置是否允许所有投票者弃权时通过 setAllowIfAllAbstainDecisions(false); } }
3. 配置SecurityFilterChain并替换FilterSecurityInterceptor
@Configuration @EnableWebSecurity public class SecurityConfig { private final AuthenticationManager authenticationManager; private final CustomWebSecurityExpressionHandler expressionHandler; private final CustomAccessDecisionManager accessDecisionManager; // 构造注入依赖 public SecurityConfig(AuthenticationManager authenticationManager, CustomWebSecurityExpressionHandler expressionHandler, CustomAccessDecisionManager accessDecisionManager) { this.authenticationManager = authenticationManager; this.expressionHandler = expressionHandler; this.accessDecisionManager = accessDecisionManager; } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { // 定义URL授权规则:RequestMatcher对应权限表达式 LinkedHashMap<RequestMatcher, Collection<String>> authRules = new LinkedHashMap<>(); authRules.put(new AntPathRequestMatcher("/admin/**"), Collections.singletonList("hasRole('ADMIN')")); authRules.put(new AntPathRequestMatcher("/user/**"), Collections.singletonList("hasRole('USER')")); authRules.put(new AntPathRequestMatcher("/**"), Collections.singletonList("authenticated")); // 创建安全元数据源,绑定自定义expressionHandler DefaultFilterInvocationSecurityMetadataSource metadataSource = new DefaultFilterInvocationSecurityMetadataSource(authRules, expressionHandler); // 配置自定义FilterSecurityInterceptor FilterSecurityInterceptor customFilter = new FilterSecurityInterceptor(); customFilter.setAuthenticationManager(authenticationManager); customFilter.setAccessDecisionManager(accessDecisionManager); customFilter.setSecurityMetadataSource(metadataSource); // 替换默认的FilterSecurityInterceptor http .authorizeHttpRequests(auth -> auth.anyRequest().authenticated()) .addFilterBefore(customFilter, FilterSecurityInterceptor.class); return http.build(); } // 提供WebExpressionVoter给自定义AccessDecisionManager(用于表达式投票) @Bean public WebExpressionVoter webExpressionVoter() { WebExpressionVoter voter = new WebExpressionVoter(); voter.setExpressionHandler(expressionHandler); return voter; } }
方案二:使用ExpressionAuthorizationManager(符合新授权模型)
Spring Security 6.x推荐使用AuthorizationManager接口处理授权逻辑,我们可以通过ExpressionAuthorizationManager整合自定义的expressionHandler和accessDecisionManager。
1. 自定义组件同方案一
复用方案一中的CustomWebSecurityExpressionHandler和CustomAccessDecisionManager。
2. 配置SecurityFilterChain
@Configuration @EnableWebSecurity public class SecurityConfig { private final CustomWebSecurityExpressionHandler expressionHandler; private final CustomAccessDecisionManager accessDecisionManager; public SecurityConfig(CustomWebSecurityExpressionHandler expressionHandler, CustomAccessDecisionManager accessDecisionManager) { this.expressionHandler = expressionHandler; this.accessDecisionManager = accessDecisionManager; } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { // 构建自定义表达式授权管理器 AuthorizationManager<RequestAuthorizationContext> customAuthManager = ExpressionAuthorizationManager.authenticated() .expressionHandler(expressionHandler) .accessDecisionManager(accessDecisionManager); http .authorizeHttpRequests(auth -> auth .requestMatchers("/admin/**").access(customAuthManager) .requestMatchers("/user/**").access("hasRole('USER')") // 该表达式会使用全局配置的expressionHandler .anyRequest().authenticated() ) // 全局设置表达式处理器,确保所有表达式授权都使用自定义实现 .securityContext(context -> context.expressionHandler(expressionHandler)); return http.build(); } @Bean public WebExpressionVoter webExpressionVoter() { WebExpressionVoter voter = new WebExpressionVoter(); voter.setExpressionHandler(expressionHandler); return voter; } }
两种方案各有优劣:方案一更贴近旧版配置逻辑,适合需要完全复用原有授权规则的场景;方案二更符合Spring Security 6.x的新设计理念,代码更简洁。
内容的提问来源于stack exchange,提问作者codependent
相关产品推荐
相关产品推荐

