如何在GraphQL Spring中动态限制同返回类型方法的字段
实现同一对象类型在不同GraphQL查询中的字段权限控制
针对你提出的需求——让getAllUsers禁止查询UserDto.birthday,getUser无限制,结合你使用的graphql-spqr-spring-boot-starter 0.0.7和Spring Boot 2.7.6,有几种可行的实现方式:
1. 字段级注解结合SpEL表达式控制可见性
直接在UserDto的birthday字段上使用SPQR的@GraphQLField注解,通过SpEL表达式判断当前执行的查询名称或用户权限,动态控制字段是否可见:
public class UserDto { // 其他字段... @GraphQLField(condition = "!#context.executionInfo.field.name.equals('getAllUsers')") private LocalDate birthday; // getter、setter... }
或者结合权限判断(利用已有的@PreAuthorize权限体系):
@GraphQLField(condition = "#securityContext.authentication.authorities.any { it.authority == 'userRead' }") private LocalDate birthday;
核心是借助SPQR支持的SpEL上下文,通过#context获取当前查询的执行信息,或#securityContext获取用户权限,来决定字段是否暴露。
2. 拆分DTO类型
创建两个DTO类,一个包含所有字段,一个仅保留getAllUsers允许返回的字段,让两个查询分别返回不同的DTO:
// 基础用户DTO,不含birthday字段 public class BaseUserDto { private Long id; private String username; private String email; // getter、setter... } // 完整用户DTO,继承BaseUserDto并添加birthday字段 public class FullUserDto extends BaseUserDto { private LocalDate birthday; // getter、setter... }
修改查询方法的返回类型:
@GraphQLQuery(name = "getUser") @PreAuthorize("hasAuthority('userRead')") public FullUserDto getUser(@GraphQLArgument(name = "id") Long id) { // 业务逻辑:将User转换为FullUserDto返回 } @GraphQLQuery(name = "getAllUsers") @PreAuthorize("hasAuthority('userSelect')") public List<BaseUserDto> getAllUsers() { // 业务逻辑:将User集合转换为BaseUserDto集合返回 }
这种方式类型边界清晰,避免动态判断的复杂性,适合字段权限区分明确的场景。
3. 自定义DataFetcher拦截器
通过自定义拦截器,在查询结果返回前过滤掉指定字段:
首先创建拦截器类:
@Component public class FieldFilterInterceptor implements DataFetcherInterceptor { @Override public Object intercept(DataFetchingEnvironment env, DataFetcher<?> next) { Object result = next.get(env); // 判断当前查询是否为getAllUsers if ("getAllUsers".equals(env.getFieldDefinition().getName())) { if (result instanceof List) { ((List<?>) result).forEach(this::removeBirthdayField); } } return result; } private void removeBirthdayField(Object obj) { if (obj instanceof UserDto) { ((UserDto) obj).setBirthday(null); // 也可通过反射直接移除字段值,根据实际场景选择 } } }
然后将拦截器注册到SPQR配置中:
@Configuration public class GraphQLConfig { @Autowired private FieldFilterInterceptor fieldFilterInterceptor; @Bean public GraphQLSchema schema(GraphQLSchemaGenerator generator) { generator.withInterceptors(fieldFilterInterceptor); return generator.generate(); } }
这种方式适合需要动态调整字段过滤规则的场景,无需修改DTO或查询方法的返回类型。
内容的提问来源于stack exchange,提问作者StarkSlasher
相关产品推荐
相关产品推荐

