能否复用RestController/RequestMapping实现HTTP请求授权?
借助RestController类实现Spring Security授权控制(复用RequestMapping配置)
完全可以实现类似Spring Boot Actuator中EndpointRequest的效果,通过RestController类直接配置授权规则,无需重复编写路径字符串。Spring Security本身没有提供现成的API,但可以通过解析Controller的@RequestMapping注解自定义请求匹配器,复用已有配置。
实现步骤与代码示例
1. 自定义Controller请求匹配器
创建一个RequestMatcher实现类,负责解析Controller类和方法上的@RequestMapping(及其派生注解如@GetMapping),生成对应的路径和请求方法匹配规则:
import org.springframework.core.annotation.AnnotationUtils; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import jakarta.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.List; import java.util.Set; public class ControllerRequestMatcher implements RequestMatcher { private final List<RequestMatcher> matchers = new ArrayList<>(); public ControllerRequestMatcher(Class<?>... controllerClasses) { for (Class<?> controllerClass : controllerClasses) { // 解析类级@RequestMapping RequestMapping classMapping = AnnotationUtils.findAnnotation(controllerClass, RequestMapping.class); if (classMapping == null) continue; String[] basePaths = classMapping.value(); RequestMethod[] allowedMethods = classMapping.method().length > 0 ? classMapping.method() : RequestMethod.values(); // 解析方法级@RequestMapping(含派生注解) Set<RequestMapping> methodMappings = AnnotationUtils.findRepeatableAnnotations(controllerClass, RequestMapping.class); if (methodMappings.isEmpty()) { // 无方法级注解时,直接使用类级路径和方法 for (String basePath : basePaths) { for (RequestMethod method : allowedMethods) { matchers.add(new AntPathRequestMatcher(basePath, method.name())); } } continue; } // 组合类和方法的路径与请求方法 for (String basePath : basePaths) { for (RequestMapping methodMapping : methodMappings) { String[] methodPaths = methodMapping.value(); RequestMethod[] methodSpecificMethods = methodMapping.method().length > 0 ? methodMapping.method() : allowedMethods; for (String methodPath : methodPaths) { String fullPath = combinePaths(basePath, methodPath); for (RequestMethod method : methodSpecificMethods) { matchers.add(new AntPathRequestMatcher(fullPath, method.name())); } } } } } } // 拼接类和方法的路径,处理斜杠重复问题 private String combinePaths(String basePath, String methodPath) { if (basePath.endsWith("/") && methodPath.startsWith("/")) { return basePath + methodPath.substring(1); } if (!basePath.endsWith("/") && !methodPath.startsWith("/")) { return basePath + "/" + methodPath; } return basePath + methodPath; } @Override public boolean matches(HttpServletRequest request) { return matchers.stream().anyMatch(matcher -> matcher.matches(request)); } }
2. 封装工具类简化调用
模仿Actuator的EndpointRequest,封装静态方法让配置更简洁:
public class ControllerRequest { public static ControllerRequestMatcher to(Class<?>... controllerClasses) { return new ControllerRequestMatcher(controllerClasses); } }
3. 在Security配置中使用
直接通过Controller类配置授权规则,无需编写路径字符串:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.web.SecurityFilterChain; @Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests(auth -> auth // 授权指定Controller的所有请求无需认证 .requestMatchers(ControllerRequest.to(MyPublicRestController.class)) .permitAll() // 其他请求需要认证 .anyRequest() .authenticated() ); return http.build(); } }
关键特性说明
- 自动解析
@RequestMapping及其派生注解(@GetMapping、@PostMapping等),覆盖所有请求方法和路径配置 - 支持类级和方法级注解的路径组合,自动处理斜杠重复问题
- 复用Controller的现有配置,避免路径字符串重复编写,降低维护成本
- 匹配规则与
AntPathMatcher完全兼容,支持路径变量、通配符等语法
内容的提问来源于stack exchange,提问作者MelleD
相关产品推荐
相关产品推荐

