如何在Spring Security 4.0.4中实现可获取URL路径变量的AccessDecisionVoter
Spring Security 4.0.4 版本未内置 4.2 版本新增的路径变量权限表达式原生支持,你找到的 3.1 版本基于 AccessDecisionVoter 的实现方案完全兼容 4.0.4 版本,也是该版本下的最优实现方案,具体实现步骤如下:
实现逻辑说明
Spring Security 中AccessDecisionVoter的vote方法入参的实际类型为FilterInvocation,可从中拿到当前请求的完整路径,再通过 Spring 自带的UriTemplate工具即可解析出路径变量,后续执行自定义权限校验逻辑即可。
代码实现
1. 自定义路径变量感知的投票器
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.web.util.UriTemplate; import java.util.Collection; import java.util.Map; public class PathVariableAccessVoter implements AccessDecisionVoter<FilterInvocation> { // 自定义权限标识前缀,按需调整 private static final String PERMISSION_PREFIX = "PERM_RESOURCE_"; @Override public boolean supports(ConfigAttribute attribute) { return attribute.getAttribute() != null && attribute.getAttribute().startsWith(PERMISSION_PREFIX); } @Override public boolean supports(Class<?> clazz) { return FilterInvocation.class.isAssignableFrom(clazz); } @Override public int vote(Authentication authentication, FilterInvocation filterInvocation, Collection<ConfigAttribute> attributes) { // 匹配路径获取变量,示例路径为 /resource/{resourceId},按需替换为你的业务路径 UriTemplate uriTemplate = new UriTemplate("/resource/{resourceId}"); Map<String, String> pathVariables = uriTemplate.match(filterInvocation.getRequestUrl()); String resourceId = pathVariables.get("resourceId"); // 自定义权限校验逻辑,比如校验当前用户是否有该资源的访问权限 if (hasPermission(authentication, resourceId)) { return ACCESS_GRANTED; } return ACCESS_DENIED; } private boolean hasPermission(Authentication authentication, String resourceId) { // 此处替换为你的业务权限校验逻辑 return true; } }
2. 配置投票器到权限决策管理器
XML 配置方式
<security:http access-decision-manager="customAccessDecisionManager"> <!-- 其他HTTP配置 --> <security:intercept-url pattern="/resource/**" access="PERM_RESOURCE_ACCESS"/> </security:http> <bean id="customAccessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased"> <constructor-arg> <list> <!-- 注入自定义投票器 --> <bean class="com.yourpackage.PathVariableAccessVoter"/> <!-- 保留默认投票器 --> <bean class="org.springframework.security.access.vote.RoleVoter"/> <bean class="org.springframework.security.access.vote.AuthenticatedVoter"/> </list> </constructor-arg> </bean>
JavaConfig 配置方式
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.access.AccessDecisionManager; import org.springframework.security.access.vote.AffirmativeBased; import org.springframework.security.access.vote.AuthenticatedVoter; import org.springframework.security.access.vote.RoleVoter; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import java.util.ArrayList; import java.util.List; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/resource/**").access("PERM_RESOURCE_ACCESS") .anyRequest().authenticated() .and() .accessDecisionManager(accessDecisionManager()); } @Bean public AccessDecisionManager accessDecisionManager() { List<AccessDecisionVoter<?>> voterList = new ArrayList<>(); voterList.add(new PathVariableAccessVoter()); voterList.add(new RoleVoter()); voterList.add(new AuthenticatedVoter()); return new AffirmativeBased(voterList); } }
多路径适配优化
如果需要支持多个不同的路径规则,可在自定义投票器中维护一个UriTemplate列表,遍历匹配当前请求路径,拿到对应路径变量后再执行校验即可。
内容的提问来源于stack exchange,提问作者Delsh
相关产品推荐
相关产品推荐

