728x90
반응형
[Springboot] 30. Intercepter 활용 (인증검사 공통 처리)
1. Intercepter란?
(1) Intercepter의 개념:
-인터셉터는 Spring MVC의 핵심 기능 중 하나로, 웹 애플리케이션에서 공통적인 처리를 재사용할 수 있게 해주는 강력한 도구이다.
(2) Intercepter의 활용 사례:
(3) Intercepter의 작동 원리
(4) filter와 intercepter
2. Intercepter 사용해보기
(1) Intercepter 클래스 생성하기
package com.tenco.bank.handler;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@Component // IoC 대상(싱글톤 패턴)
public class AuthIntercepter implements HandlerInterceptor{
// prehandle 동작 흐름 (단, 스프링부트 설정 파일/클래스에 등록되어야 한다.)
// 컨트롤러 들어오기 전에 동작하는 녀석!
// true --> 컨트롤러 안으로 들여 보낸다.
// false --> 컨트롤러 안으로 못들어감
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return HandlerInterceptor.super.preHandle(request, response, handler);
}
// postHandle
// 뷰가 렌더링 되기 바로 전에 콜백되는 메서드
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
// afterCompletion
// 요청 처리가 완료된 후, 즉 뷰가 완전 렌더링이 된 후에 호출 된다.
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
(2) Controller 전 인증검사 메서드 만들기
package com.tenco.bank.handler;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import com.tenco.bank.handler.exception.UnAuthorizedException;
import com.tenco.bank.repository.model.User;
import com.tenco.bank.utils.Define;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
@Component // IoC 대상(싱글톤 패턴)
public class AuthIntercepter implements HandlerInterceptor{
// prehandle 동작 흐름 (단, 스프링부트 설정 파일/클래스에 등록되어야 한다.)
// 컨트롤러 들어오기 전에 동작하는 녀석!
// true --> 컨트롤러 안으로 들여 보낸다.
// false --> 컨트롤러 안으로 못들어감
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
HttpSession session = request.getSession();
User principal = (User) session.getAttribute(Define.PRINCIPAL);
if(principal == null) {
throw new UnAuthorizedException("로그인 먼저 해주세요.", HttpStatus.UNAUTHORIZED);
}
return true;
}
// postHandle
// 뷰가 렌더링 되기 바로 전에 콜백되는 메서드
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
// afterCompletion
// 요청 처리가 완료된 후, 즉 뷰가 완전 렌더링이 된 후에 호출 된다.
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
(3) WebMvcConfigurer을 구현해 인터셉터 등록하기
package com.tenco.bank.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.tenco.bank.handler.AuthIntercepter;
import lombok.RequiredArgsConstructor;
@Configuration // <-- 하나의 클래스를 IOC 하고 싶다면 사용
@RequiredArgsConstructor
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired // DI
private final AuthIntercepter authIntercepter;
// @RequiredArgsConstructor <-- 생성자 대신 사용 가능!
// 우리가 만든 AuthIntercepter를 등록해야 한다.
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authIntercepter)
.addPathPatterns("/account/**")
.addPathPatterns("/auth/**");
}
}
*AccountController 리팩토링
package com.tenco.bank.controller;
import java.util.Arrays;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttribute;
import org.springframework.web.servlet.ModelAndView;
import com.tenco.bank.dto.DepositDTO;
import com.tenco.bank.dto.SaveDTO;
import com.tenco.bank.dto.TransferDTO;
import com.tenco.bank.dto.WithdrawalDTO;
import com.tenco.bank.handler.exception.DataDeliveryException;
import com.tenco.bank.handler.exception.UnAuthorizedException;
import com.tenco.bank.repository.interfaces.AccountRepository;
import com.tenco.bank.repository.interfaces.HistoryRepository;
import com.tenco.bank.repository.model.Account;import com.tenco.bank.repository.model.HistoryAccount;
import com.tenco.bank.repository.model.User;
import com.tenco.bank.service.AccountService;
import com.tenco.bank.utils.Define;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
@Controller // IoC 대상(싱글톤으로 관리)
@RequestMapping("/account")
@RequiredArgsConstructor
public class AccountController {
private final HttpSession session;
private final AccountService accountService;
/**
* 계좌 생성 페이지 요청
* 주소 설계 : http://localhost:8080/account/save
* @return save.jsp
*/
@GetMapping("/save")
public String savePage() {
return "account/save";
}
/**
* 계좌 생성 기능 요청
* 주소 설계 : http://localhost:8080/account/save
* @param dto
* @return
*/
@PostMapping("/save")
public String saveProc(SaveDTO dto, @SessionAttribute(Define.PRINCIPAL) User principal) {
// 1. form 데이터 추출(파싱 전략)
// 2. 인증 검사
// 3. 유효성 검사
// 4. 서비스 호출
if(dto.getNumber()==null||dto.getNumber().isEmpty()) {
throw new DataDeliveryException(Define.ENTER_YOUR_ACCOUNT_NUMBER, HttpStatus.BAD_REQUEST);
}
if(dto.getPassword()==null||dto.getPassword().isEmpty()) {
throw new DataDeliveryException(Define.ENTER_YOUR_PASSWORD, HttpStatus.BAD_REQUEST);
}
if(dto.getBalance()==null||dto.getBalance()<=0) {
throw new DataDeliveryException(Define.ENTER_YOUR_BALANCE, HttpStatus.BAD_REQUEST);
}
accountService.createAccount(dto,principal.getId());
return "redirect:/account/list";
}
/**
* 계좌 목록 화면 요청
* 주소 설계: http://localhost:8080/account/list ...
* @return list.jsp
*/
@GetMapping({"/list","/"})
public String listPage(Model model,
@RequestParam(name="currentPage", defaultValue="1") Integer currentPage,
@RequestParam(name="totalPage", defaultValue="3") Integer totalPage,
@SessionAttribute(Define.PRINCIPAL) User principal) {
if(currentPage==null) {
currentPage=1;
}
Integer limit=2; // 한 페이지당 내역 수
Integer offset=currentPage; // 몇번부터 뽑을지
// 총 내역 수
Integer totalAccount=(accountService.readAccountListByUserId(principal.getId())).size();
// 총 페이지 수
Integer totalPages=(int)Math.ceil((double)totalAccount/limit);
// 페이지별 내역 수
List<Account> accountList=accountService.readAccountListByUserIdForPaging(principal.getId(),limit,currentPage);
if(accountList.isEmpty()) {
model.addAttribute("accountList",null);
} else {
model.addAttribute("accountList",accountList);
}
model.addAttribute("currentPage", currentPage);
model.addAttribute("totalPages", totalPages);
model.addAttribute("totalPages", totalPages);
// JSP 데이터를 넣어주는 방법
return "account/list";
}
/**
* 출금 페이지 요청
* @return
*/
@GetMapping("/withdrawal")
public String withDrawalPage() {
return "account/withdrawal";
}
/**
* 계좌 출금 요청
* @param dto
* @return
*/
@PostMapping("/withdrawal")
public String withDrawalProc(WithdrawalDTO dto, @SessionAttribute(Define.PRINCIPAL) User principal) {
// 유효성 검사 (자바 코드를 개발) --> 스프링부트 @Valid 라이브러리가 존재한다.
if(dto.getAmount()==null) {
throw new DataDeliveryException(Define.ENTER_YOUR_BALANCE, HttpStatus.BAD_REQUEST);
}
if(dto.getAmount().longValue()<=0) {
throw new DataDeliveryException(Define.W_BALANCE_VALUE, HttpStatus.BAD_REQUEST);
}
if(dto.getWAccountNumber()==null) {
throw new DataDeliveryException(Define.ENTER_YOUR_ACCOUNT_NUMBER, HttpStatus.BAD_REQUEST);
}
if(dto.getWAccountPassword()==null) {
throw new DataDeliveryException(Define.ENTER_YOUR_PASSWORD, HttpStatus.BAD_REQUEST);
}
accountService.updateAccountWithdrawal(dto, principal.getId());
return "redirect:/account/list";
}
/**
* 입금 페이지 요청
*/
@GetMapping("/deposit")
public String depositPage() {
return "account/deposit";
}
/**
*
*/
/**
* 입금 요청 처리
*/
@PostMapping("/deposit")
public String depositProc(DepositDTO dto, @SessionAttribute(Define.PRINCIPAL) User principal) {
// 입금시 검사해야 할 것
// 1. 계좌가 존재하는지
// 2. 돈이 존재하는지
if (dto.getAmount() == null) {
throw new DataDeliveryException(Define.ENTER_YOUR_BALANCE, HttpStatus.BAD_REQUEST);
}
if(dto.getAmount()<=0) {
throw new DataDeliveryException(Define.ENTER_YOUR_BALANCE, HttpStatus.BAD_REQUEST);
}
if(dto.getDAccountNumber()==null) {
throw new DataDeliveryException(Define.ENTER_YOUR_ACCOUNT_NUMBER, HttpStatus.BAD_REQUEST);
}
accountService.updateAccountDeposit(dto, principal.getId());
return "redirect:/account/list";
}
/**
* 이체 페이지 요청
*/
@GetMapping("/transfer")
public String transferPage() {
return "account/transfer";
}
/**
*
*/
/**
* 이체 요청 처리
*/
@PostMapping("/transfer")
public String transferProc(TransferDTO dto, @SessionAttribute(Define.PRINCIPAL) User principal) {
// 입금시 검사해야 할 것
// 1. 계좌가 존재하는지
// 2. 돈이 존재하는지
System.out.println(dto);
// 입금-출금 계좌 중복 여부 확인 확인
if(dto.getDAccountNumber().equals(dto.getWAccountNumber())) {
throw new DataDeliveryException(Define.INVALID_INPUT, HttpStatus.BAD_REQUEST);
}
// 입금 금액 널 확인
if (dto.getAmount() == null) {
throw new DataDeliveryException(Define.ENTER_YOUR_BALANCE, HttpStatus.BAD_REQUEST);
}
// 입금 금액 - 확인
if(dto.getAmount()<=0) {
throw new DataDeliveryException(Define.ENTER_YOUR_BALANCE, HttpStatus.BAD_REQUEST);
}
// 출금 계좌 널 확인
if(dto.getDAccountNumber()==null) {
throw new DataDeliveryException(Define.ENTER_YOUR_ACCOUNT_NUMBER, HttpStatus.BAD_REQUEST);
}
// 입금 계좌 널 확인
if(dto.getWAccountNumber()==null) {
throw new DataDeliveryException(Define.ENTER_YOUR_ACCOUNT_NUMBER, HttpStatus.BAD_REQUEST);
}
// 출금 계좌 비밀번호 널 확인
if(dto.getPassword()==null) {
throw new DataDeliveryException(Define.ENTER_YOUR_PASSWORD, HttpStatus.BAD_REQUEST);
}
accountService.updateAccountTransfer(dto, principal.getId());
return "redirect:/account/list";
}
/**
* 계좌 상세 보기 페이지
* 주소 설계 : http://localhost:800/account/detail/${1}?type=all, deposit, withdraw
* @return
*/
@GetMapping("/detail/{accountId}")
public String detail(@PathVariable(name="accountId") Integer accountId,
@RequestParam(required=false, name="type") String type,
@RequestParam(name="page", defaultValue = "1") int page,
@RequestParam(name="size", defaultValue = "2") int size,
Model model) {
// 유효성 검사
// arrays.asList() -> array 선언과 동시에 초기화해주는 메서드
// 사용자가 선택한 타입(입출금/입금/출금 검색)을 검사한다.
List<String> validTypes=Arrays.asList("all","deposit","withdrawal");
if(!validTypes.contains(type)) {
throw new DataDeliveryException("유효하지 않은 접근입니다.", HttpStatus.BAD_REQUEST);
}
// 페이지 개수를 계산하기 위해서, 총 페이지의 수를 계산해주어야 한다.
int totalRecords=accountService.countHistoryByAccountIdAndType(type, accountId);
int totalPages=(int)Math.ceil((double)totalRecords/size);
// 계좌/내역 정보
Account account = accountService.readAccountById(accountId);
List<HistoryAccount> historyList= accountService.readHistoryByAccountId(type, accountId,page,size);
model.addAttribute("account",account);
model.addAttribute("historyList", historyList);
model.addAttribute("currentPage", page);
model.addAttribute("totalPages", totalPages);
model.addAttribute("type",type);
model.addAttribute("size",size);
return "account/detail";
}
}
728x90
반응형
'Springboot' 카테고리의 다른 글
[Springboot] 32. 사용자 비밀번호 암호화 처리 (0) | 2024.08.13 |
---|---|
[Springboot] 31. DB 마이그레이션 (H2->MySQL) (0) | 2024.08.13 |
[Springboot] 29. 계좌 상세 보기 기능(5단계-계좌 목록 페이징 처리) (0) | 2024.08.12 |
[Springboot] 28. 계좌 상세 보기 기능(5단계-정답 코드) (1) | 2024.08.12 |
[Springboot] 27. 계좌 상세 보기 기능(5단계-JSTL 사용 및 페이징 기능) (0) | 2024.08.12 |