0

我们将 spqr graphQl 与 Spring 一起使用。它工作得很好并且简化了很多,谢谢!现在我们使用 ResolverInterceptor 进行授权:从 DefaultGlobalContext 读取 JWT-Token 并进行验证。我们还能够从这个令牌中确定当前的用户名。所以现在我的问题是:我可以将此用户名存储在 spring bean 中吗?spring bean 和上下文之间有什么联系吗?我们目前所做的是将 DefaultGlobalContext 作为@GraphQLRootContext 注入到我们@GraphQLApi 的每个方法中,并以这种方式获取用户。如果我们可以只读取一次,将其存储在某个地方并可以在 Spring 托管服务中访问,那就更好了。有谁知道这是怎么做到的吗?提前致谢, 马蒂亚斯

4

1 回答 1

3

你可以做几件事。

1. 定制ArgumentInjector

最简单的方法(也是我推荐的方法)是连接自定义ArgumentInjector. 您可以使其对某些注释或仅按类型做出反应。例如

@Query
public Book book(String id, @LoggedIn User user) {...}

还有一个像这样的注射器:

public UserInjector implement ArgumentInjector {
    @Override
    public Object getArgumentValue(ArgumentInjectorParams params) {
        ResolutionEnvironment env = params.getResolutionEnvironment();
        return extractUserFromRootContext(env.rootContext);
    }

    @Override
    public boolean supports(AnnotatedType type, Parameter parameter) {
        //You can also check type.getType().equals(User.class), but IMHO explicit is always better
        return parameter != null && parameter.isAnnotationPresent(LoggedIn.class);
    }
}

2.自定义GraphQLExecutor和请求范围的bean

另一种方法是让一个@RequestScopedbean 注入GraphQLExecutor到你的类和你的@GraphQLApi类中。

@Component
//implements GraphQLServletExecutor
public class CustomGraphQLExecutor extends DefaultGraphQLExecutor {

    //This is @RequestScoped
    private UserHolder userHolder;

    @Autowired
    public CustomGraphQLExecutor(
            ServletContextFactory contextFactory,
            Optional<DataLoaderRegistryFactory> registryFactory,
            UserHolder userHolder) {

        super(contextFactory, registryFactory.orElse(null));
        this.userHolder = userHolder;
    }

    @Override
    public Map<String, Object> execute(GraphQL graphQL, GraphQLRequest graphQLRequest, NativeWebRequest nativeRequest) {
        userHolder.setUser(getUserFromTheRequest(nativeRequest));
        return super.execute(graphQL, graphQLRequest, nativeRequest);
    }
}

@Service
@GraphQLApi
public class BookService {

    @Autowired
    private UserHolder userHolder; //@RequestScoped

    @Query
    public Book book(String id) {
        //Access userHolder as needed
        ... 
    }
}

我不喜欢这种方法,因为它对ThreadLocal. 如果在任何时候您开始使解析器异步(这很可能使用 graphql-java),您将需要特别包装的线程池,否则这些东西根本无法工作。(另外,如果你曾经求助于 WebFlux,它可能根本不起作用,因为 graphql-java 不支持 Reactor 并且无法传播上下文)。

3. 使用 Spring Security

由于您所做的只是基本的 Spring Security 内容,因此只需使用它为您提供的功能。它与请求范围的 bean 方法具有相同的问题,但您至少不必自己做任何事情。Spring 已经让您SecurityContext可以从任何地方访问。而且如果自定义了控制器类,也可以直接将Principal/注入Authentication到控制器方法中。

于 2019-10-23T09:23:26.157 回答