Spring에서의 쿠키와 세션 처리

hoyong.eom·2023년 7월 24일
0

스프링

목록 보기
20/59
post-thumbnail

Spring

쿠키

로그인 상태를 유지하기 위해서는 쿠키를 사용할 수 있다.
쿠키를 사용하지 않는다면 로그인 정보를 매번 Request Parameter로 전달해야한다.

특히, 쿠키의 종류는 영속 쿠키와 세션 쿠키가 있다고 한다.

  • 영속 쿠키 : 만료 날짜를 입력 하면 해당 날짜까지 유지
  • 세션 쿠키 : 만료 날짜를 생략하면 브라우저 종료시 까지만 유지

아래의 코드는 쿠키를 이용해서 로그인 정보를 클라이언트에게 전달하는 코드의 예시이다.

    @PostMapping("/login")
    public String login(@Valid @ModelAttribute LoginForm form, BindingResult bindingResult, HttpServletResponse response) {
        if (bindingResult.hasErrors()) {
            return "login/loginForm";
        }

        Member loginMember = loginService.login(form.getLoginId(), form.getPassword());

        if (loginMember == null) {
            bindingResult.reject("loginFail", "아이디 또는 비밀번호가 맞지 않습니다.");
            return "login/loginForm";
        }

     
        //쿠키에 시간 정보를 주지 않으면 세션 쿠기(브라우저 종료시 모두 종료)
        Cookie idCookie = new Cookie("memberId", String.valueOf(loginMember.getId()));
        response.addCookie(idCookie);
        return "redirect:/";

    }

쿠키를 생성하고 HttpSevletResponse 객체에 쿠키를 추가해서 클라이언트에게 전달한다.

아래의 코드는 @CookieValue 애노테이션을 사용해서 쿠키를 쉽게 조회할 수 있다.

    @GetMapping("/")
    public String homeLogin(@CookieValue(name = "memberId", required = false) Long memberId, Model model) {

        if (memberId == null) {
            return "home";
        }

        //로그인
        Member loginMember = memberRepository.findById(memberId);
        if (loginMember == null) {
            return "home";
        }

        model.addAttribute("member", loginMember);
        return "loginHome";
    }

required = false는 반드시 필요하지 않음을 의미한다. homeLogin 컨트롤러가 호출되면 쿠키값을 파싱해서 알아서 Long memberId에 바인딩된다.

실제로 쿠키를 제거할떄는 SetMaxAge(0)으로 설정해주면 된다.

Cookie cookie = new Cookie(cookieName, null);
       cookie.setMaxAge(0);

    }

쿠키의 문제

쿠키는 심각한 보안 문제를 갖고 있다.

1) 쿠키 값은 임의로 변경이 가능하다.

  • 클라이언트가 쿠키를 강제로 변경할 수 있다.
    2) 쿠키에 보관된 정보는 훔쳐갈 수 있다.
  • 쿠키에는 민감한 정보를 포함해서는 안된다.
    3) 해커가 쿠키를 한번 훔쳐가면 평생 사용할 수 있다.

따라서, 이러한 보안 문제 때문에 쿠키는 세션 Id를 별도로 생성해서 쿠키에 담아 전달한다.
세션Id는 노출되도 상관없는 의미없는 Id를 가리킨다. 실제로는 쿠키를 이용해서 서버와 클라이언트를 연결시켜주는 역할을 한다.

세션

세션은 로그인 정보 대신에 쿠키를 통해서 전달할 클라이언트와 서버와의 연결 변수 값을 의미한다.
하지만 세션은 이전에 사용하던 로그인 정보와 달리 의미없는 값이 때문에 탈취되어도 상관없다.
세션도 관리가 필요한데, 세션은 아래의 3가지 기능을 제공해야한다.
1) 세션 생성

  • 임의의 추정 불가능한 랜덤 값으로 세션 Id 생성
  • 세션 저장소에 세선 Id와 보관할 값(로그인 정보)를 저장
  • 세션 Id로 응답 쿠키를 생성해서 클라이언트로 전달

2) 세션 조회

  • 클라이언트가 요청한 세션Id 쿠키의 값으로 세션 저장소에서 보관한 값 조회
    3) 세션 만료
  • 클라이언트가 요청한 세션Id 쿠키의 값으로 세션 저장소에서 보관한 값 제거

세션은 위 기능을 직접 만들어서 사용할 수 도 있지만, Servlet이 Session 기능을 지원해준다.

주의) Servlet을 사용하는 경우 테스트가 쉽지 않을 수 있는데, MockHttpServletRequest와 MockHttpServletResponse 객체를 사용하면 된다.

서블릿 Http 세션

세션이라는 개념은 대부분의 웹 애플리케이션에서 공통으로 필요하기 때문에 서블릿은 세션을 위해서 HttpSession이라는 기능을 제공한다고 한다.

Http Session

서블릿이 제공하는 HttpSession도 이전에 정리했던 3가지 세션 관리 기능을 수행한다.
서블릿을 통해서 HttpSession을 생성하면 아래와 같은 이름의 쿠키를 생성한다고 한다.

Cookie: JSESSIONID=5B78E23B513F50164D6FDD8C97B0AD05

아래의 코드는 HttpSession을 이용해서 세션을 만드는 코드이다.

    @PostMapping("/login")
    public String loginV3(@Valid @ModelAttribute LoginForm form, BindingResult bindingResult, HttpServletRequest request) {
        if (bindingResult.hasErrors()) {
            return "login/loginForm";
        }

        Member loginMember = loginService.login(form.getLoginId(), form.getPassword());

        if (loginMember == null) {
            bindingResult.reject("loginFail", "아이디 또는 비밀번호가 맞지 않습니다.");
            return "login/loginForm";
        }

        
        //세션이 있으면 있는 세션 반환, 없으면 신규 세션을 생성
        HttpSession session = request.getSession();
        session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);

        return "redirect:/";

    }

위 코드에서 HttpSession이 사용된걸 볼 수 있는데, HttpSerlvetRequest의 getSession 함수는 세션이 있으면 반환하고 없으면 신규 세션을 만들어서 전달한다.
그리고 setAttribute 함수를 통해서 세션 저장소에다가 로그인 정보를 저장하는걸 볼 수 있다.
그리고 세션 만료를 위해서는 session.invalidate() 함수를 사용해서 세션을 제거한다.

@SessionAttribute

@SessionAttribute는 서블릿 Session을 좀 더 편리하게 사용할 수 있도록 도와준다.

        HttpSession session = request.getSession(false);
        if (session == null) {
            return "home";
        }

        Member loginMember = (Member)session.getAttribute(SessionConst.LOGIN_MEMBER);

위 코드는 일반적으로 세션을 조회해서 세션에서 객체를 만들어내는걸 확인할 수 있다.
다만 이 코드 자체가 중복되고 복잡해지기 때문에 @SessionAttribute 애노테이션으로 위 코드를 제거할 수 있다.

    public String homeLoginV3Spring(
            @SessionAttribute(name = SessionConst.LOGIN_MEMBER, required = false) Member loginMember, Model model) {

        if (loginMember == null) {
            return "home";
        }

        model.addAttribute("member", loginMember);
        return "loginHome";
    }

위 코드는 @SessionAttribute 애노테이션을 통해서 실제 HttpSession에서 조회 후 아래의 객체를 자동으로 만들어준다. 물론, 이 기능으로 세션을 생성할 수는 없다.

세션 타임아웃 설정

아래의 코드는 스프링 부트로 글로벌 설정 내역입니다.(application.properties)
#server.servlet.session.tracking-modes=cookie
#server.servlet.session.timeout=60

세션에서는 타임아웃 시간을 설정할 수 있는데, 세션의 lastAccessTime을 기준으로 타임아웃 시간이 되지 않으면 자동으로 세션 시간이 갱신된다.

참고

해당 포스팅은 아래의 강의를 공부하여 정리한 내용입니다.
김영한님의 SpringMVC2-로그인 요구사항

0개의 댓글