공부방

Interceptor 본문

스프링

Interceptor

코딩 화이팅 2023. 4. 17. 15:21
  • HandlerInterceptor(인터페이스)를 구현한 것(또는 HandlerInterorAdapter(클래스)를 상속한 것)
  • 요청(requests)을 처리하는 과정에서 요청을 가로채서 처리
  • 접근 제어(Auth), 로그(Log)등 비즈니스 로직과 구분되는 반복적이고 부수적인 로직 처리

preHandle

postHandle

afterCompletion

위 세가지를 구현했을 때 출력되는 순서들을 보면 인터셉터는 순서가 중요하다.

servlet-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

	<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
	
	<!-- Enables the Spring MVC @Controller programming model -->
	<annotation-driven />

	<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
	<resources mapping="/resources/**" location="/resources/" />

	<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
	<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>
	
	<context:component-scan base-package="com.ssafy.mvc.controller" />
	
	
	<interceptors>
		<interceptor>
			<mapping path="/*"/>
			<beans:bean class="com.ssafy.mvc.interceptor.AInterceptor"/>
		</interceptor>
		<interceptor>
			<mapping path="/*"/>
			<beans:bean class="com.ssafy.mvc.interceptor.BInterceptor"/>
		</interceptor>
		<interceptor>
			<mapping path="/*"/>
			<beans:bean class="com.ssafy.mvc.interceptor.CInterceptor"/>
		</interceptor>
	</interceptors>	
</beans:beans>
===============================================================
HomeContreller.java
package com.ssafy.mvc.controller;

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * Handles requests for the application home page.
 */
@Controller
public class HomeController {
	
	private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
	
	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Locale locale, Model model) {
		logger.info("Welcome home! The client locale is {}.", locale);
		
		Date date = new Date();
		DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
		
		String formattedDate = dateFormat.format(date);
		
		model.addAttribute("serverTime", formattedDate );
		
		return "home";
	}
}
===============================================================
AInterceptor.java
package com.ssafy.mvc.interceptor;

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

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

//이거 인터셉터로 할랭
//1. 구현 : implements HandlerInterceptor 이게 더 나은 버전
//2. 상속 : extends HandlerInterceptorAdapter (지금은 괜춘 / 나중에는 없앨 수 도 있음)
public class AInterceptor implements HandlerInterceptor{
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		
		System.out.println("A : preHandle");
	
		
		return true;
	}
	
	
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		System.out.println("A : postHandle");
	}
	
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		
		System.out.println("A : afterComplection");
	}
}
===============================================================
BInterceptor
package com.ssafy.mvc.interceptor;

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

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

//이거 인터셉터로 할랭
//1. 구현 : implements HandlerInterceptor
//2. 상속 : extends HandlerInterceptorAdapter (지금은 괜춘 / 나중에는 없앨 수 도 있음)
public class BInterceptor implements HandlerInterceptor {
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		System.out.println("B : preHandle");

		return true;
	}

	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		System.out.println("B : postHandle");
	}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {

		System.out.println("B : afterComplection");
	}
}
===============================================================
CInterceptor
package com.ssafy.mvc.interceptor;

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

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

//이거 인터셉터로 할랭
//1. 구현 : implements HandlerInterceptor
//2. 상속 : extends HandlerInterceptorAdapter (지금은 괜춘 / 나중에는 없앨 수 도 있음)
public class CInterceptor implements HandlerInterceptor {
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		System.out.println("C : preHandle");

		return true;
	}

	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		System.out.println("C : postHandle");
	}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {

		System.out.println("C : afterComplection");
	}
}
===============================================================
<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
<title>Home</title>
</head>
<body>

	<h1>Hello world!</h1>
	<P>The time on the server is ${serverTime}.</P>

	
</body>
</html>
//
A : preHandle
B : preHandle
C : preHandle
INFO : com.ssafy.mvc.controller.HomeController - Welcome home! The client locale is ko_KR.
C : postHandle
B : postHandle
A : postHandle
C : afterComplection
B : afterComplection
A : afterComplection

언제 쓰일까?

  1. 인증 및 인가: 인터셉터를 사용하여 사용자의 인증 및 권한을 검사하고, 권한이 없는 사용자에게 액세스 거부 메시지를 반환할 수 있습니다.
  2. 로깅 및 감사: 인터셉터를 이용하여 모든 요청에 대한 로그를 작성하거나, 특정 액션에 대해 감사 기록을 남길 수 있습니다.
  3. 퍼포먼스 측정: 인터셉터를 이용하여 요청 처리 시간을 측정하고, 느린 API 호출에 대한 경고를 발생시킬 수 있습니다.
  4. 요청 및 응답의 가공: 인터셉터를 사용하여 요청 또는 응답 데이터를 수정하거나, 일부 공통 데이터를 추가할 수 있습니다. 예를 들어, 모든 응답에 공통 헤더를 추가하거나, 요청 데이터를 검증 및 정규화할 수 있습니다.
  5. 캐싱 및 압축: 인터셉터를 사용하여 캐싱 및 압축 로직을 추가하여, 성능을 개선하거나 네트워크 대역폭 사용을 줄일 수 있습니다.

간단한 로그인 구현

등록 페이지

나머지는 위 코드와 동일
HomeController.java
package com.ssafy.mvc.controller;

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * Handles requests for the application home page.
 */
@Controller
public class HomeController {
	
	private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
	
	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Locale locale, Model model) {
		logger.info("Welcome home! The client locale is {}.", locale);
		
		Date date = new Date();
		DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
		
		String formattedDate = dateFormat.format(date);
		
		model.addAttribute("serverTime", formattedDate );
		
		return "home";
		
	}
	//regist라고 하는 요청이 들어오면 regist.jsp 로 보내면 좋겠어
	//그런데 요청이 a태그를 이용해서 날아왔어... 
	@GetMapping("regist")
	public String regist() {
		//로그인 유무를 파악해서... 
		//로그인 했으면 계속 고
		//로그인 안했으면 다시 메인으로 돌아가...
		return "regist";
	}
}
===================================================================
home.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
<title>Home</title>
</head>
<body>

	<h1>Hello world!</h1>
	<P>The time on the server is ${serverTime}.</P>
	
<!-- 	regist.jsp로 보내는 버튼을 하나 만들고 싶다. -->
	<a href="regist">regist.jsp 로 가즈아</a>
	
</body>
</html>
===================================================================
regist.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>등록페이지</title>
</head>
<body>
	<h2>등록페이지</h2>
</body>
</html>

로그인 페이지

나머지 코드는 위와 동일
HomeController
package com.ssafy.mvc.controller;

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * Handles requests for the application home page.
 */
@Controller
public class HomeController {
	
	private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
	
	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Locale locale, Model model) {
		logger.info("Welcome home! The client locale is {}.", locale);
		
		Date date = new Date();
		DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
		
		String formattedDate = dateFormat.format(date);
		
		model.addAttribute("serverTime", formattedDate );
		
		return "home";
		
	}
	//regist라고 하는 요청이 들어오면 regist.jsp 로 보내면 좋겠어
	//그런데 요청이 a태그를 이용해서 날아왔어... 
	@GetMapping("regist")
	public String regist() {
		//로그인 유무를 파악해서... 
		//로그인 했으면 계속 고
		//로그인 안했으면 다시 메인으로 돌아가...
		return "regist";
	}
	
	//로그인 요청을 보내는 메서드
	@GetMapping("login")
	public String loginForm() {
		return "login";
	}
	
	//로그인 시도가 들어오면 동작할 메서드
	@PostMapping("login")
	public String login(HttpSession session,String id, String pw) {
		
		//id와 pw를 이용해서 실제 로그인을 해야겠지요.
		//Service를 만들어서 Dao DB
		if(id.equals("ssafy") && pw.equals("1234")) {
			session.setAttribute("id", "ssafy");
			return "redirect:/";
		}
		//아니라면 로그인페이지로 다시가라. 실패했으니까
		return "redirect:/login";
	}
}
=====================================================================
home.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
<title>Home</title>
</head>
<body>

	<c:choose>
		<c:when test="${empty id }">
			<a href="login">로그인 페이지</a>
		</c:when>
		<c:otherwise>
			${id}님 반갑습니다.
		</c:otherwise>
	</c:choose>

	<h1>Hello world!</h1>
	<P>The time on the server is ${serverTime}.</P>
	
<!-- 	regist.jsp로 보내는 버튼을 하나 만들고 싶다. -->
	<a href="regist">regist.jsp 로 가즈아</a>
	
</body>
</html>
=====================================================================
login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
 pageEncoding="UTF-8"%>
 <!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>로그인페이지</title>
</head>
<body>
	<h2>로그인페이지입니다.</h2>
	<form action="login" method="POST">
		<input type="text" name="id"><br>
		<input type="password" name="pw"><br>
		<input type="submit" ><br>
	</form>
</body>
</html>

 

로그아웃

나머지 코드는 위와 동일
HomeController.java
package com.ssafy.mvc.controller;

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * Handles requests for the application home page.
 */
@Controller
public class HomeController {
	
	private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
	
	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Locale locale, Model model) {
		logger.info("Welcome home! The client locale is {}.", locale);
		
		Date date = new Date();
		DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
		
		String formattedDate = dateFormat.format(date);
		
		model.addAttribute("serverTime", formattedDate );
		
		return "home";
		
	}
	//regist라고 하는 요청이 들어오면 regist.jsp 로 보내면 좋겠어
	//그런데 요청이 a태그를 이용해서 날아왔어... 
	@GetMapping("regist")
	public String regist() {
		//로그인 유무를 파악해서... 
		//로그인 했으면 계속 고
		//로그인 안했으면 다시 메인으로 돌아가...
		return "regist";
	}
	
	//로그인 요청을 보내는 메서드
	@GetMapping("login")
	public String loginForm() {
		return "login";
	}
	
	//로그인 시도가 들어오면 동작할 메서드
	@PostMapping("login")
	public String login(HttpSession session,String id, String pw) {
		
		//id와 pw를 이용해서 실제 로그인을 해야겠지요.
		//Service를 만들어서 Dao DB
		if(id.equals("ssafy") && pw.equals("1234")) {
			session.setAttribute("id", "ssafy");
			return "redirect:/";
		}
		//아니라면 로그인페이지로 다시가라. 실패했으니까
		return "redirect:/login";
	}
	
	@GetMapping("logout")
	public String logout(HttpSession session) {
		session.removeAttribute("id");
		return "redirect:/";
	}
}
==================================================================
home.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
<title>Home</title>
</head>
<body>

	<c:choose>
		<c:when test="${empty id }">
			<a href="login">로그인 페이지</a>
		</c:when>
		<c:otherwise>
			${id}님 반갑습니다. <a href="logout">로그아웃</a>
		</c:otherwise>
	</c:choose>

	<h1>Hello world!</h1>
	<P>The time on the server is ${serverTime}.</P>
	
<!-- 	regist.jsp로 보내는 버튼을 하나 만들고 싶다. -->
	<a href="regist">regist.jsp 로 가즈아</a>
	
</body>
</html>

이제 로그인을 하기 전까지 다른 창을 눌러도 로그인 창으로 가게 만들 로그인 인터프리터 생성

다른 코드는 위와 동일
LoginInterceptor.java
package com.ssafy.mvc.interceptor;

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

import org.springframework.web.servlet.HandlerInterceptor;

public class LoginInterceptor implements HandlerInterceptor {
	
	//동작 시키기 이전에 검사를 할 것이기 때문에 preHandle을 사용
	@Override	
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		//요기서 한번 검사를 해야겠다.
		HttpSession session = request.getSession();
		//로그인할때 이렇게 id를 직접 세션에 담아 두었다라고 가정
		if(session.getAttribute("id") == null) {
			response.sendRedirect("login");
			return false;
		}
		return true; //옥헤이 통과	
	}
}
==================================================================
servlet-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

	<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
	
	<!-- Enables the Spring MVC @Controller programming model -->
	<annotation-driven />

	<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
	<resources mapping="/resources/**" location="/resources/" />

	<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
	<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>
	
	<context:component-scan base-package="com.ssafy.mvc.controller" />
<!-- 	일단 빈으로 등록하고 -->
	<beans:bean class="com.ssafy.mvc.interceptor.LoginInterceptor" id="confirm"></beans:bean>
	
	<interceptors>
		<interceptor>
			<mapping path="/*"/>
			<exclude-mapping path="/login"/>
			<exclude-mapping path="/"/>
<!-- 			exclude-mapping : 여기로 가는 건 막아라 -->
			<beans:ref bean="confirm"/>
		</interceptor>
	</interceptors>	
</beans:beans>

등록페이지를 누르면

로그인 페이지로 이동하게 된다.

로그인을 하고 등록페이지를 누르면

등록페이지로 탈 없이 들어가게된다.

그 이유는

logininterceptor에 return true; //옥헤이 통과 코드에서 만약 아이디가 있다면 true로 판단되기 때문에 login창으로 가지 않고 원래대로 등록페이지로 가게된다.

'스프링' 카테고리의 다른 글

MyBatis  (0) 2023.04.19
File Upload&File Download  (0) 2023.04.18
Spring Web MVC  (0) 2023.04.13
Spring AOP  (0) 2023.04.12
스프링 기초  (0) 2023.04.11