❗메서드의 문제를 파악하여 리팩토링 후 비교
JWT 필터 메서드 → 리팩토링 진행
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String url = httpRequest.getRequestURI();
if (url.startsWith("/auth")) {
chain.doFilter(request, response);
return;
}
String bearerJwt = httpRequest.getHeader("Authorization");
if (bearerJwt == null) {
// 토큰이 없는 경우 400을 반환합니다.
httpResponse.sendError(HttpServletResponse.SC_BAD_REQUEST, "JWT 토큰이 필요합니다.");
return;
}
String jwt = jwtUtil.substringToken(bearerJwt);
try {
// JWT 유효성 검사와 claims 추출
Claims claims = jwtUtil.extractClaims(jwt);
if (claims == null) {
httpResponse.sendError(HttpServletResponse.SC_BAD_REQUEST, "잘못된 JWT 토큰입니다.");
return;
}
UserRole userRole = UserRole.valueOf(claims.get("userRole", String.class));
httpRequest.setAttribute("userId", Long.parseLong(claims.getSubject()));
httpRequest.setAttribute("email", claims.get("email"));
httpRequest.setAttribute("userRole", claims.get("userRole"));
if (url.startsWith("/admin")) {
// 관리자 권한이 없는 경우 403을 반환합니다.
if (!UserRole.ADMIN.equals(userRole)) {
httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "관리자 권한이 없습니다.");
return;
}
chain.doFilter(request, response);
return;
}
chain.doFilter(request, response);
} catch (SecurityException | MalformedJwtException e) {
log.error("Invalid JWT signature, 유효하지 않는 JWT 서명 입니다.", e);
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "유효하지 않는 JWT 서명입니다.");
} catch (ExpiredJwtException e) {
log.error("Expired JWT token, 만료된 JWT token 입니다.", e);
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "만료된 JWT 토큰입니다.");
} catch (UnsupportedJwtException e) {
log.error("Unsupported JWT token, 지원되지 않는 JWT 토큰 입니다.", e);
httpResponse.sendError(HttpServletResponse.SC_BAD_REQUEST, "지원되지 않는 JWT 토큰입니다.");
} catch (Exception e) {
log.error("Invalid JWT token, 유효하지 않는 JWT 토큰 입니다.", e);
httpResponse.sendError(HttpServletResponse.SC_BAD_REQUEST, "유효하지 않는 JWT 토큰입니다.");
}
}
doFilter() 메서드에 너무 많은 로직이 포함되어 있다. (JWT 추출, 검증, 권한 체크 등)doFilter의 책임을 명확히 "요청 흐름 제어"로만 한정한다.isAuthUri(): 인증 예외 URL 판단private boolean isAuthUri(String uri) {
return uri.startsWith("/auth");
}
extractToken(): 헤더에서 토큰 추출 및 가공private String extractToken(HttpServletRequest request, HttpServletResponse response) throws IOException {
String bearerToken = request.getHeader("Authorization");
if (bearerToken == null) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "JWT 토큰이 필요합니다.");
return null;
}
return jwtUtil.substringToken(bearerToken);
}
validateToken(): JWT 유효성 검사 및 claims 추출 private Claims validateToken(String token, HttpServletResponse response) throws IOException {
try {
return jwtUtil.extractClaims(token);
} catch (SecurityException | MalformedJwtException e) {
log.error("유효하지 않은 JWT 서명", e);
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "유효하지 않는 JWT 서명입니다.");
} catch (ExpiredJwtException e) {
log.error("만료된 JWT 토큰", e);
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "만료된 JWT 토큰입니다.");
} catch (UnsupportedJwtException e) {
log.error("지원되지 않는 JWT 토큰", e);
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "지원되지 않는 JWT 토큰입니다.");
} catch (Exception e) {
log.error("JWT 검증 실패", e);
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "유효하지 않는 JWT 토큰입니다.");
}
return null;
}
setUserAttributes(): claims → request attribute 설정private void setUserAttributes(HttpServletRequest request, Claims claims) {
request.setAttribute("userId", Long.parseLong(claims.getSubject()));
request.setAttribute("email", claims.get("email"));
request.setAttribute("userRole", claims.get("userRole"));
}
authorizeAdminAccess(): 관리자 URL 접근 시 권한 확인private boolean authorizeAdminAccess(String requestURI, HttpServletRequest request, HttpServletResponse response) throws IOException {
String userRole = (String) request.getAttribute("userRole");
if (requestURI.startsWith("/admin") && !UserRole.ADMIN.name().equals(userRole)) {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "관리자 권한이 없습니다.");
return false;
}
return true;
}
doFilter()는 흐름 조정만 담당@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String requestURI = httpRequest.getRequestURI();
//auth url 판단
if (isAuthUri(requestURI)) {
chain.doFilter(request, response);
return;
}
//토큰 추출
String token = extractToken(httpRequest, httpResponse);
if (token == null) return;
//Claims 생성 및 정합성 검사
Claims claims = validateToken(token, httpResponse);
if (claims == null) return;
//Attribute 에 유저 정보 저장
setUserAttributes(httpRequest, claims);
//관리자 권한 여부 판단
if (!authorizeAdminAccess(requestURI, httpRequest, httpResponse)) {
return;
}
chain.doFilter(request, response);
}