본문으로 바로가기

JWT 인터셉터 적용

category Spring boot 2024. 11. 7. 16:54

LoginInterceptor

package com.tenco.blog_v3.common.config;

import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.tenco.blog_v3.common.errors.Exception401;
import com.tenco.blog_v3.common.errors.Exception500;
import com.tenco.blog_v3.common.utils.Define;
import com.tenco.blog_v3.common.utils.JwtUtil;
import com.tenco.blog_v3.user.User;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

// IoC 를 안한 상태 이다.
public class LoginInterceptor implements HandlerInterceptor {

    /**
     * 컨트롤러 메서드 호출 전에 실행 되는 메서드 이다.
     * @param request current HTTP request
     * @param response current HTTP response
     * @param handler chosen handler to execute, for type and/or instance evaluation
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        String jwt = request.getHeader(Define.AUTHORIZATION);

        if(jwt == null || ! jwt.startsWith(Define.BEARER)) {
            throw new Exception401("JWT 토큰을 전달해주세요");
        }

        jwt = jwt.replace(Define.BEARER, "");

        try {
            User sessionUser = JwtUtil.verify(jwt);
            request.setAttribute(Define.SESSION_USER, sessionUser);
            return  true;

        } catch (TokenExpiredException e) {
            throw new Exception401("토큰 만료 시간이 지났습니다. 다시 로그인 하세요");
        } catch (JWTDecodeException e) {
            throw new Exception401("유효하지 않은 토큰입니다");
        } catch (Exception e) {
            throw new Exception500("서버 오류 : " + e.getMessage());
        }
    }

    /**
     * 컨트롤러 실행 후, 뷰가 렌더링되기 전에 실행되는 메서드
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    /**
     * 뷰가 렌더링 된 후 실행되는 메서드
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

WebConfig

package com.tenco.blog_v3.common.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

// @Component  // IOC
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired // DI 처리
    private LoginInterceptor loginInterceptor;

    /**
     * 인터셉터를 등록하고 적용할 URL 패턴을 설정하는 메서드이다.
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 로그인 인터셉터 등록
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/api/**")  // 인터셉터를 적용할 경로 패턴 설정
                .excludePathPatterns("/board/{id:\\\\d+}");
                // 인터셉터 적용에서 제외할 URL 패턴 설정
                // /board/1, /board/33  <-- 로그인 인터셉터에서 제외
                // \\d+ 숫자 하나 이상을 의미하는 정규 표현식 패턴

        // 관리자용 인터셉터 등록

    }
}

BoardController

  // 게시글 전체 조회
  @GetMapping({"/", "/boards"})
  public ResponseEntity<List<BoardResponse.ListDTO>> list() {
      return ResponseEntity.ok(boardService.getAllBoards());
  }

BoardService

  /**
   * 모든 게시글 조회 서비스
   */
  // 응답 타입 변경
  public List<BoardResponse.ListDTO> getAllBoards() {
      // 게시글을 ID 기준으로 내림차순으로 정렬해서 조회 해라.
      Sort sort = Sort.by(Sort.Direction.DESC, "id");
      List<Board> boards = boardJPARepository.findAll(sort);
      // 문법 공부해보기
      return boards.stream().map(BoardResponse.ListDTO::new).toList();
  }

BoardResponse

package com.tenco.blog_v3.board;

import lombok.Getter;
import lombok.Setter;

public class BoardResponse {

    @Getter
    @Setter
    public static class DTO {
        private int id;
        private String title;
        private String content;

        // DTO 사용시 사용자 정의 생성자
        public DTO(Board board) {
            this.id = board.getId();
            this.title = board.getTitle();
            this.content = board.getContent();
        }
    }

    // 게시글 상세보기 응답

    // 게시글 상세보기 - 댓글 정보

    // 게시글 목록 보기 화면을 위한 DTO 클래스 만들어보기
    @Getter
    @Setter
    public static class ListDTO {
        private int id;
        private String title;

        public ListDTO(Board board) {
            this.id = board.getId();
            this.title = board.getTitle();
        }
    }
}

도전 과제 게시글 상세보기 만들기

UserResponse DTO 설계

UserController 수정

/**
 * 게시글 상세보기 처리 메서드
 * 요청 주소: **GET <http://localhost:8080/board/{id}**>
 *
 * @param id      게시글의 ID
 * @param request HTTP 요청 객체
 * @return 게시글 상세보기 페이지 뷰
 */
@GetMapping("/boards/{id}")
public ResponseEntity<?> detail(@PathVariable Integer id, HttpServletRequest request) {
    User sessionUser = null;
    // api 경로가 아니기 때문에 jwt 토큰 있는지 여부 검사 해보자
    String authorizationHeader = request.getHeader(Define.AUTHORIZATION);
    if(authorizationHeader != null && authorizationHeader.startsWith(Define.BEARER)) {
        // 인증된 사용자 이다. (여기 안에 오면)
        String token = authorizationHeader.replace(Define.BEARER, "");
        try {
            sessionUser  = JwtUtil.verify(token);
        } catch (TokenExpiredException e) {
            return ResponseEntity.status(401).body(new ApiUtil<>(401, "토큰 유효시간 만료"));
        } catch (Exception e) {
            return ResponseEntity.status(401).body(new ApiUtil<>(401, "유효하지 않은 토큰 입니다"));
        }
    }
    
    // 게시글 상세보기 로직 호출 - 서비스 단에서 수정한 DTO 을 내려줘야 합니다.
    // TODO 수정 해야 함
    return ResponseEntity.ok(new ApiUtil<>(null));
}

'Spring boot' 카테고리의 다른 글

Board JWT 적용  (3) 2024.11.07
User JWT 적용  (0) 2024.11.07
JDBC란 뭘까? - 1  (1) 2024.11.07
Reply JWT 적용  (2) 2024.11.07
JwtUtil 만들어 보기  (3) 2024.11.06