스터디 프로젝트에서 화면(client)은 React로, 서버(server)는 Spring Boot으로 개발하고 있다.
다양한 이슈로 인해 화면 단 서버를 Node로 띄우고 있다. 따라서 CORS(Cross-Origin Resource Sharing, CORS) 이슈가 발생하여 Spring Boot에 다음과 같은 처리를 하였다.
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:3000", "http://35.222.169.XX:9000");
}
}
하지만 JWT(JSON Web Token)을 적용하는 과정에서 서버 단에서 response header에 값을 설정하고, 화면 단에서 읽는 과정에서 값을 읽지 못하는 현상(null 값으로 반환됨)이 발생하였다.
response.setHeader("jwt-token", "token value");
fetch("/user/login", oOption).then((response) => {
// not "token value", but null
var token = response.headers.get("jwt-token");
});
구글링을 해보니 CORS의 경우 기본적으로 화면에서 response header 값을 읽지 못한다고 한다. (개발자도구 네트워크 탭에서는 확인 할 수 있음.. JavaScript 코드에서 읽을 수 없음)
좌절하고 response body에 값을 넣으려다 조금 더 찾아보니 addCorsMappings에서 다음을 추가하면 화면에서 지정된 header 값("jwt-token")은 읽을 수 있다고 하여 추가하였고, 화면에서 값을 읽을 수 있게 되었다.
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:3000", "http://35.222.169.XX:9000")
.exposedHeaders("jwt-token") //make client read header("jwt-token")
;
}
}
fetch("/user/login", oOption).then((response) => {
// return "token value"
var token = response.headers.get("jwt-token");
});
구글링을 해보니 CORS 시 browser에서 API를 받는 서버가 요청을 받을 수 있는지 option 메소드를 통해 먼저 서버에 호출한다고 한다. 그런데 해당 요청을 보낼 때 request header에 실어보낸 JWT를 받지 못하여 다음과 같은 문제가 발생하였다.
Access to fetch at 'http://localhost:9090/user/jwt-auth/tokeninfo' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
따라서 JWT를 체크하는 부분에서 request method가 OPTIONS일 경우 JWT를 체크하지 않는 방향으로 문제를 해결하게 되었다.
@Component
public class JwtInterceptor extends HandlerInterceptorAdapter {
Logger logger = LoggerFactory.getLogger("io.ojw.mall.interceptor.JwtInterceptor");
private static final String TOKEN = "jwt-token";
private JwtService jwtService;
public JwtInterceptor(JwtService jwtService) {
this.jwtService = jwtService;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
final String token = request.getHeader(TOKEN);
logger.debug("JwtInterceptor > preHandle > token: " + token);
if (StringUtils.equals(request.getMethod(), "OPTIONS")) {
logger.debug("if request method is options, return true");
return true;
}
if(token != null && jwtService.checkToken(token)){
return true;
}else{
throw new UnauthorizedException();
}
}
}
좋은 내용 잘 보고 갑니다!!! 감사합니다.