JwtAccessDeniedHandler , JwtAuthenticationEntryPointHandler

Seung jun Cha·2024년 7월 14일
0
post-thumbnail

1. JwtErrorResponse 생성

  • jwt 와 관련된 응답을 위해 JwtErrorResponse 클래스를 따로 생성했다.
/**
 * JWT 관련 작업 중 발생한 오류 응답을 나타내는 클래스입니다.
 */
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class JwtErrorResponse {

    /** 오류가 발생한 타임스탬프입니다. */
    private String timestamp;

    /** 오류와 연관된 HTTP 상태 코드입니다. */
    private int status;

    /** 상태 코드에 따른 HTTP 오류 메시지입니다. */
    private String error;

    /** 오류에 대한 상세 메시지입니다. */
    private String message;

    /** 오류가 발생한 요청 경로입니다. */
    private String path;

    /**
     * 주어진 세부 정보로 새로운 JwtErrorResponse 객체를 생성합니다.
     *
     * @param timestamp 오류 발생 시간대
     * @param status HTTP 상태 코드
     * @param error 상태 코드에 따른 HTTP 오류 메시지
     * @param message 오류에 대한 상세 메시지
     * @param path 오류가 발생한 요청 경로
     */
    private JwtErrorResponse(String timestamp, int status, String error, String message, String path) {
        this.timestamp = timestamp;
        this.status = status;
        this.error = error;
        this.message = message;
        this.path = path;
    }

    /**
     * 현재 시간을 기준으로 초기화된 JwtErrorResponse 객체를 생성합니다.
     *
     * @param status HTTP 상태 코드
     * @param error 상태 코드에 따른 HTTP 오류 메시지
     * @param message 오류에 대한 상세 메시지
     * @param path 오류가 발생한 요청 경로
     * @return 새로 생성된 JwtErrorResponse 객체
     */
    public static JwtErrorResponse of(int status, String error, String message, String path) {
        String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        return new JwtErrorResponse(timestamp, status, error, message, path);
    }
}

1. JwtAccessDeniedHandler

/**
 * JWT 인증이 실패할 때 접근 거부를 처리하는 핸들러 클래스입니다.
 */
@Component
public class JwtAccessDeniedHandler implements AccessDeniedHandler {

    /**
     * 접근이 거부되었을 때 호출되는 메서드입니다.
     *
     * 이 메서드는 클라이언트가 권한이 없는 리소스에 접근하려고 할 때 호출됩니다.
     *  HTTP 응답 코드 403(Forbidden)와 함께 예외 정보를 포함한 JSON 응답을 전송합니다.
     *
     * @param request  HTTP 요청 객체
     * @param response HTTP 응답 객체
     * @param accessDeniedException 접근 거부 예외
     * @throws IOException 입출력 예외
     * @throws ServletException 서블릿 예외
     */
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        response.setStatus(HttpStatus.FORBIDDEN.value());
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding("UTF-8");

        JwtErrorResponse responseBody = JwtErrorResponse.of(
                HttpStatus.FORBIDDEN.value(),
                HttpStatus.FORBIDDEN.getReasonPhrase(),
                "권한이 없습니다.",
                    request.getRequestURI());

        response.getWriter().write(JsonUtils.convertObjectToJson(responseBody));

    }
}

  • JWT 인증이 실패할 때 처리하는 핸들러 클래스인 JwtAccessDeniedHandler를 구현했다.
  • HTTP 요청 객체(request), HTTP 응답 객체(response), 접근 거부 예외(accessDeniedException) 파라미터를 받는다
  • HTTP 응답 상태를 403 Forbidden으로 설정한다
  • 응답의 컨텐츠 타입을 JSON으로 설정한다
  • UTF-8 인코딩으로 문자열을 설정한다.

  • responseBody는 JwtAccessDeniedHandler 클래스의 handle 메서드에서 접근 거부 상황에 대한 정보를 클라이언트에게 JSON 형식으로 전달하기 위해 사용된다.

    • HttpStatus.FORBIDDEN.value() : 403 (Forbidden)을 설정하여 클라이언트가 권한이 없음을 명확하게 알림

    • HttpStatus.FORBIDDEN.getReasonPhrase() : 상태 코드에 해당하는 기본 오류 메시지를 저장

    • request.getRequestURI() : 클라이언트가 어느 경로로 요청을 시도하다가 거부되었는지 알 수 있게 함

  • response.getWriter().write(JsonUtils.convertObjectToJson(responseBody)); : 위에서 생성한 JSON 객체를 문자열로 변환하여 응답 본문에 기록한다.

  • convertObjectToJson 메서드는 JsonUtils에 있다. @UtilityClass 어노테이션은 해당 클래스의 기본생성자를 private으로, 모든 필드와 메서드를 static으로 설정한다.
  • ObjectMapper는 JSON을 Java 객체로 변환하거나 Java 객체를 JSON으로 변환할 때 사용하는 Jackson 라이브러리이다. 여기서는 ObjectMapper의 writeValueAsString 메서드를 사용하여 객체를 JSON 문자열로 변환한다.

2. JwtAuthenticationEntryPointHandler

package com.study.api.jwt.handler;

import com.study.common.exception.JwtErrorResponse;
import com.study.common.utill.JsonUtils;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import java.io.IOException;


/**
 * JWT 인증 과정에서 발생하는 예외를 처리하는 핸들러 클래스로 인증되지 않은 상태로 보호된 리소스에 접근하려고 할 때 호출됩니다.
 */
@Component
public class JwtAuthenticationEntryPointHandler implements AuthenticationEntryPoint {


    /**
     * 인증 과정에서 예외가 발생했을 때 호출되는 메서드입니다.
     *
     * 이 메서드는 클라이언트가 인증되지 않은 상태로 보호된 리소스에 접근하려고 할 때 호출됩니다.
     *  HTTP 응답 코드 401(Unauthorized)와 함께 예외 정보를 포함한 JSON 응답을 전송합니다.
     *
     * @param request       HTTP 요청 객체
     * @param response      HTTP 응답 객체
     * @param authException 인증 예외
     * @throws IOException      입출력 예외
     * @throws ServletException 서블릿 예외
     */
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding("UTF-8");

        JwtErrorResponse responseBody = JwtErrorResponse.of(
                HttpStatus.UNAUTHORIZED.value(),
                HttpStatus.UNAUTHORIZED.getReasonPhrase(),
                "인증 과정에 문제가 발생했습니다.",
                request.getRequestURI());

        String errorResponse = JsonUtils.convertObjectToJson(responseBody);
        
        response.getWriter().write(errorResponse);
    }
}

0개의 댓글