Spring Boot GraphQL:如何在控制器方法执行前预处理单个查询/变更?
在Spring GraphQL中实现单Query/Mutation级别的权限校验
针对你要给控制器方法加注解、单独校验每个GraphQL操作(而非整个HTTP请求)的需求,Spring GraphQL提供了两种直接的实现方式:
方案1:用Spring Security原生GraphQL支持(推荐)
Spring Security对GraphQL做了专门适配,直接用@PreAuthorize注解在控制器的query/mutation方法上就能实现单操作级别的权限校验——哪怕一个HTTP请求里塞了多个操作,每个都会单独触发检查。
示例代码:
@Controller public class BookController { @QueryMapping @PreAuthorize("hasAuthority('BOOK_READ')") public Book getBook(@Argument Long id) { // 业务逻辑 } @MutationMapping @PreAuthorize("hasAuthority('BOOK_WRITE')") public Book createBook(@Argument BookInput input) { // 业务逻辑 } }
要启用这个功能,确保项目引入spring-boot-starter-graphql和spring-boot-starter-security依赖,然后配置Spring Security支持GraphQL:
@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth.anyRequest().authenticated()) .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt) // 开启GraphQL权限校验支持 .graphql(graphql -> graphql.authorizeRequests(auth -> auth .anyRequest().authenticated() )); return http.build(); } }
方案2:自定义DataFetcher拦截器
如果需要完全自定义校验逻辑(比如自己搞注解规则),可以用Spring GraphQL的DataFetcherInterceptor——它专门用来拦截单个DataFetcher(也就是控制器里的每个query/mutation方法)的执行流程。
步骤如下:
- 定义自定义权限注解:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RequirePermission { String value(); // 传入权限标识,比如"BOOK_READ" }
- 实现拦截器,在方法执行前检查注解:
@Component public class PermissionInterceptor implements DataFetcherInterceptor { @Override public CompletionStage<Object> intercept(DataFetcherInvocation invocation) { // 获取当前执行的控制器方法 Method targetMethod = getTargetMethod(invocation); if (targetMethod == null) { return invocation.proceed(); } // 检查是否存在自定义权限注解 if (targetMethod.isAnnotationPresent(RequirePermission.class)) { RequirePermission annotation = targetMethod.getAnnotation(RequirePermission.class); String requiredPerm = annotation.value(); // 校验当前用户权限 Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth == null || !auth.getAuthorities().stream() .anyMatch(a -> a.getAuthority().equals(requiredPerm))) { throw new AccessDeniedException("无权限执行此操作"); } } // 继续执行原方法 return invocation.proceed(); } // 辅助方法:从invocation中解析出对应的控制器方法 private Method getTargetMethod(DataFetcherInvocation invocation) { try { return invocation.getTarget().getClass().getMethod( invocation.getDefinition().getName(), invocation.getArguments().values().stream() .map(Object::getClass) .toArray(Class[]::new) ); } catch (NoSuchMethodException e) { return null; } } }
- 在控制器方法上使用自定义注解:
@Controller public class BookController { @QueryMapping @RequirePermission("BOOK_READ") public Book getBook(@Argument Long id) { // 业务逻辑 } }
核心原理
Spring GraphQL里,每个query/mutation对应的控制器方法本质是一个DataFetcher,所以针对DataFetcher的拦截就是精准作用于单个GraphQL操作,和HTTP请求层面的拦截完全独立,正好满足你的需求。
内容的提问来源于stack exchange,提问作者Marsh
相关产品推荐
相关产品推荐

