Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/391.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何获取查询/变异操作名称_Java_Graphql_Graphql Java - Fatal编程技术网

Java 如何获取查询/变异操作名称

Java 如何获取查询/变异操作名称,java,graphql,graphql-java,Java,Graphql,Graphql Java,我是Spring boot+GraphQL的新手。 我需要在控制器类中获取查询/变异操作名称 目的:需要授予某些用户特定变异/查询操作的权限。 在这里,用户类型将作为请求头传递,并将被验证并检查是否允许用户访问该操作 @PostMapping public ResponseEntity<Object> callGraphQLService(@RequestBody String query, @RequestHeader("user") String userName) {

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

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

@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);
}
@PostMapping
公共响应属性callGraphQLService(@RequestBody字符串查询,@RequestHeader(“用户”)字符串用户名){
ExecutionResult=graphService.getGraphQL().execute(ExecutionInput.newExecutionInput())
.query(查询)
.context(用户名)
.build());
返回新的响应状态(结果,HttpStatus.OK);
}
建议任何有效的机制来执行特定查询/变异的授权


我认为您在这里考虑的是REST术语中的授权,它与GraphQL的映射不好。您需要一种更细粒度的方法,而不是基于操作名称(或基于REST中的URL)在顶层进行单个决策。您需要知道谁可以在字段级别查看/做什么,因为客户可以进行特别选择

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

更好的是,您还应该提供一个自定义的
GraphqlFieldVisibility
实现,以便未经授权的客户端甚至不知道他们在模式中不允许看到的字段的存在。例如,您可以使用Spring的
SpelExpressionParser
,根据Spring安全规则,决定架构的哪些部分对于每个用户都是动态可见的

如果Spring Security不是一个选项,您可以实现一个自定义的
检测
(例如,通过扩展
SimpleInstrumentation
)。在那里,您可以实现像
beginExecuteOperation
这样的回调,这将使您能够访问已解析的查询(如果您真的只想执行REST样式的顶级身份验证,就足够了),或者
begin(Deferred)Field
(它允许您访问
FieldDefinition
)或
beginFieldFetch/instrumentDataFetcher
(这使您可以访问整个
DataFetchingEnvironment
)以对每个字段执行身份验证

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

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

下面是一个抽象示例,展示了使用
工具化方法的要点(因为您不需要Spring安全性方法的特殊功能,只需像往常一样使用Spring安全性):

执行查询时,将当前用户放入上下文:

//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()
这样,即使不使用Spring安全性,也可以将所有内容整齐地分开(解析器中没有身份验证逻辑,不需要外部上下文)和每个字段的上下文

让我们进一步制作一个自定义的
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);
    }
}

这样,您就有了一个完整的安全设置。如果不允许未经授权的用户知道某个字段存在,他们甚至都不会知道。如果允许他们查看该字段,但他们只能有条件地获取该字段,则
AuthInstrumentation
会覆盖该字段。

我认为您在这里考虑的是REST术语中的授权,并且它与G不匹配raphQL。您需要一种更精细的方法,而不是基于操作名称(或基于REST中的URL)在顶层做出单个决策。您需要知道在字段级允许谁查看/做什么,因为允许客户端进行特别选择

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

更好的是,您还应该提供一个自定义的
GraphqlFieldVisibility
实现,以便未经授权的客户端甚至不知道他们在架构中不允许看到的字段的存在。您可以使用Spring的
SpelExpressionParser
来决定架构的哪些部分是可见的根据Spring安全规则,为每个用户创建一个新的安全机制

如果Spring Security不是一个选项,那么您可以实现一个自定义的
指令插入
(例如,通过扩展
SimpleInstrumentation
)。在那里,您可以实现像
beginExecuteOperation
这样的回调,这将允许您访问已解析的查询(如果您真的只想进行REST样式的顶级身份验证就足够了),或
begin(Deferred)Field
(允许您访问
FieldDefinition
)或
beginFieldFetch/instrumentDataFetcher
(允许您访问整个
DataFetchingEnvironment
)以对每个字段执行身份验证

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

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

下面是一个抽象示例,展示了使用
工具化方法的要点(因为您不需要Spring安全性方法的特殊功能,只需要u
//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()
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);
    }
}
GraphQLSchema schema = baseSchema.transform(sch ->
        sch.codeRegistry(baseSchema.getCodeRegistry().transform(code ->
                code.fieldVisibility(new RoleBasedVisibility(currentUser)))));

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