We are using spqr graphQl with Spring. It works great and simplifies a lot, thanks for that! Right now we are using a ResolverInterceptor for authorization: A JWT-Token is read from DefaultGlobalContext and validated. We are also able to determine the current username from this token. So now my question is: Can I store this username in a spring bean? Is there any connection between spring beans and the context? What we do at the moment is to inject the DefaultGlobalContext as a @GraphQLRootContext into every method of our @GraphQLApi and get the user this way. It would be nicer if we could just read it once, store it somewhere and have access in a spring managed service. Does anyone know how to do that? Thanks in advance, Matthias
-
I'd use a central data store like redis to store the user and its JWT. But I'm not sure if there's a Java specific way. – Vatsal Jain Sep 25 '19 at 15:13
1 Answers
There's a few things you can do.
1. Custom ArgumentInjector
The simplest way (and the one I'd recommend) is wiring a custom ArgumentInjector
. You can make it react to some annotation, or just by type. E.g.
@Query
public Book book(String id, @LoggedIn User user) {...}
And an injector like:
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. Custom GraphQLExecutor
and a request-scoped bean
Another way is to have a @RequestScoped
bean that you inject both into your GraphQLExecutor
and into your @GraphQLApi
class.
@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
...
}
}
I do not like this approach as it creates an invisible dependency to the ThreadLocal
. If at any point you start making your resolvers asynchronous (which is highly probable with graphql-java), you'll need specially wrapped thread pools or the things won't work at all. (Also, if you ever turn to WebFlux, it likely won't work at all, as graphql-java isn't Reactor-aware and can't propagate the context).
3. Use Spring Security
Since all you're doing is basic Spring Security stuff, just use the features it gives you. It has the same issues as the request-scoped bean approach, but you at least don't have to do anything yourself. Spring already gives you SecurityContext
access from anywhere. And if you customize the controller class, you can also inject Principal
/ Authentication
directly into the controller methods.

- 12,984
- 10
- 64
- 118