Spring

MVC2 - 로그인(쿠키, 세션)

코드파고 2022. 10. 3. 21:14
도메인이란?
화면, UI, 기술 인프라 등등의 영역을 제외한 시스템이 구현해야 하는 핵심 비즈니스 업무 영역
웹은 도메인에 의존하지만, 도메인은 웹에 유지하지 않는다.

 

쿠키의 사용 배경

HTTP 는 Stateless Protocol

그러므로 현재 로그인에 성공 시 회원임을 검증할 수 있는 쿠키를 만들어 웹 브라우저에 전달해 주어야 한다.

모든 요청에 쿠키를 포함하여 로그인이 된 상태인지 확인하자!

 

쿠키의 종류

세션 쿠키 : 브라우저 종료시까지만 유지(만료 날짜 생략 시)

영속 쿠키 : 만료 날짜까지 쿠키 유지

 

쿠키로 로그인/로그아웃 구현

쿠키 만들기(로그인) 🍪 

로그인에 성공할 시에 쿠키를 만들어 주면 되겠다!

@PostMapping("/login")
public String login(@Validated @ModelAttribute LoginForm form, BindingResult bindingResult, HttpServletResponse response) {
    ...
    // 쿠키 넣어주기
    // 세션 쿠키 (쿠키에 시간 정보를 주지 않았으므로 )
    Cookie idCookie = new Cookie("memberId", String.valueOf(loginMember.getId()));
    response.addCookie(idCookie);

    return "redirect:/";
}

 

쿠키 만료(로그아웃) 🍪💥

@PostMapping("/logout")
public String logout(HttpServletResponse response) {
    expireCookie(response, "memberId");
    return "redirect:/";
}

문제점

하지만 위의 방식(쿠키만으로 로그인 구현) 은 보안 이슈가 있다

쿠키 값은 조작이 가능하기 때문에 의미가 있는 정보를 쿠키에 담게 되면 쿠키에 보관된 정보를 훔쳐갈 수 있다.

 

해결 방법

쿠키를 탈취해서 해당 정보로 계속 로그인해올 가능성이 있다.

1. 그러므로 쿠키에 중요한 정보를 넣지 말고, 임의의 값(토큰)을 넣어 주자!

2.  토큰의 유지시간을 짧게(30분) 유지한다

 

서버-세션의 개념 적용하기

세션 저장소에 UUID를 생성해 이를 키(세션 ID)로 잡고, 세션 저장소에 값을 저장한다

이 UUID를 쿠키에 담아 웹 브라우저에 보내준다.

브라우저는 쿠키 저장소에 쿠키를 저장하고, 쿠키 정보(세션ID)로 세션 저장소를 조회해서 보관된 정보를 사용한다.

 

세션 원리 적용해보기

UUID로 세션의 키 값이 되는 SessionID를 만들어 주고, 이를 Map에 넣어 준 후 SessionId로 회원을 반환받도록 하자

세션 생성

public void createSession(Object value, HttpServletResponse response) {
    // 세션 ID 생성, 값을 세션에 저장
    String sessionId = UUID.randomUUID().toString();
    sessionStore.put(sessionId, value);

    // 쿠키 생성
    Cookie mySessionCookie = new Cookie(SESSION_COOKIE_NAME, sessionId);// 상수로 만들기 ctrl+alt+c
    response.addCookie(mySessionCookie);
}

세션 조회

public Object getSession(HttpServletRequest request) {
    Cookie cookie = findCookie(request, SESSION_COOKIE_NAME);
    if (cookie == null) {
        return null;
    }
    return sessionStore.get(cookie.getValue());
}

public Cookie findCookie(HttpServletRequest request, String cookieName) {
        Cookie[] cookies = request.getCookies();
        if (cookies == null) {
            return null;
        }
        return Arrays.stream(cookies)
                .filter(cookie -> cookie.getName().equals(cookieName))
                .findAny()
                .orElse(null);
    }

세션 만료

public void expire(HttpServletRequest request) {
    Cookie cookie = findCookie(request, SESSION_COOKIE_NAME);
    if (cookie != null) {
        sessionStore.remove(cookie.getValue());
    }
}

 

HttpSession 이용하기

세션 생성

HttpServletRequest를 필요로 한다

 public String homeLoginV3(HttpServletRequest request, Model model) {
        // Guest도 접근이 가능해야 함
        HttpSession session = request.getSession();
getSession(default = true)
true 일 경우, 세션을 신규 생성
false 일 경우 세션이 존재할 경우 해당 세션을 반환

 

세션 삭제

HttpSession session = request.getSession(false);
if (session != null) {
    session.invalidate();
}

 

세션 조회

Controller상에서의 SessionAttribute 사용

@GetMapping("/")
public String homeLoginV3Spring(@SessionAttribute(name = SessionConst.LOGIN_MEMBER, required = false) Member member, Model model) {
    // Guest도 접근이 가능해야 함
    if (member == null) {
        return "home";
    }
    // 세션이 유지되면 로그인으로 이동
    model.addAttribute("member", member);
    return "loginHome";
}

 

처음 로그인할 때 세션ID 값이 함께 출력되어 나올 때 이를 방지하는 방법

출력되는 이유는 브라우저가 쿠키를 지원하지 않을 수 있기 때문이다

application.properties 에 한 줄 추가

server.servlet.session.tracking-modes=cookie

 

세션 정보와 타임아웃

세션이 종료되는 경우 : session.invalidate() - 로그아웃시 사용

그러나 유저는 직접 로그아웃 하기보다 브라우저를 종료하게 된다.

세션이 종료되지 않은채로 유지되어 세션이 많아지면 메모리를 많이 사용하게 될 수 밖에 없다

 

👉그래서! 타임아웃🕑을 설정

최근 요청시간을 기준으로 30분 정도 유지

1. 글로벌 적용

server.servlet.session.timeout=120 // 분 단위

2. 특정 세션에 적용

session.setMaxInactiveInterval(1800);