Is it possible to support Mono return type in DataFetcher?
jaggerwang opened this issue · 1 comments
jaggerwang commented
Right now, graphql java webflux only support CompletableFuture
return type, the mono returned need to be converted to CompletableFuture
by calling toFuture()
method.
package net.jaggerwang.scip.gateway.adapter.graphql.datafetcher.query;
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import net.jaggerwang.scip.gateway.adapter.graphql.datafetcher.AbstractDataFetcher;
import org.springframework.stereotype.Component;
@Component
public class QueryUserInfoDataFetcher extends AbstractDataFetcher implements DataFetcher {
@Override
public Object get(DataFetchingEnvironment env) {
var id = Long.valueOf((Integer) env.getArgument("id"));
return userAsyncService.info(id)
.subscriberContext(context -> env.getContext())
.toFuture();
}
}
But if I want to use spring security to protect method by @EnableReactiveMethodSecurity
, spring security will require the method to return Mono
or Flux
. The two are conflicted.
@Component
public class QueryUserInfoDataFetcher extends AbstractDataFetcher implements DataFetcher {
@Override
@PreAuthorize("isAuthenticated()")
public Object get(DataFetchingEnvironment env) {
var id = Long.valueOf((Integer) env.getArgument("id"));
return monoWithContext(userAsyncService.info(id), env);
}
}
How can I solve this problem?
jaggerwang commented
Finally, I use a aspect to convert Mono
to CompletableFuture
and do authentication.
package net.jaggerwang.scip.gateway.api.security;
...
/**
* Check authentication and authorization of all GraphQL datafetchers, and convert Mono returned
* by datafetcher to CompletableFuture, as GraphQL not support reactor's Mono and Flux.
*/
@Component
@Aspect
public class SecureGraphQLAspect {
@Around("allDataFetchers() && isInApplication()")
public Object doSecurityCheck(ProceedingJoinPoint joinPoint) {
var args = joinPoint.getArgs();
var env = (DataFetchingEnvironment) args[0];
return ReactiveSecurityContextHolder.getContext()
.doOnSuccess(securityContext -> {
var method = ((MethodSignature) joinPoint.getSignature()).getMethod();
var permitAll = method.getAnnotation(PermitAll.class);
if (permitAll == null) {
var auth = securityContext.getAuthentication();
if (auth == null || auth instanceof AnonymousAuthenticationToken ||
!auth.isAuthenticated()) {
throw new UnauthenticatedException("未认证");
}
}
})
.flatMap(securityContext -> {
Object result;
try {
result = joinPoint.proceed();
} catch (Throwable e) {
return Mono.error(new RuntimeException(e));
}
return result instanceof Mono ? (Mono) result : Mono.just(result);
})
.subscriberContext(context -> env.getContext())
.toFuture();
}
@Pointcut("target(graphql.schema.DataFetcher)")
private void allDataFetchers() {
}
@Pointcut("within(net.jaggerwang.scip.gateway.adapter.graphql..*)")
private void isInApplication() {
}
}