개발을 하다보면 API 호출시 사용자의 인증정보를 체크할 필요성이 생긴다.
하지만 모든 API에 인증 체크 코드를 넣어두기엔 유지보수도 힘들고 같은 코드가 모든 메소드에 반복되게 되니
보통은 요청이 넘어오기 전에 Interceptor를 둬서 인증 체크하고 실제 메소드로 넘겨주는 방법을 사용하게 된다.
아래는 예전에 인터셉터를 구성했던 코드를 가져와봤다.
JwtAuthInterceptor.java
package me.huiya.core.Config;
import me.huiya.core.Common.JWTManager;
import me.huiya.Exception.AuthRequiredException;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class JwtAuthInterceptor implements HandlerInterceptor {
private static final String HEADER_TOKEN_KEY = "Bearer ";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if(request.getMethod().equals("OPTIONS")) {
return true;
}
String token = request.getHeader("Authorization");
if(token == null) {
throw new AuthRequiredException("Required token");
}
token = token.replace(HEADER_TOKEN_KEY, "");
JWTManager.verify(token); // 실제 체크하는 메소드
return true;
}
}
WebConfig.java
package me.huiya.core.Config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("*")
.maxAge(3000);
}
private static final String[] INTERCEPTOR_WHITE_LIST = {
"/error",
// /error 를 화이트리스트로 등록하지 않으면 서버 에러시 /error 호출하면서 인터셉터가 다시 실행된다
// 2020-12-25 18:28 huiya
"/", "/status",
"/images/*",
"/account/signup-check/email",
"/account/signup",
"/auth/signin",
"/auth/refresh",
};
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new JwtAuthInterceptor())
.addPathPatterns("/**")
.excludePathPatterns(INTERCEPTOR_WHITE_LIST);
}
}
JwtAuthInterceptor에 인터셉터를 만들고, WebConfig에서 인터셉터를 모든 요청에 넣어준다.
하지만 인증 체크를 넣지 말아야 할 API가 있을 수도 있다.
예를 들어 회원가입 API나 이미지를 요청하는 API에서는 인증 체크를 해선 안된다.
그래서 WebConfig에서 INTERCEPTOR_WHITE_LIST 목록을 통해 인터셉터를 제외할 API를 지정해놓고 있다.
하지만 이 목록이 많아진다면? 반대로 인증 체크를 해야할 목록을 관리해야 하나? 만약 그렇게 관리하다가 이번에는 인증 체크가 필요한 API가 많아진다면?
API를 추가할때마다 저 화이트리스트 목록에 API를 추가하는 작업은 생각보다 귀찮을 수 밖에 없다.
만약 각 메소드를 개발할때 annotation을 통해 인터셉터를 적용할지 안할지 여부를 결정할 수 있다면 좀 더 편하게 개발을 진행할 수 있을 것이다.
1. annotation 추가 (WithOutAuth.java)
package me.huiya.core.Config;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface WithOutAuth {
}
2. Interceptor에서 annotation 체크하기 (JwtAuthInterceptor.java)
package me.huiya.core.Config;
import me.huiya.core.Common.JWTManager;
import me.huiya.core.Exception.AuthRequiredException;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class JwtAuthInterceptor implements HandlerInterceptor {
private static final String HEADER_TOKEN_KEY = "Bearer ";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if(request.getMethod().equals("OPTIONS")) {
return true;
}
// 컨트롤러에 @WithOutAuth 어노테이션이 사용되었는지 체크함
WithOutAuth withoutAuth = ((HandlerMethod) handler).getMethodAnnotation(WithOutAuth.class);
if(withoutAuth == null) {
// @WithOutAuth 없으면 인증 체크
String token = request.getHeader("Authorization");
if(token == null) {
throw new AuthRequiredException("Required token");
}
token = token.replace(HEADER_TOKEN_KEY, "");
JWTManager.verify(token);
} else {
// @WithOutAuth 어노테이션이 있으므로 인증 체크하지 않고 넘어감
// 2021-07-15 13:55 huiya
}
return true;
}
}
3. Controller에 @WithOutAuth 추가
@RestController
@RequestMapping(value="/")
public class RootController {
@RequestMapping(value={"/", "/status"})
@WithOutAuth
public Result index() {
// (생략)
}
}
이제 기존에 사용하고 있던 화이트리스트를 지워버리면 끝이다.
기존에는 화이트리스트를 통해서 인터셉터를 실행할지 말지를 결정했다면
이 코드는 인터셉터는 무조건 실행되지만 인증 체크 여부를 인터셉터 내부에서 결정하게 된다.
이 방법은 스프링 3.0에서는 동작하지 않는다고 한다.
출처.
'개발 > Backend' 카테고리의 다른 글
[Kotlin/SpringBoot] jjwt 0.12.x으로 JWT 토큰 발행하기 (0) | 2023.12.04 |
---|---|
[Spring Boot] Log4J2 shell 취약점 대응하기 (0) | 2021.12.13 |
[Spring Boot] 작업 비동기로 실행하기 (2) | 2021.07.13 |
[Spring Boot] html 템플릿 메일 보내기 - Thymeleaf (0) | 2021.07.13 |
Access-Control-Allow-Origin을 넣어도 POST에서 CORS 에러가 발생할때 (0) | 2020.07.09 |