[Practice] 인터셉터 활용 (2)

2021. 4. 20. 00:10Spring/Practice

1. 문제

  • 인터셉터를 이용하여 JWT 인증이 필요한 API 에 대해서(글쓰기) 토큰 유효성을 검증하는 API
  • 게시글 작성 기능 구현 ("/api/board")
  • 글쓰기 API 호출 시 토큰 유효성 검사

 

 

 

2. 풀이

- BoardInput.java

package com.example.jpa.sample.board.model;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor
@Builder
@Data
public class BoardInput {

    private Long boardType;
    private String title;
    private String contents;
}

- BoardService.java

package com.example.jpa.sample.board.service;

import com.example.jpa.sample.board.entity.Board;
import com.example.jpa.sample.board.entity.BoardBadReport;
import com.example.jpa.sample.board.entity.BoardComment;
import com.example.jpa.sample.board.entity.BoardType;
import com.example.jpa.sample.board.model.*;

import java.util.List;

public interface BoardService {

    ...

    ServiceResult add(String email, BoardInput boardInput);
}

- BoardServiceImpl.java

package com.example.jpa.sample.board.service;

import com.example.jpa.sample.board.entity.*;
import com.example.jpa.sample.board.model.*;
import com.example.jpa.sample.board.repository.*;
import com.example.jpa.sample.common.exception.BizException;
import com.example.jpa.sample.user.entity.User;
import com.example.jpa.sample.user.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

@RequiredArgsConstructor
@Service
public class BoardServiceImpl implements BoardService {

    ...
    
    @Override
    public ServiceResult add(String email, BoardInput boardInput) {
        Optional<User> optionalUser = userRepository.findByEmail(email);
        if(!optionalUser.isPresent()) {
            throw new BizException("회원 정보가 존재하지 않습니다.");
        }
        User user = optionalUser.get();

        Optional<BoardType> optionalBoardType = boardTypeRepository.findById(boardInput.getBoardType());
        if(!optionalBoardType.isPresent()) {
            return ServiceResult.fail("게시판 정보가 존재하지 않습니다.");
        }
        BoardType boardType = optionalBoardType.get();

        Board board = Board.builder()
                .user(user)
                .boardType(boardType)
                .title(boardInput.getTitle())
                .contents(boardInput.getContents())
                .regDate(LocalDateTime.now())
                .build();

        boardRepository.save(board);

        return ServiceResult.success();
    }
}

- ApiBoardController.java

package com.example.jpa.sample.board.controller;

import com.auth0.jwt.exceptions.JWTVerificationException;
import com.example.jpa.sample.board.entity.Board;
import com.example.jpa.sample.board.entity.BoardType;
import com.example.jpa.sample.board.model.*;
import com.example.jpa.sample.board.service.BoardService;
import com.example.jpa.sample.common.exception.BizException;
import com.example.jpa.sample.common.model.ResponseResult;
import com.example.jpa.sample.notice.model.ResponseError;
import com.example.jpa.sample.user.model.ResponseMessage;
import com.example.jpa.sample.util.JwtUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.List;

@RequiredArgsConstructor
@RestController
public class ApiBoardController {

    ...
    
    //------------------------------------------------------------------------------
    // 문제 1
    @GetMapping("/api/board")
    public ResponseEntity<?> list() {

        List<Board> list = boardService.list();

        return ResponseResult.success(list);
    }

    // 문제 2
    @PostMapping("/api/board")
    public ResponseEntity<?> add(@RequestBody BoardInput boardInput, @RequestHeader("X-ACCESS-TOKEN") String token) {

//        String email = "";
//        try {
//            email = JwtUtils.getIssuer(token);
//        } catch(JWTVerificationException e) {
//            return ResponseResult.fail("토큰 정보가 정확하지 않습니다.");
//        }
        
        String email = JwtUtils.getIssuer(token); // 인터셉터에서 자동으로 예외를 잡음
        ServiceResult result = boardService.add(email, boardInput);

        return ResponseResult.success(result);
    }
}

- CommonInterceptor.java

package com.example.jpa.sample.common.interceptor;

import com.auth0.jwt.exceptions.JWTVerificationException;
import com.example.jpa.sample.common.exception.AuthFailException;
import com.example.jpa.sample.util.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Slf4j
public class CommonInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        log.info("#################################");
        log.info("[인터셉터] - preHandler 시작");

        // 요청한 Method, URI 정보
        log.info(request.getMethod());
        log.info(request.getRequestURI());
        
        if(!!validJWT(request)) {
            throw new AuthFailException("인증 정보가 정확하지 않습니다.");
        }

        return true;
    }

    private boolean validJWT(HttpServletRequest request) {
        String token = request.getHeader("X-ACCESS-TOKEN");

        String email = "";
        try {
            email = JwtUtils.getIssuer(token);
        } catch(JWTVerificationException e) {
            return false;
        }

//        request.setAttribute("email", email); 로 데이터를 전달 할 수 있음

        return true;
    }
}

- AuthFailException.java

package com.example.jpa.sample.common.exception;

public class AuthFailException extends RuntimeException {
    public AuthFailException(String message) {
        super(message);
    }
}

- GlobalExceptionHandler.java

package com.example.jpa.sample.common.handler;

import com.example.jpa.sample.common.exception.AuthFailException;
import com.example.jpa.sample.common.model.ResponseResult;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(AuthFailException.class)
    public ResponseEntity<?> AuthFailException(AuthFailException exception) {
        return ResponseResult.fail("[인증 실패]" + exception.getMessage());
    }
}
728x90