You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在GraphQL Spring Boot中如何获取查询选中字段的所有指令?

问题:GraphQL @auth指令如何校验用户请求字段的权限

指令定义

directive @auth(role: [String!]!) on FIELD_DEFINITION

示例Schema

type Query {
    test: TestResultType! @auth(role: ["USER", "ADMIN"])
}

type TestResultType {
    customer: Customer!
    seller: Seller!
}

type Customer {
    email: String!
    username: String!
    password: String! @auth(role: "ADMIN")
}

type Seller {
    brandName: String!
    email: String!
    username: String!
    password: String! @auth(role: "ADMIN")
}

需求说明

当用户权限为USER而非ADMIN时,未请求password字段的查询可正常执行;若请求password字段,查询应返回权限错误。

已实现的授权指令代码

public class AuthorizationDirective implements SchemaDirectiveWiring {

    @Override
    public GraphQLFieldDefinition onField(
            SchemaDirectiveWiringEnvironment<GraphQLFieldDefinition> wiringEnv) {
        // Get current data fetcher
        GraphQLFieldsContainer fieldsContainer = wiringEnv.getFieldsContainer();
        GraphQLFieldDefinition fieldDefinition = wiringEnv.getFieldDefinition();
        final DataFetcher<?> currentDataFetcher = wiringEnv
                .getCodeRegistry()
                .getDataFetcher(fieldsContainer, fieldDefinition);

        // Apply data fetcher with authorization logic
        final DataFetcher<?> authorizingDataFetcher = buildAuthorizingDataFetcher(
                wiringEnv,
                currentDataFetcher);
        wiringEnv.getCodeRegistry()
                .dataFetcher(
                        fieldsContainer,
                        fieldDefinition,
                        authorizingDataFetcher);

        return fieldDefinition;
    }

    private DataFetcher<Object> buildAuthorizingDataFetcher(
            SchemaDirectiveWiringEnvironment<GraphQLFieldDefinition> wiringEnv,
            DataFetcher<?> currentDataFetcher) {
        return fetchingEnv -> {
            // Implementation here
        };
    }
}

困惑点

  • 如何在buildAuthorizingDataFetcher方法中提取用户选中的字段及其@auth指令信息?
  • 已尝试通过fetchingEnv.getSelection().getFields()获取选中字段,但返回的SelectedField对象不包含指令信息,该如何解决?

解决方案

1. 关联SelectedField与Schema中的字段定义

SelectedField本身不存储指令数据,但可以通过它的getFieldDefinition()方法获取对应的GraphQLFieldDefinition,后者包含该字段上定义的所有指令信息。

2. 递归遍历选中字段并校验权限

在buildAuthorizingDataFetcher中,递归遍历查询的选中字段树,对每个字段检查是否存在@auth指令,再对比当前用户角色是否符合要求。

修改后的buildAuthorizingDataFetcher实现示例:

private DataFetcher<Object> buildAuthorizingDataFetcher(
        SchemaDirectiveWiringEnvironment<GraphQLFieldDefinition> wiringEnv,
        DataFetcher<?> currentDataFetcher) {
    return fetchingEnv -> {
        // 获取当前用户角色(替换为你的实际认证逻辑)
        Set<String> userRoles = getCurrentUserRoles(fetchingEnv);
        
        // 校验当前选中字段及子字段的权限
        checkSelectedFieldsAuthorization(fetchingEnv.getSelectionSet(), userRoles);
        
        // 执行原数据获取逻辑
        return currentDataFetcher.get(fetchingEnv);
    };
}

private void checkSelectedFieldsAuthorization(SelectionSet selectionSet, Set<String> userRoles) {
    for (Selection selection : selectionSet.getSelections()) {
        if (selection instanceof FieldSelection) {
            FieldSelection fieldSelection = (FieldSelection) selection;
            SelectedField selectedField = fieldSelection.getField();
            GraphQLFieldDefinition fieldDef = selectedField.getFieldDefinition();
            
            // 检查字段是否带有@auth指令
            Directive authDirective = fieldDef.getDirective("auth");
            if (authDirective != null) {
                List<String> requiredRoles = authDirective.getArgument("role").getValue(List.class);
                // 校验用户是否拥有至少一个所需角色
                boolean hasPermission = requiredRoles.stream().anyMatch(userRoles::contains);
                if (!hasPermission) {
                    throw new GraphQLException(String.format("无权访问字段: %s", selectedField.getName()));
                }
            }
            
            // 递归校验子字段权限
            if (selectedField.getSelectionSet() != null) {
                checkSelectedFieldsAuthorization(selectedField.getSelectionSet(), userRoles);
            }
        }
    }
}

// 示例:从上下文获取用户角色的方法
private Set<String> getCurrentUserRoles(DataFetchingEnvironment env) {
    // 替换为实际获取角色逻辑,比如从SecurityContextHolder或请求上下文
    return Set.of("USER");
}

3. 注意事项

  • 需要自定义异常类(如GraphQLException)并配置GraphQL异常处理器,将权限错误转为标准的GraphQL响应格式。
  • 根字段(如Query中的test)的权限校验可直接在当前指令逻辑中处理,无需在子字段遍历中重复校验。
  • 递归遍历需覆盖所有嵌套字段(如customer.password),确保所有选中字段都经过权限校验。

内容的提问来源于stack exchange,提问作者the thinker

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 11:27:33