如何仅用JSR 250(@RolesAllowed)实现sec:authorize的URL权限校验
问题描述
Spring Security的<sec:authorize>标签可用于校验URL权限,示例如下:
<sec:authorize url="/details" var="allow_url_details"/>
该标签默认通过WebInvocationPrivilegeEvaluator进行权限评估,规则来源于HttpSecurity的配置:
@Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests(auth -> auth .requestMatchers("/details").hasAnyRole("OPERATOR","TECH") .requestMatchers("/static/*", "/*", "/favicon/*").permitAll() .anyRequest().authenticated() ); // ... }
目前已启用@EnableMethodSecurity(jsr250Enabled = true),并在控制器方法上添加了JSR 250的@RolesAllowed注解:
@Controller public class DetailController extends ControllerTemplate { @GetMapping("/details") @RolesAllowed({"OPERATOR", "TECH"}) public String list() { return "details/list"; } }
现在存在两处重复的权限配置:一是HttpSecurity中的URL权限规则,二是控制器方法上的@RolesAllowed注解。请问能否仅通过JSR 250方式定义URL权限,无需在HttpSecurity中重复配置即可让<sec:authorize>标签生效?若可以,该如何实现?
实现方案
可以实现。核心思路是替换默认的WebInvocationPrivilegeEvaluator实现,让它从控制器方法的JSR 250注解中获取权限规则,而非依赖HttpSecurity的配置。具体步骤如下:
1. 自定义WebInvocationPrivilegeEvaluator实现
这个类的作用是根据URL匹配对应的控制器方法,读取方法上的@RolesAllowed注解,并判断当前用户是否具备对应权限:
import org.springframework.security.access.SecurityMetadataSource; import org.springframework.security.access.method.MethodSecurityMetadataSource; import org.springframework.security.core.Authentication; import org.springframework.security.web.FilterInvocation; import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator; import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestWrapper; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import jakarta.servlet.http.HttpServletRequest; import java.lang.reflect.Method; public class MethodAnnotationWebInvocationPrivilegeEvaluator implements WebInvocationPrivilegeEvaluator { private final RequestMappingHandlerMapping handlerMapping; private final MethodSecurityMetadataSource methodSecurityMetadataSource; public MethodAnnotationWebInvocationPrivilegeEvaluator(RequestMappingHandlerMapping handlerMapping, MethodSecurityMetadataSource methodSecurityMetadataSource) { this.handlerMapping = handlerMapping; this.methodSecurityMetadataSource = methodSecurityMetadataSource; } @Override public boolean isAllowed(String uri, String method, Authentication authentication) { try { // 构建模拟请求,匹配对应的控制器方法 HttpServletRequest request = new SecurityContextHolderAwareRequestWrapper( new FilterInvocation(uri, method, null).getRequest(), null); // 获取URL对应的处理器方法 Method handlerMethod = handlerMapping.getHandler(request).getHandlerMethod().getMethod(); // 读取方法上的权限元数据(包含@RolesAllowed注解信息) var attributes = methodSecurityMetadataSource.getAttributes(handlerMethod, null); if (attributes == null || attributes.isEmpty()) { // 无权限注解时,默认要求用户已认证(可按需调整逻辑) return authentication.isAuthenticated(); } // 调用Spring Security的权限决策器判断用户是否有权限 org.springframework.security.access.intercept.AbstractSecurityInterceptor .getAccessDecisionManager().decide(authentication, null, attributes); return true; } catch (Exception e) { // 匹配不到方法或权限不足时返回false return false; } } @Override public boolean isAllowed(String uri, Authentication authentication) { // 默认以GET方法匹配URL return isAllowed(uri, "GET", authentication); } }
2. 注册自定义Bean并简化HttpSecurity配置
在Spring Security配置类中,注册自定义的WebInvocationPrivilegeEvaluator,同时简化HttpSecurity的权限配置(无需再单独配置URL的角色规则):
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.access.method.MethodSecurityMetadataSource; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; @Configuration @EnableMethodSecurity(jsr250Enabled = true) public class SecurityConfig { // 注册自定义的权限评估器 @Bean public WebInvocationPrivilegeEvaluator webInvocationPrivilegeEvaluator( RequestMappingHandlerMapping handlerMapping, MethodSecurityMetadataSource methodSecurityMetadataSource) { return new MethodAnnotationWebInvocationPrivilegeEvaluator(handlerMapping, methodSecurityMetadataSource); } // 简化HttpSecurity配置,仅做基础认证控制 @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .anyRequest().authenticated() // 所有请求需先认证,具体权限由方法注解控制 ) // 按需配置登录、退出等其他规则 .formLogin(form -> form.permitAll()) .logout(logout -> logout.permitAll()); return http.build(); } }
3. 注意事项
- URL匹配兼容性:如果URL包含路径变量、请求参数等复杂场景,需要调整自定义评估器中构建请求的逻辑,确保能正确匹配到对应的控制器方法。
- 静态资源处理:对于无对应控制器方法的静态资源,可在
HttpSecurity中单独配置permitAll(),或在自定义评估器中添加特殊逻辑处理。 - 权限决策逻辑:示例中直接复用了Spring Security的默认权限决策器,若有自定义权限需求,可替换为自定义的决策器。
内容的提问来源于stack exchange,提问作者Chpokeridze
相关产品推荐
相关产品推荐

