세션, 로그인

바그다드·2023년 5월 17일
0

세션과 쿠키

  • 먼저 쿠키에 대한 기본적인 지식은 쿠키를 참조하자
    쿠키는 사용자의 pc에 저장되는 데이터이기 때문에 탈취를 당하는 등의 보안적인 문제가 있다. 따라서 사용자의 예민한 정보 같은 것들은 세션을 이용해서 서버에 저장해야 한다.

세션

  • 세션은 서버에 저장되는 값으로 로그인 시에 쿠키로 보내기에는 예민한 정보를 서버의 세션이라는 저장소에 저장을 한다. 작동 방식은 다음과 같다
  1. 사용자가 로그인을 시도
  2. 서버에서는 db에서 일치하는 회원정보를 탐색
  3. 일치하는 정보가 있다면 세션을 생성
  4. 쿠키에 세션 id를 담아 사용자에게 전달
  5. 사용자는 다음 요청에 쿠키를 함께 전달
  6. 서버는 쿠키에 들어있는 세션id와 세션에 저장되어 있는 세션id를 대조하여 로그인 유지
  • 여기서 중요한 점은 세션 id를 생성할 때 예측할 수 없는 값을 생성해야 한다는 것이다.
    예를 들어 세션id를 번호 순서대로 1,2,3...등으로 생성해버리면 사용자가 악의적으로 세션id를 추측해서 다른 사용자의 정보를 빼가거나 악의적인 요청을 보낼 수 있다.
    따라서 UUID등을 이용해서 고유 번호를 생성하고 이 번호를세션id로 활용하자.
    그럼 이제 코드로 로그인 기능을 구현해보자

1. 로그인 / 로그아웃

    @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";
        }

        //로그인 성공 처리 TODO
        // 세션이 잇으면 있는 세션 반환, 없으면 신규 세션 생성
        // getSession(true) - 있으면 기존 세션 반환, 없으면 생성해서 반환
        // getSession(false) - 있으면 기존 세션 반환, 없으면 null반환
        HttpSession session = request.getSession();
        // 세션에 로그인 회원 정보 보관
        session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);

        return "redirect:/";

    }
    
    @PostMapping("/logout")
    public String logoutV3(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            session.invalidate();
        }
        return "redirect:/";
    }

로그인

  • 사실 위에서 UUID등을 이용하여 고유한 세션id를 사용하자고 했는데, request객체의 getSession() 메서드를 사용하면, 세션id를 생성하고 이를 쿠키에 담아 응답을 보내주는 것까지 처리해준다.
    이때 getSession()에는 true false값이 들어갈 수 있는데,
    - getSession(true) - 기존에 생성한 세션이 있으면 기존 세션 반환, 없으면 새로 생성해서 반환
    - getSession(false) - 있으면 기존 세션 반환, 없으면 null반환

로그아웃

  • 로그아웃 기능이기 때문에 세션이 존재하지 않는 경우에 세션을 새로 생성해서는 안되므로 getSession(false)로 설정해주자.
  • invalidate() 메서드를 사용하면 세션 저장소에서 해당 세션을 제거한다.

2. 로그인 확인하기

  • 그럼 이제 사용자가 로그인을 했는지 안했는지 확인을 해보자.
//    @GetMapping("/")
    public String homeLogin(HttpServletRequest request, Model model) {

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

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

        // 세션에 회원 데이터가 없으면 home
        if (loginMember == null) {
            return "home";
        }

        // 세션이 유지되면 로그인으로 이동
        model.addAttribute("member", loginMember);
        return "loginHome";
    }

    @GetMapping("/")
    public String homeLoginSpring(
            @SessionAttribute(name = SessionConst.LOGIN_MEMBER, required = false) Member loginMember, Model model) {

        // 세션에 회원 데이터가 없으면 home
        if (loginMember == null) {
            return "home";
        }

        // 세션이 유지되면 로그인으로 이동
        model.addAttribute("member", loginMember);
        return "loginHome";
    }
  • 처음 메소드는 직접 session값을 가져와서 검증하였고, 두번째 메서드는 @SessionAttribute를 사용하여 세션 값을 가져왔다.
  • SessionConst.LOGIN_MEMBER는 세션 이름을 통일하기 위해 static변수를 사용한 것이다.
  • @SessionAttribute를 사용하면
    1. 세션을 request에서 직접 찾아서
    2. Member객체에 세션의 값(value)를 담는 번거로움을 편리하게 해준다!!!

세션 정보 확인하기

@GetMapping("/session-info")
    public String sessionInfo(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        if (session == null) {
            return "세션이 없습니다";
        }

        // 세션 데이터 출력
        session.getAttributeNames().asIterator()
                .forEachRemaining(name -> log.info("session name={}, value={}", name, session.getAttribute(name)));
        log.info("sessionId={}", session.getId());
        // 세션 유효 시간
        log.info("getMaxInactiveInterval={}", session.getMaxInactiveInterval());
        log.info("creationTime={}", new Date(session.getCreationTime()));
        log.info("lastAccessedTime={}", new Date(session.getLastAccessedTime()));
        log.info("isNew={}", session.isNew());

        return "세션 출력";
    }
  • getAttributeNames()
    세션의 키 값들을 가져온다.
  • getId()
    세션의 id
  • getMaxInactiveInterval()
    세션의 유효시간
  • getCreationTime()
    세션 생성 시간
  • getLastAccessedTime()
    최근에 사용자가 서버에 요청한 시간
    서버로 요청할 때마다 갱신된다.
  • isNew()
    세션이 새로 생성된 세션이면 true
    클라이언트에서 서버로 세션id를 조회해서 가져온 세션이면 false

타임아웃

  • 우리가 구현한 로그아웃 기능은 사용자가 로그아웃 버튼을 클릭해야 동작한다. 그런데 보통 사용자는 직접 로그아웃을 하지 않고, 브라우저를 종료하는 경우가 많다. 웹는 기본적으로 비연결성을 지향한다. 따라서 서버에서는 사용자가 브라우저를 종료했는지 확인을 할 수 없다. 그럼 어떤 문제가 발생할까?
  1. 세션 데이터가 서버에 계속 누적된다.
  2. 쿠키를 탈취당했을 경우 이 쿠키로 언제든지 악의적인 요청을 보낼 수 있다.
  • 그럼 이 문제를 어떻게 해결하는 것이 좋을까?
    세션을 특정 시간마다 제거하면 된다.
    만약 생성 시간을 기준으로 30분마다 제거를 하면 30분마다 로그인을 해야하는 번거로움이 있다.
    반면에 사용자의 최근 요청 시간을 기준으로 30분동안 세션을 유지하면, 사용자가 사용중에 30분마다 로그인을 해야하는 번거로움은 해결된다.
    그럼 타임아웃 설정을 해보자.

타임아웃 설정

글로벌 설정

server.servlet.session.timeout=60
  • application.properties에 해당 값을 입력하면 세션 타임아웃을 설정할 수 있다.
    - 글로벌 설정은 분 단위로 설정해야 한다!!!(60초(1분), 120초(2분) ...)

개별 설정

  • 만약에 세션별로 다른 유효기간을 설정하고 싶다면 세션을 생성할 때 아래의 코드를 사용하면 된다.
session.setMaxInactiveInterval(1800); //1800초
profile
꾸준히 하자!

0개의 댓글