예를들어 인증, 인가와 같은 작업을 Controller 에서 하게되면 Controller 의 책임이 불필요하게 많아지고,
비즈니스로직과 섞여 유지보수가 어려워지는 문제점이 발생한다.
Intrerceptor 와 ArgumentResolver 를 사용해 요청이 API 에 라우팅 되기 전,
공통 처리 작업들을 사전에 하면 명확한 책임 분리와 많은 중복을 제거할 수 있게된다.
@GetMapping("/logout")
public ResponseEntity logout(
@RequestHeader("Authorization") String accessToken,
HttpServletRequest request
) {
log.info("logout 요청 확인");
String refreshToken = jwtService.getRefreshToken(request.getCookies());
Member member = jwtService.getMember(accessToken);
jwtService.expireToken(accessToken, refreshToken);
log.info("logout 완료 / member id = {}", member.getId());
return ResponseEntity.noContent()
.build();
}
import com.atowz.auth.infrastructure.jwt.JwtService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
@Slf4j
@Component
@RequiredArgsConstructor
public class AccessTokenInterceptor implements HandlerInterceptor {
private final JwtService jwtService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String accessToken = request.getHeader("Authorization");
if (accessToken != null) {
jwtService.isAccessTokenValid(accessToken);
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
import com.atowz.auth.infrastructure.jwt.JwtService;
import com.atowz.global.interceptor.AccessTokenInterceptor;
import com.atowz.global.interceptor.RefreshTokenInterceptor;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@RequiredArgsConstructor
public class WebMvcConfig implements WebMvcConfigurer {
private final JwtService jwtService;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AccessTokenInterceptor(jwtService))
.addPathPatterns("/**");
}
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.Documented;
@Target({ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AccessTokenToMember {
boolean required() default true;
}
import com.atowz.auth.infrastructure.jwt.JwtService;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
@Component
@RequiredArgsConstructor
public class AccessTokenArgumentResolver implements HandlerMethodArgumentResolver {
private final JwtService jwtService;
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(AccessTokenToMember.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
String accessToken = request.getHeader("Authorization");
return jwtService.getMember(accessToken);
}
}
import com.atowz.global.argumentResolver.accessTokenToMember.AccessTokenArgumentResolver;
import com.atowz.global.argumentResolver.getToken.TokenArgumentResolver;
import com.atowz.global.argumentResolver.refreshTokenToMember.RefreshTokenArgumentResolver;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {
private final AccessTokenArgumentResolver accessTokenArgumentResolver;
private final RefreshTokenArgumentResolver refreshTokenArgumentResolver;
private final TokenArgumentResolver tokenArgumentResolver;
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(accessTokenArgumentResolver);
resolvers.add(refreshTokenArgumentResolver);
resolvers.add(tokenArgumentResolver);
}
}
@GetMapping("/logout")
public ResponseEntity logout(
@GetToken TokenRequest tokenRequest,
@AccessTokenToMember Member member) {
log.info("logout 요청 확인");
jwtService.expireToken(tokenRequest);
log.info("logout 완료 / member id = {}", member.getId());
return ResponseEntity.noContent()
.build();
}