SpringSecurity自定义投票器

本文介绍如何在Spring Security中使用自定义投票器实现URL权限管理,包括配置文件指定自定义投票器及其实现类GlobalAccessDecisionVoter的详细代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

拦截URL的时候使用自定义权限判断

1:配置文件指定自定义投票器

在这里插入图片描述

在这里插入图片描述

<!-- 页面拦截规则 -->
    <http use-expressions="false" access-decision-manager-ref="accessDecisionManager">
        <!-- 下述页面拥有ROLE_USER角色 -->
        <intercept-url pattern="/index.jsp" access="ROLE_USER" />
        <intercept-url pattern="/frame/index.do" access="ROLE_USER" />
        <intercept-url pattern="/frame/loadMenu.do" access="ROLE_USER" />
        <intercept-url pattern="/system/welcome.do" access="ROLE_USER" />
        <intercept-url pattern="/**" access="URL_PERMISSION" />

        <form-login login-page="/login/login.jsp" 
                    authentication-failure-url="/login/login.jsp?error=true"
                    login-processing-url="/login.do"
                    default-target-url="/index.jsp"
                    authentication-details-source-ref="authenticationDetailsSource"
                    />
        <logout logout-url="/logout.do" logout-success-url="/login/login.jsp"/>
        <csrf disabled="true"/>
        <headers>
            <frame-options policy="SAMEORIGIN"/>
        </headers>
    </http>
    <!-- 使用自定义投票器 -->
    <beans:bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased">
        <beans:constructor-arg name="decisionVoters">
        <beans:list>  
             <beans:bean class="org.springframework.security.web.access.expression.WebExpressionVoter" />  
             <beans:bean class="common.spring.security.GlobalAccessDecisionVoter" />  
             <beans:bean class="org.springframework.security.access.vote.AuthenticatedVoter" />  
        </beans:list>
        </beans:constructor-arg>
    </beans:bean>
2:自定义投票器实现类

在这里插入图片描述

import java.util.Collection;

import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.FilterInvocation;

import cfca.org.slf4j.Logger;
import cfca.org.slf4j.LoggerFactory;

public class GlobalAccessDecisionVoter implements AccessDecisionVoter<Object> {

    private static Logger logger = LoggerFactory.getLogger(GlobalAccessDecisionVoter.class);

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }

    @Override
    public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {
        FilterInvocation fi = (FilterInvocation) object;
        String url = fi.getRequestUrl();
        logger.info("interceptor url:" + url);
        if (authentication == null) {
            return ACCESS_DENIED;
        }
        int result = ACCESS_ABSTAIN;
        Collection<? extends GrantedAuthority> authorities = extractAuthorities(authentication);
        for (ConfigAttribute attribute : attributes) {
            if (attribute.getAttribute() == null) {
                continue;
            }
            if (this.supports(attribute)) {
                result = ACCESS_DENIED;
                // Attempt to find a matching granted authority
                for (GrantedAuthority authority : authorities) {
                    if (authority instanceof GlobalGrantedAuthority) {
                        GlobalGrantedAuthority globalGrantedAuthority = (GlobalGrantedAuthority) authority;
                        GlobalPermission permission = globalGrantedAuthority.getPermission();
                        if (permission != null && url.trim().equals("/"+permission.getUrl())) {
                            return ACCESS_GRANTED;
                        }
                    } else if (attribute.getAttribute().equals(authority.getAuthority())) {
                        return ACCESS_GRANTED;
                    }
                }
            }
        }

        return result;
    }

    Collection<? extends GrantedAuthority> extractAuthorities(Authentication authentication) {
        return authentication.getAuthorities();
    }

}

说明

1:我目前登录用户都赋予了USER_ROLE角色,因此只要用户登录,具有USER_ROLE角色的URL都会请求通过。
2:其他的URL都拥有URL_PERMISSION角色,登录用户不具备此角色,因此只能判断用户的自定义权限是否可以匹配到。

<think>好的,用户想了解如何在Spring Security中实现自定义权限校验。我之前回答过类似的问题,需要参考提供的引用内容。先看看引用里的信息。 引用1提到OAuth2AuthorizationService的配置方式,有通过注册Bean和使用配置类的方法。不过这可能和权限校验关系不大,可能更多是授权服务器的配置。引用2中的MySecurityService类展示了如何通过自定义的权限服务来检查用户权限,这里用到了Authentication对象和GrantedAuthority。引用3详细说明了自定义FilterSecurityInterceptor需要配置accessDecisionManager、fisMetadataSource等组件,并提到了decide方法的作用。 用户的问题是关于自定义权限校验的,所以需要结合这些引用中的信息。可能需要以下步骤: 1. 实现自定义的权限校验逻辑,比如使用@PreAuthorize注解结合自定义的权限检查方法,如引用2中的hasPermission方法。这样可以在方法级别进行权限控制。 2. 配置自定义的安全拦截器,如引用3提到的myFilterSecurityInterceptor,需要创建并配置accessDecisionManager和securityMetadataSource。这里可能需要自定义投票器或者决策管理器,以决定是否允许访问资源。 3. 可能需要扩展GrantedAuthority,自定义权限的表示方式,比如使用特定的字符串或对象。 需要注意,用户可能需要具体的代码示例和配置步骤。需要分步骤讲解,包括如何创建自定义权限校验服务、如何集成到Spring Security的配置中,以及如何在实际应用中使用这些自定义权限。 另外,要确保回答结构清晰,步骤明确,并包含代码示例,比如自定义的AccessDecisionVoter或者使用SpEL表达式进行权限校验。还要考虑是否需要覆盖默认的权限配置,或者与现有的权限管理结合使用。 可能需要提到两种主要方式:一种是使用表达式注解(如@PreAuthorize),另一种是自定义投票器和决策管理器。这样用户可以根据需求选择合适的方法。同时,需要参考引用中的代码示例,例如如何从Authentication中获取权限,如何实现权限判断逻辑。 最后,生成相关问题,帮助用户深入理解,比如如何动态管理权限,如何处理复杂的权限逻辑等。</think>在Spring Security中实现自定义权限校验可通过以下三种方式实现,以下步骤结合动态权限管理和安全拦截器配置进行说明: ### 一、基于表达式的方法级权限控制 1. 创建权限校验服务类,继承`SecurityExpressionRoot`并实现自定义逻辑: ```java @Component("myAuth") public class CustomAuthService { public boolean checkProjectAccess(Long projectId) { // 从数据库/缓存获取权限数据 return projectService.hasAccess(SecurityUtils.getCurrentUser(), projectId); } } ``` 2. 在Security配置中启用方法级注解: ```java @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true) public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration { @Override protected MethodSecurityExpressionHandler createExpressionHandler() { DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler(); handler.setPermissionEvaluator(new CustomPermissionEvaluator()); return handler; } } ``` 3. 在Controller层使用校验: ```java @PreAuthorize("@myAuth.checkProjectAccess(#projectId)") @GetMapping("/projects/{projectId}") public Project getProject(@PathVariable Long projectId) { //... } ``` ### 二、自定义投票器实现 1. 实现`AccessDecisionVoter`接口: ```java public class CustomVoter implements AccessDecisionVoter<FilterInvocation> { @Override public boolean supports(ConfigAttribute attribute) { return attribute.getAttribute().startsWith("CUSTOM_"); } @Override public int vote(Authentication auth, FilterInvocation fi, Collection<ConfigAttribute> attributes) { String requestURI = fi.getRequest().getRequestURI(); return checkAccess(auth, requestURI) ? ACCESS_GRANTED : ACCESS_DENIED; } } ``` 2. 配置决策管理器: ```java @Bean public AccessDecisionManager accessDecisionManager() { List<AccessDecisionVoter<?>> voters = new ArrayList<>(); voters.add(new WebExpressionVoter()); voters.add(new CustomVoter()); return new UnanimousBased(voters); } ``` ### 三、动态元数据源配置 1. 实现`FilterInvocationSecurityMetadataSource`: ```java public class DynamicMetadataSource implements FilterInvocationSecurityMetadataSource { @Override public Collection<ConfigAttribute> getAttributes(Object object) { FilterInvocation fi = (FilterInvocation) object; String url = fi.getRequestUrl(); // 从数据库动态加载权限配置 return loadAttributesFromDB(url); } } ``` 2. 配置安全拦截器: ```java @Bean public FilterSecurityInterceptor customFilter() throws Exception { FilterSecurityInterceptor interceptor = new FilterSecurityInterceptor(); interceptor.setAccessDecisionManager(accessDecisionManager()); interceptor.setSecurityMetadataSource(metadataSource()); interceptor.setAuthenticationManager(authenticationManager); return interceptor; } ``` ### 四、权限上下文集成 在JWT认证场景中,可在`JwtAuthenticationFilter`解析时注入权限: ```java public class JwtFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) { String token = extractToken(request); Claims claims = parseToken(token); List<GrantedAuthority> authorities = convertClaimsToAuthorities(claims); Authentication auth = new JwtAuthenticationToken(claims.getSubject(), authorities); SecurityContextHolder.getContext().setAuthentication(auth); } } ``` ### 性能优化建议 1. 权限缓存:使用`@Cacheable`注解缓存权限数据 ```java @Cacheable(value = "userPermissions", key = "#userId") public Set<String> getUserPermissions(Long userId) { // 数据库查询 } ``` 2. 批量查询:在`loadAttributesFromDB`方法中使用IN语句批量获取URL权限配置
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值