🛩️
【Spring Boot】Spring for GraphQLで関数ごとに認証の有無を設定できるようにする
概要
Spring for GraphQLではチュートリアルにある通り、リクエストのUTLのパスは基本的に単一であり、関数ごとに認証をかけたい場合は少し工夫が必要です。
取れる対応案としては複数あると思いますが、今回はGraphQL backend — authorization & authenticationの記事で紹介されているような方法で実装してみたので、その紹介です。
前提
- 上記記事ではKotlinでの実装ですが、この記事ではJavaで実装します。
- 使用したspring-boot-starter-graphqlのバージョンは
3.3.0
です。
実装
まずは認証部分の実装となるRequestManager
です。
ここでは、ヘッダーからのトークン取得と、一度認証した情報を取得できる関数を実装しています。
RequestManager
@Component
public class RequestManager {
@Autowired
UserAccountService userAccountService;
private final String LOGIN_USER_ID_SESSION = "userAccountId";
// Authorizationヘッダーからトークンをdecodeして保存する処理
public void saveUserAccountIdSession(HttpServletRequest request) {
String authHeader = request.getHeader("Authorization");
// Authorizationヘッダーが設定されているか判定
if (authHeader != null && authHeader.split(" ").length > 1) {
String bearerToken = authHeader.split(" ")[1];
try {
// トークンの検証(実装内容は割愛)
String userAccountId = userAccountService.getUserIdFromAuthToken(bearerToken);
request.setAttribute(LOGIN_USER_ID_SESSION, userAccountId);
} catch (Exception e) {
// 失敗した場合は何もしない
}
}
}
// ContextHolderからユーザ情報を取得する処理
public String getUserAccountIdSession() {
Object session = RequestContextHolder
.getRequestAttributes().getAttribute(LOGIN_USER_ID_SESSION, RequestAttributes.SCOPE_REQUEST);
return (String) session;
}
}
次にRequestFilterです。filterにてRequestManagerのヘッダー取得処理を呼び出しています。
RequestFilter
@Component
public class RequestFilter extends OncePerRequestFilter {
@Autowired
RequestManager requestManager;
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
// リクエストヘッダーのトークンを検証してユーザIDを格納する処理
requestManager.saveUserAccountIdSession(request);
filterChain.doFilter(request, response);
}
}
次にアノテーションの実装です。
今回はLoggedInOnly
というアノテーションを用意して、このアノテーションが付与されたら認証チェックが行われるようにします。
LoggedInOnly
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LoggedInOnly {}
AuthAspect
@Aspect
@Component
public class AuthAspect {
@Autowired
RequestManager requestManager;
@Before("@annotation(com.sample.annotation.loginStatus.LoggedInOnly)")
public void checkLoggedInOnly() throws GraphqlErrorException {
String userAccountId = requestManager.getUserAccountIdSession();
if (userAccountId == null) {
throw GraphqlErrorException
.newErrorException()
.errorClassification(ErrorType.FORBIDDEN)
.message("Not Login")
.build();
}
}
}
最後に関数へアノテーションを設定する部分の実装です。
PostCategoryController
@Controller
public class PostCategoryController {
@Autowired
PostCategoryService postCategoryService;
@Autowired
RequestManager requestManager;
@MutationMapping
@LoggedInOnly
public Boolean addPostCategory(@Argument String name) throws GraphqlErrorException {
String userAccountId = requestManager.getUserAccountIdSession();
return postCategoryService.addPostCategory(name);
}
}
Discussion