如何获取Query / Mutation操作名称 [英] How to get Query/Mutation operation name

查看:131
本文介绍了如何获取Query / Mutation操作名称的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是Spring boot + GraphQL的新手。
我需要在我的控制器类中获取查询/变异操作名称。

I'm new to Spring boot + GraphQL. I need to get the Query/Mutation operation name inside my controller class.

目的:需要对某些用户的大权限特定的突变/查询操作。
这里用户类型将作为请求标头传递,并将被验证并检查是否允许用户访问该操作。

Purpose : Need to grand permission to some users to specific mutation/Query operations. Here the user type will be passed as a request header and will be validated and check whether the user is allowed to access that operation.

@PostMapping
public ResponseEntity<Object> callGraphQLService(@RequestBody String query, @RequestHeader("user") String userName) {
    ExecutionResult result = graphService.getGraphQL().execute(ExecutionInput.newExecutionInput()
            .query(query)
            .context(userName)
            .build());
    return new ResponseEntity<>(result, HttpStatus.OK);
}




建议任何有效机制来执行特定授权查询/变异

Suggest any efficient mechanism to perform authorization for specific Query/Mutation


推荐答案

我认为你在这里考虑REST术语的授权,以及它没有很好地映射到GraphQL。您需要更精细的方法,而不是基于操作名称(或基于REST中的URL)在顶层进行单一决策。您需要知道允许哪些人在场外级别查看/执行 ,因为客户端可以进行临时选择。

I think you're thinking of authorization in REST terms here, and it doesn't map well to GraphQL. Instead of a single decision at the top level based on the operation name (or based on the URL in REST), you need a more granular approach. You need to know who's allowed to see/do what at the field level, as the client is allowed make ad-hoc selections.

这里有有多种方法可以做到这一点,但既然你提到了Spring,你可以简单地在服务级别使用Spring Security。如果每个受保护的字段都由服务方法支持(它应该是),您可以像往常一样使用Spring Security保护这些方法。

There's multiple ways to do this, but since you mentioned Spring, you can simply use Spring Security at the service level. If each protected field is backed by a service method (and it should be), you can protect those methods using Spring Security as usual.

更好的是,您还应该提供自定义 GraphqlFieldVisibility 实现,以便未经授权的客户端甚至无法知道模式中不允许看到的存在字段。你可以用例如Spring的 SpelExpressionParser 根据Spring Security规则为每个用户动态显示模式的哪些部分做出决定。

Even better, you should also provide a custom GraphqlFieldVisibility implementation, so that unauthorized clients can't even know about the the existence of fields they're not allowed to see in the schema. You can use e.g. Spring's SpelExpressionParser to make decisions on what parts of the schema are visible dynamically, for each user, based on Spring Security rules.

如果Spring Security不是一个选项,您可以实现自定义 Instrumentation (例如,通过扩展 SimpleInstrumentation )。在那里,您可以实现回调,如 beginExecuteOperation ,这将使您可以访问已解析的查询(如果您真的只想进行REST风格的顶级身份验证,那就足够了),或开始(延期)字段(这使您可以访问 FieldDefinition )或 beginFieldFetch / instrumentDataFetcher (允许您访问整个 DataFetchingEnvironment )以执行每个字段的身份验证。

If Spring Security is not an option, you can implement a custom Instrumentation (e.g. by extending SimpleInstrumentation). There you can implement the callbacks like beginExecuteOperation, that will give you access to the parsed query (enough if you really just want to do REST-style top-level auth only), or begin(Deferred)Field (which gives you access to the FieldDefinition) or beginFieldFetch/instrumentDataFetcher (which gives you access to the entire DataFetchingEnvironment) to perform auth per-field.

如果你这样做,你可以将字段定义本身的auth信息(例如所需的角色)保存为指令。并将当前登录的用户保留在共享上下文中。这样,您始终拥有在每个级别进行身份验证所需的一切。

If you go this way, you can keep the auth information (e.g. the required roles) in the field definition itself as directives. And keep the currently logged in user in the shared context. This way you always have everything you need to do authentication at each level.

在所有情况下,建议提供 GraphqlFieldVisibility 在上下文中完全隐藏受保护字段的存在。

In all cases, it's advisable to provide GraphqlFieldVisibility to completely hide the existence of the protected fields contextually.

这是一个抽象示例,显示使用 Instrumentation的主要点方法(因为你对Spring安全方法没有什么特别之处,只需像往常一样使用Spring Security):

Here's an abstract example showing the major points using the Instrumentation approach (as you need nothing special for the Spring Security approach, just use Spring Security as usual):

//Checks if the current user has the needed roles for each field
public class AuthInstrumentation extends SimpleInstrumentation {
    @Override
    public DataFetcher<?> instrumentDataFetcher(DataFetcher<?> dataFetcher, InstrumentationFieldFetchParameters parameters) {
        GraphQLFieldDefinition fieldDefinition = parameters.getEnvironment().getFieldDefinition();
        //Each protected field is expected to have a directive called "auth" with an argument called "rolesRequired" that is a list of strings representing the roles
        Optional<GraphQLArgument> rolesRequired = DirectivesUtil.directiveWithArg(fieldDefinition.getDirectives(), "auth", "rolesRequired");
        if (rolesRequired.isPresent()) {
            List<String> roles = (List<String>) rolesRequired.get().getValue();
            User currentUser = parameters.getEnvironment().getContext(); //get the user from context
            if (!currentUser.getRoles().containsAll(roles)) {
                //Replace the normal resolution logic with the one that always returns null (or throws an exception) when the user doesn't have access
                return env -> null;
            }
        }
        return super.instrumentDataFetcher(dataFetcher, parameters);
    }
}

您不必存储所需的角色指令,它只是一个方便的地方。如果合适,您可以从外部来源获得相同的信息。

You don't have to store the required roles in the directives, it's just a convenient place. You can get the same info from an external source if it's appropriate.

然后注册此工具:

GraphQL graphQL = GraphQL.newGraphQL(schema)
    .instrumentation(new AuthInstrumentation())
    .build();

执行查询时,将当前用户置于上下文中:

And when executing a query, put the current user into the context:

//Get the current user's roles however you normally do
User user = loadUser(userName);
ExecutionInput input = ExecutionInput.newExecutionInput()
    .query(operation)
    .context(user) //put the user into context so the instrumentation can get it
    .build()

这样你就可以整齐地分开一切(解析器中没有auth逻辑,不需要外部上下文)和每个字段的上下文,即使不使用Spring Security。

This way you have everything neatly separated (no auth logic in resolvers, no external context needed) and contextual per field, even without using Spring Security.

让我们进一步做出自定义 GraphqlFieldVisibility

Let's go further and make a custom GraphqlFieldVisibility:

public class RoleBasedVisibility implements GraphqlFieldVisibility {

    private final User currentUser;

    public RoleBasedVisibility(User currentUser) {
        this.currentUser = currentUser;
    }

    @Override
    public List<GraphQLFieldDefinition> getFieldDefinitions(GraphQLFieldsContainer fieldsContainer) {
        return fieldsContainer.getFieldDefinitions().stream()
                .filter(field -> isFieldAllowed(field, currentUser))
                .collect(Collectors.toList());
    }

    @Override
    public GraphQLFieldDefinition getFieldDefinition(GraphQLFieldsContainer fieldsContainer, String fieldName) {
        GraphQLFieldDefinition fieldDefinition = fieldsContainer.getFieldDefinition(fieldName);
        return fieldDefinition == null || !isFieldAllowed(fieldDefinition, currentUser) ? null : fieldDefinition;
    }

    private boolean isFieldAllowed(GraphQLDirectiveContainer field, User user) {
        //Same as above, extract this into a common function
        Optional<GraphQLArgument> rolesRequired = DirectivesUtil.directiveWithArg(field.getDirectives(), "auth", "rolesRequired");
        List<String> roles = (List<String>) rolesRequired.get().getValue();
        return currentUser.getRoles().containsAll(roles);
    }
}

如您所见,可见性取决于用户,这次你无法从上下文中获取,所以你必须实例化它每个请求。这意味着您还需要转换架构并实例化每个请求的GraphQL 。其余的是相同的。

As you see, visibility depends on the user, which this time you can not get from the context, so you have to instantiate it per request. This means you need to transform the schema and instantiate GraphQL per request as well. The rest is the same.

GraphQLSchema schema = baseSchema.transform(
    schemaBuilder -> schemaBuilder.fieldVisibility(new RoleBasedVisibility(currentUser)));
GraphQL graphQL = GraphQL.newGraphQL(schema)
        .instrumentation(new AuthInstrumentation())
        .build();

有了这个,您就拥有了完整的安全设置。如果不允许,未经授权的用户甚至不知道某个字段是否存在。如果他们被允许一般地看到它,但他们只能有条件地获取它, AuthInstrumentation 覆盖它。

With that, you have a full security setup. Unauthorized users won't even know a field exists if they're not allowed to. If they're allowed to see it in general, but they can only fetch it conditionally, the AuthInstrumentation covers it.

这篇关于如何获取Query / Mutation操作名称的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆