将Spring Boot与GraphQLContext进行整合
我认为在Spring Boot上构建GraphQL时,有时我们希望对某些查询进行身份验证。在这种情况下,GraphQLContext非常有用。但是,关于GraphQLContext的信息非常有限,包括英文和日文。
GraphQLContext 是什么意思?
这个接口可以直接从GraphQL请求中创建具有相同名称的上下文。使用它可以通过上下文获取认证信息和角色权限。
依赖库
首先,在GraphQL的依赖库中,有很多关于应该指定什么的信息是杂乱无章的,但是看起来只要安装graphql-spring-boot-starter就可以了。
参考的网址是:https://github.com/graphql-java-kickstart/graphql-spring-boot
dependencies {
implementation("com.graphql-java-kickstart:graphql-spring-boot-starter:8.0.0")
runtimeOnly("com.graphql-java-kickstart:playground-spring-boot-starter:8.0.0")
testImplementation("com.graphql-java-kickstart:graphql-spring-boot-starter-test:8.0.0")
}
整理目标
我认为,基本上,需要在使用移动应用程序或使用SPA构建的应用程序中进行GraphQL和身份验证的组合的请求。
在这次的情况下,我想要在HTTP标头中设置身份验证令牌,并发出请求。
通过GraphQL获取HttpServletRequest
要获取HttpServletRequest对象,可以使用以下代码。
import graphql.kickstart.tools.GraphQLMutationResolver
import graphql.schema.DataFetchingEnvironment
@Component
final class GraphQLMutationResolver: GraphQLMutationResolver {
fun registerUser(input: User, environment: DataFetchingEnvironment): User {
val context = environment.getContext<DefaultGraphQLServletContext>()
val request: HttpServletRequest = context.getHttpServletRequest()
}
}
环境:DataFetchingEnvironment在Query or Mutation指定的方法参数的末尾会自动添加。
此外,DefaultGraphQLServletContext是包含在graphql-java-servlet库中的一个类,它提供了graphql-java和servlet之间的桥梁。
DefaultGraphQLServletContext是一个实现了GraphQLContext的类,可以通过environment.getContext进行使用。
创建自己的GraphQLContext
每次都从DefaultGraphQLServletContext中提取头信息并进行认证是很麻烦的。因此,我想创建一个自定义的GraphQLContext。
首先,我们将创建一个AuthGraphQLBuilder来生成GraphQLContext。
在内部,我们将使用DefaultGraphQLServletContext来从Header中提取令牌。接下来,我们将使用提取到的令牌创建自定义的AuthContext。
import graphql.kickstart.execution.context.GraphQLContext
import graphql.kickstart.servlet.context.DefaultGraphQLServletContext
import graphql.kickstart.servlet.context.GraphQLServletContext
import graphql.kickstart.servlet.context.GraphQLServletContextBuilder
@Component
final class AuthGraphQLBuilder: GraphQLServletContextBuilder {
val AUTHORIZATION = "Authorization"
override fun build(httpServletRequest: HttpServletRequest?, httpServletResponse: HttpServletResponse?): GraphQLContext {
val context = DefaultGraphQLServletContext.createServletContext().with(httpServletRequest).with(httpServletResponse).build()
val accessToken: String? = context.httpServletRequest.getHeader(AUTHORIZATION)
return AuthContext(formatAccessToken(accessToken), context)
}
override fun build(session: Session?, handshakeRequest: HandshakeRequest?): GraphQLContext {
throw IllegalStateException("not support")
}
override fun build(): GraphQLContext {
throw IllegalStateException("not support")
}
private fun formatAccessToken(rawToken: String?): String? {
return rawToken?.replace("Bearer ", "")
}
}
处理的内容很简单,只是在verifyToken中执行认证处理而已。
class AuthContext(accessToken: String?, private val context: GraphQLServletContext): GraphQLServletContext {
init {
accessToken?.also { token ->
verifyToken(token)
}
}
private fun verifyToken(accessToken: String) {
// 認証処理をする
}
override fun getSubject(): Optional<Subject> {
return context.subject
}
override fun getDataLoaderRegistry(): Optional<DataLoaderRegistry> {
return context.dataLoaderRegistry
}
override fun getFileParts(): MutableList<Part> {
return context.fileParts
}
override fun getParts(): MutableMap<String, MutableList<Part>> {
return context.parts
}
override fun getHttpServletRequest(): HttpServletRequest {
return context.httpServletRequest
}
override fun getHttpServletResponse(): HttpServletResponse {
return context.httpServletResponse
}
}
最后,使用方法很简单。只需指定AuthContext,并能获取到已经认证的上下文。
val authContext = environment.getContext<AuthContext>()
我最近对GraphQLContext进行了一些调查,但是发现信息很陈旧,无法找到可参考的内容,非常困扰。而且,由于找不到官方文档,更加困难。
对我有帮助的是下面这个Youtube视频:https://www.youtube.com/watch?v=YsM2VSnWUcg&t=421s。