You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于自定义条件的访问授权:AccessDecisionVoter适用性及实现示例

嘿,完全理解你的需求!AccessDecisionVoter确实是Spring Security里处理这类自定义访问控制的绝佳方案——它刚好能让你脱离单纯的角色/权限校验,基于任意自定义条件来决定是否允许用户访问某个资源,完全适配你的场景。

为什么AccessDecisionVoter是合适的选择?

Spring Security的访问决策体系中,AccessDecisionVoter就是用来处理自定义投票逻辑的核心组件。它可以和AccessDecisionManager配合,针对特定资源(比如你的/getAllOrders URL),根据你定义的任意条件(而非固定角色/权限)来决定是否放行。相比注解式的权限控制(比如@PreAuthorize),它更适合全局URL级别的统一控制,不用在每个Controller方法上重复配置。

可运行的代码示例

下面是完整的实现步骤,你可以直接基于自己的项目调整:

1. 创建自定义投票器(CustomOrderAccessVoter)

这个类负责实现你的核心条件判断逻辑:

import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.FilterInvocation;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalTime;
import java.util.Collection;

@Component
public class CustomOrderAccessVoter implements AccessDecisionVoter<Object> {

    // 目标拦截URL
    private static final String TARGET_ORDERS_URL = "/getAllOrders";

    @Override
    public boolean supports(ConfigAttribute attribute) {
        // 支持所有配置属性,你也可以在这里过滤特定的权限标识
        return true;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        // 处理HTTP请求的FilterInvocation类型
        return FilterInvocation.class.isAssignableFrom(clazz);
    }

    @Override
    public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {
        FilterInvocation filterInvocation = (FilterInvocation) object;
        HttpServletRequest request = filterInvocation.getRequest();
        String requestUri = request.getRequestURI();

        // 只对目标URL做校验,其他请求投弃权票
        if (!TARGET_ORDERS_URL.equals(requestUri)) {
            return ACCESS_ABSTAIN;
        }

        // --------------------------
        // 这里替换成你的自定义条件逻辑
        // 示例条件:1. 用户账户处于活跃状态;2. 当前时间在工作时段(9:00-18:00)
        // --------------------------
        // 1. 获取当前登录用户的自定义信息(假设你有CustomUserDetails实现了UserDetails)
        CustomUserDetails currentUser = (CustomUserDetails) authentication.getPrincipal();
        boolean isUserActive = currentUser.isActive();

        // 2. 判断当前时间是否在工作时段
        LocalTime now = LocalTime.now();
        boolean isInWorkingHours = now.isAfter(LocalTime.of(9, 0)) && now.isBefore(LocalTime.of(18, 0));

        // 满足所有条件则允许访问
        if (isUserActive && isInWorkingHours) {
            return ACCESS_GRANTED;
        }

        // 不满足条件则拒绝访问
        return ACCESS_DENIED;
    }
}

2. 配置Spring Security,将投票器加入访问决策链

接下来需要在Security配置中把这个投票器注册到访问决策管理器里,让它生效:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import java.util.List;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final CustomOrderAccessVoter customOrderAccessVoter;

    // 构造注入自定义投票器
    public SecurityConfig(CustomOrderAccessVoter customOrderAccessVoter) {
        this.customOrderAccessVoter = customOrderAccessVoter;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(auth -> auth
                        // 对目标URL应用自定义访问决策逻辑
                        .requestMatchers("/getAllOrders").access(accessConfig -> accessConfig
                                .decisionManager(accessDecisionManager())
                        )
                        // 其他URL的权限配置,根据你的需求调整
                        .anyRequest().authenticated()
                )
                // 这里可以添加你的登录配置(比如表单登录、JWT等)
                .formLogin(form -> form.permitAll());

        return http.build();
    }

    @Bean
    public AccessDecisionManager accessDecisionManager() {
        // 使用AffirmativeBased策略:只要有一个投票器投同意,就允许访问
        // 你也可以根据需求选择ConsensusBased(多数同意)或UnanimousBased(全票同意)
        List<AccessDecisionVoter<?>> voters = List.of(customOrderAccessVoter);
        return new AffirmativeBased(voters);
    }
}

3. 补充说明

  • CustomUserDetails是你需要自己实现的UserDetails子类,用来封装用户的自定义属性(比如示例中的isActive()方法),你需要根据自己的用户实体来调整这部分代码。
  • 你可以完全替换vote方法里的条件逻辑,比如判断用户所在部门、是否有特定标签、甚至请求中的参数值等等,只要是能通过Authentication或HttpServletRequest获取到的信息,都可以作为判断条件。
  • 如果需要多个投票器共同生效,只需要把它们都加入到accessDecisionManager()的voters列表中即可。

内容的提问来源于stack exchange,提问作者Kathiresa J

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 08:53:54