[Spring] 세션, 쿠키, 인터셉터

Fortice·2021년 2월 25일
2

Spring

목록 보기
11/13
post-thumbnail

쿠키 , 세션

1. 사용 이유

Stateless한 HTTP 프로토콜의 특징과 대비되는 Stateful한 상태를 위해 사용하는 것이다. 간단히 이전 상태를 유지하고 있는 것이다.

HTTP 특징

  • Connectionless 프로토콜 (비연결지향)

    • 클라이언트가 서버에 요청을 했을 때, 그 요청에 맞는 응답을 보낸 후 연결을 끊는 처리방식이다.
    • HTTP 1.1 버전에서 연결을 유지하고, 재활용 하는 기능이 Default 로 추가되었다. (keep-alive 값으로 변경 가능)
  • Stateless 프로토콜 (상태정보 유지 안함)

    • 클라이언트의 상태 정보를 가지지 않는 서버 처리 방식이다.
    • 클라이언트와 첫번째 통신에서 데이터를 주고 받았다 해도, 두번째 통신에서 이전 데이터를 유지하지 않는다.

이러한 HTTP의 특징으로 인해 페이지를 옮길 때마다 로그인을 다시 해야하거나, 사용자가 상품을 선택했는데 구매 페이지에서 선택한 상품의 정보가 없는 경우가 생긴다. 따라서 상태를 유지해 이런 불편함을 없앤다.

2. 쿠키와 세션의 차이점

쿠키(Cookie)세션(Session)
저장 위치클라이언트(=접속자 PC)웹 서버
저장 형식text (key-value)Object
만료 시점쿠키 저장시 설정(만료 시간 기준)브라우저 종료시 삭제(기간 지정 가능)
사용하는 자원(리소스)클라이언트 리소스웹 서버 리소스
용량 제한총 300개
하나의 도메인 당 20개
하나의 쿠키 당 4KB(=4096byte)
서버가 허용하는 한 용량제한 없음.
속도세션보다 빠름쿠키보다 느림
보안세션보다 안좋음쿠키보다 좋음

쿠키는 아래 사진과 같이 클라이언트 측에서 확인이 가능하지만, 세션은 서버에 저장하므로 확인할 수 없어 보안측면에서 좋다.

참고로 세션은 사용자를 위와 같이 JSESSIONID 같은 클라이언트마다의 고유 ID를 부여하여 요청 시 쿠키의 값을 확인하여 구분한다.

3. Spring Session 설정

Spring에서 세션은 컨트롤러에 HttpSession을 이용해 처리한다.

Spring Web MVC에서는 HttpSession을 주입해야 할 때, 내부적으로 Servlet Container에게 Session을 획득하는데, 이는 아래의 두 가지 방법으로 나뉜다.

  1. 컨트롤러에서 @Autowired를 통해 주입받는다.(setAttribute같은 API 사용 시 요청/생성)
  2. 요청 매핑 어노테이션 적용 메서드에 HttpSession 파라미터를 추가한다.
  3. 요청 매핑 어노테이션 적용 메서드에 HttpServletRequest 파라미터를 추가하고, HttpServletRequest를 이용해서 HttpSession을 구한다.
  4. @SessionAttribute, @ModelAttribute로 주입받는다.

1번 방법 : @Autowired로 주입받기

setAttribute, getAttribute 등 메서드 호출 시 session 요청/생성

@Controller
public class HelloController {
	@Autowired
    private HttpSession session;
	
	@PostMapping("/hello2")
    public String submit(CommandObj cmdObj, Errors errors) {
    	session.setAttribute("name", Object);
		return "test";
	}
}

HelloController와 HttpSession의 스코프 차이 때문에 Spring이 HttpSession 인스턴스를 동적 프록시로 생성하여 주입해준다. 이것을 Scoped Proxy라고 한다. LoginController는 singleton scope를 가지며, HttpSession은 session scope를 가진다.

2번 방법 : 메서드에서 주입받기(HttpSession)

from 메서드 호출 시 session 요청/생성

@PostMapping
public String form(CommandObject cmdObj, Errors errors, HttpSession session) {
    session.setAttribute("name", Object);
    Object obj = session.getAttribute("name");
    session.removeAttribute("name");
    boolean isNew = session.isNew();
    session.invalidate(); //세션 삭제(로그아웃)
}

3번 방법 : 메서드에서 주입받기(HttpServletRequest)

@PostMapping
public String form(CommandObject cmdObj, Errors errors, HttpServletRequest req) {
    HttpSession session = req.getSession();
    // session 사용
}

4번 방법 : @SessionAttribute, @ModelAttribute로 주입받기

이 방식은 Session에 이미 저장된 데이터를 조회할 때 적합하고, 메서드를 호출할 때 Session이 요청/생성된다.

  1. @SessionAttributes를 컨트롤러에 추가함으로써 세션 단위로 스코프를 지정하도록 명시한다.
  2. @ModelAttribute을 선언함으로써 Session에서 "hello"라는 이름을 가진 오브젝트를 조회합니다. 그리고 Object형태로 변환해 주입해줍니다.
@Controller
@SessionAttribute("hello")
public class HelloController {
	@PostMapping("/hello")
    public String submit(Model model, @ModelAttribute("hello") Object obj) {
        model.addAttribute("hello", obj.helloEvery());
		return "test";
	}
}

4. Spring 쿠키 설정

스프링에서 쿠키는 @CookieValue 어노테이션을 사용하여 요청 매핑 어노테이션 적용 메서드에 Cookie 타입 파라미터에 적용하여 사용 가능하다.

@GetMaaping
public String form(
    LoginCommand loginCommand,
    @CookieValue(value = "cookie_name", required = false) Cookie rCookie
) {
    //Cookie 값 얻기, 설정
    String cValue = rCookie.getValue();
    rCookie.setValue("New Value");
    rCookie.setPath("/");
    rCookie.setMaxAge(60 * 60 * 24 * 30);
    rCookie.setSecure(true);
    rCookie.setHttpOnly(true);
    boolean cookieisHttpOnly = rCookie.isHttpOnly();
}

Session은 Request를 받아 서버에 저장했다면, Cookie는 클라이언트로 Response를 주어야 한다.HttpServletResponse를 통해 설정 가능하다.

@GetMaaping
public String form( LoginCommand loginCommand, HttpServletResponse res) {
	Cookie myCookie = new Cookie("cookie_name", "value");
    //Cookie 값 얻기, 설정
    //설정 생략
    res.addCookie(myCookie);
}

인터셉터

1. 인터셉터란?

인터셉터는 컨트롤러로 들어오는 HTTPRequest와 컨트롤러가 응답하는 HTTPResponse를 가로채는 역할을 한다.

특정 권한이 필요한 페이지 요청 시 권한이 있는지를 확인하거나, 세션이 만료되었는지 확인하는 등의 용도로 사용한다. 책에서는 여러 페이지에서 로그인이 되었는지 확인해야하는데, 이와 같이 다수의 컨트롤러에 대해 동일한 기능을 적용해야 할 때 사용한는 것으로 서술한다.

AOP와의 차이

위 설명만 보면 AOP와 같은 것 아닌가 하는 생각이 든다.

그래서 AOP, 인터셉터, 추가로 알아보다 나온 Filter의 차이점을 알고 넘어간다.

HTTP 요청 흐름

차이점

  • 실행 위치
    • Filter, 인터셉터는 서블릿 단위에서 실행되고, AOP는 메소드 앞에 Proxy패턴의 형태로 실행된다.
    • Filter는 DispathcerServlet 전, 인터셉터는 DispathcerServlet 후에 실행된다.
    • Filter - 인터셉터 - AOP 순이다.
  • 역할
    • Filter
      • 요청과 응답을 거른뒤 정제하는 역할
      • 인코딩 변환 처리, XSS방어 등의 요청/응답에 대한 변경/확인 작업이 주가 된다.
      • 스프링 외부에 존재하여 스프링과 무관한 자원에 대해 동작한다
      • 주소로 대상을 구분
      • web.xml에 설정한다
    • 인터셉터
      • 요청에 대한 작업 전/후로 가로챈다
      • Controller(Handler)에 관한 요청과 응답에 대해 처리
      • 스프링 내부에 존재하여 스프링의 모든 빈 객체에 접근 가능하다
      • 로그인 체크, 권한체크, 프로그램 실행시간 계산작업 로그확인 등의 작업을 처리
      • 주소로 대상을 구분
    • AOP
      • 메소드 전후의 지점에 자유롭게 설정
      • 로깅, 트랜잭션, 에러 처리 등 비즈니스단의 메서드에서 조금 더 세밀하게 조정하고 싶을 때 사용
      • 주소, 파라미터, 애노테이션 등 다양한 방법으로 대상을 지정

2. 인터셉터 구현(HandlerInterceptor)

스프링에서 HandlerInterceptor 인터페이스를 구현해서 세 가지 시점에 인터셉터 처리가 가능하다.

  • 컨트롤러(핸들러) 실행 전
    • boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) throws Exception;
  • 컨트롤러(핸들러) 실행 후, 아직 뷰를 실행하기 전
    • boolean postHandle(HttpServletRequest req, HttpServletResponse res, Object handler, ModelAndView modelAndView) throws Exception
  • 뷰를 실행한 이후
    • void AfterCompletion(HttpServletRequest req, HttpServletResponse res, Object handler, Exception ex) throws Exception

preHandle() 메서드는 false 리턴 시 다음 컨트롤러를 실행하지 않는다.

postHandle() 메서드는 컨트롤러 익셉션 발생 시 실행하지 않는다.

afterCompletion() 메서드는 뷰가 클라이언트에 응답을 전송한 뒤에 실행된다. 컨트롤러에서 익셉션 발생 시 ex 파라미터에 전달되고, 발생하지 않으면 null이 전달된다. 컨트롤러 실행 후 로그를 남기거나, 실행 시 간을 기록하는 등의 후처리를 하기 위한 메서드이다.

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

import org.springframework.web.servlet.HandlerInterceptor;

public class AuthCheckInterceptor implements HandlerInterceptor {

	@Override
	public boolean preHandle( HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    		// 세션 획득 후 확인
		HttpSession session = request.getSession(false);
		if (session != null) {
			Object authInfo = session.getAttribute("authInfo");
			if (authInfo != null) {
				return true;
			}
		}
        	//세션이 비어있다면 or authInfo 속성이 없다면, 리다이렉트 후 false 리턴으로 컨트롤러가 실행되지 않도록 함.
		response.sendRedirect(request.getContextPath() + "/login");
		return false;
	}

}

3. 인터셉터 적용

인터셉터는 Mvc 설정 클래스에 추가하여 적용한다.

  1. 인터셉터를 빈으로 등록
  2. WebMvcConfigureraddInterceptors() 메서드로 인터셉터 설정
    2-1. InterceptorRegister 객체의 addInterceptor() 메서드로 등록한 인터셉터를 설정
    2-2. InterceptorRegister 객체의 addPathPatterns("ant 패턴")으로 인터셉트할 url 설정
@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {

    ... 생략
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authCheckInterceptor())
        .addPathPatterns("/edit/**")
        .excludePathPatterns("/edit/help/**");
    }

    @Bean
    public AuthCheckInterceptor authCheckInterceptor() {
        return new AuthCheckInterceptor();
    }
}

Ant 패턴

  1. * : 0개 또는 그 이상의 글자
  2. ? : 1개 글자
  3. ** : 0개 또는 그 이상의 폴더 경로

example : "/profile/**/user_?*.jpg"

profile
서버 공부합니다.

0개의 댓글