ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spring MVC - Session을 이용한 로그인 처리 (HttpSession, @SessionAttribute), 세션 정보 조회
    Spring/Spring MVC 2022. 2. 8. 22:28
    728x90
    반응형

     

     

     

     

     

    Session을 이용한 로그인 처리

     

     

     

     HttpSession은 이미 이전에 직접 구현한 Session 기능과 거의 같은 방식으로 제공해준다. 쿠키 이름은 JSESSIONID이며 값은 추정 불가능한 랜덤 값으로 되어있다. HttpSession을 이용하여 로그인 처리를 구현해보자.

     

     

     

     

     

     

     

    로그인 했을때 로그인 안했을때 메인 화면

     

     

     

    HttpSession을 이용한 로그인 처리

     

     

    Session에 사용할 상수 

     

    • HttpSession에 데이터를 보관하고 조회할 때 같은 이름이 중복되어 사용되므로 상수를 정의

     

    public class SessionConst {
        public static final String LOGIN_MEMBER = "loginMember";
    }

     

    세션 생성과 조회 - 로그인

     

    • HttpSession은 HttpServletRequest를 통해 가져올 수 있다.
           - HttpSession session = request.getSession()
    • 세션 생성 : request.getSession
           - request.getSession(true) : 세션이 있으면 기존 세션 반환
                                               세션이 없으면 새로운 세션을 생성해서 반환
                                               default
           - request.getSession(false) : 세션이 있으면 기존 세션 반환
                                              : 세션이 없으면 null을 반환
    • 세션 저장소에 저장 : session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember)

     

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

     

    세션 제거 - 로그아웃

     

    • session.invalidate() : 세션 제거
            - 세션 저장소에서 해당 세션 제거
    • request.getSession(false) : 세션이 없을 경우 메모리 낭비를 줄이기 위해 false로 설정

     

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

     

     

    더보기
    @Slf4j
    @Controller
    @RequiredArgsConstructor
    public class LoginController {
    
        private final LoginService loginService;
        private final SessionManager sessionManager;
    
        @GetMapping("/login")
        public String loginForm(@ModelAttribute("loginForm") LoginForm form) {
            return "login/loginForm";
        }
    
        @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:/";
    
        }
    
        @PostMapping("/logout")
        public String logoutV3(HttpServletRequest request) {
            HttpSession session = request.getSession(false);
            if (session != null) {
                session.invalidate();
            }
            return "redirect:/";
        }
    
    }

     

    로그인 상태에 따른 메인 화면 처리

     

    • request.getSession(false) : 세션이 없을 경우 메모리 낭비를 줄이기 위해 false로 설정

     

    @Slf4j
    @Controller
    @RequiredArgsConstructor
    public class HomeController {
    
        private final MemberRepository memberRepository;
        private final SessionManager sessionManager;
    
        @GetMapping("/")
        public String homeLoginV3(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";
        }
    
    }

     


     

     

    @SessionAttribute을 이용한 로그인 처리

     위에 HttpSession을 사용하려면 HttpServletRequest에서 가져오고 조회하는 여러 처리가 필요하다. 이런 처리 없이 더 간편하게 @SessionAttribute를 이용하여 세션을 쉽게 조회할 수 있다.

     

     단 주의할 점은 @SessionAttribute는 세션을 생성하지 않는다. 따라서 세션이 존재하지 않을 경우 loginMember = null 이 된다.

     

     

    public String homeLoginV3Spring(
            @SessionAttribute(name = SessionConst.LOGIN_MEMBER, required = false) Member loginMember, Model model) {
    
        //세션에 회원 데이터가 없으면 home
        if (loginMember == null) {
            return "home";
        }
    
        //세션이 유지되면 로그인으로 이동
        model.addAttribute("member", loginMember);
        return "loginHome";
    }

     

     

    세션 정보 조회

     

    • getId()                         : JSESSIONID 값
    • getMaxInactiveIntervel()   : 세션의 유효 기간
    • getCreationTime()           : 세션 생성일시
    • getLastAccessedTime()     : 세션과 연결된 사용자가 최근에 서버에 접근한 시간(반환 타입 Date)
    • isNew()                         : 새로 생성된 세션인지, 이전에 만들어졌는지 판별

     

     

    @Slf4j
    @RestController
    public class SessionInfoController {
    
        @GetMapping("/session-info")
        public String sessionInfo(HttpServletRequest request) {
            HttpSession session = request.getSession(false);
            if (session == null) {
                return "세션이 없습니다.";
            }
            // 세션 id와 저장된 객체 정보 출력
            System.out.println(session.getId() + ", " + session.getAttribute("loginMember"));
    
            //세션 데이터 출력
            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 "세션 출력";
    
        }
    }


     

    타임 아웃 설정

     

     

     세션은 사용자가 로그아웃을 직접 호출해서 session.invalidate() 가 호출 되는 경우에 삭제된다. 그런데 대부분의 사용자는 로그아웃을 선택하지 않고, 그냥 웹 브라우저를 종료한다. 문제는 HTTP가 비 연결성(ConnectionLess)이므로 서버 입장에서는 해당 사용자가 웹 브라우저를 종료한 것인지 아닌지를 인식할 수 없다. 따라서 서버에서 세션 데이터를 언제 삭제해야 하는지 판단하기가 어렵다.

     

     이 경우 세션 정보를 담고 있는 쿠키를 탈취 당했을 경우 해당 쿠키로 악의적인 접근이 가능하며 또한 서버에서 세션이 삭제되지않고 계속 남게되면 메모리 문제가 생긴다. 

     

     이러한 문제를 해결하기위해 세션은 종료 시점을 최근에 요청한 시간을 기준으로 30분이 지나면 삭제되도록 설정되어 있다. 만약 세션 타임아웃 설정을 다르게 하고 싶으면 다음 2가지 방법이 있다

     

    application.properties 글로벌 설정 (default 값은 1800(30분))

    server.servlet.session.timeout=60

     

    특정 세션 단위로 시간 설정

    session.setMaxInactiveInterval(1800);

     

     이와 같이 설정을 하게 되면 최근에 요청한 시간을 기준으로 설정한 시간이 지나면 세션이 삭제가 된다.

     


     

    참고
    TrackingModes

     로그인을 처음 시도하면 URL이 다음과 같이 jsessionid를 포함하고 있는 것을 확인할 수 있다

     

     이것은 웹 브라우저가 쿠키를 지원하지 않을 때 쿠키 대신 URL을 통해서 세션을 유지하는 방법이다. 이 방법을 사용하려면 URL에 이 값을 계속 포함해서 전달해야 한다. 타임리프 같은 템플릿은 엔진을 통해서 링크를 걸면 jsessionid 를 URL에 자동으로 포함해준다. 서버 입장에서 웹 브라우저가 쿠키를 지원하는지 하지 않는지 최초에는 판단하지 못하므로, 쿠키 값도 전달하고, URL에 jsessionid 도 함께 전달한다

     

     URL 전달 방식을 끄고 항상 쿠키를 통해서만 세션을 유지하고 싶으면 다음 옵션을 넣어주면 된다. 이렇게 하면 URL에 jsessionid 가 노출되지 않는다

     

    application.properties

    server.servlet.session.tracking-modes=cookie

     


     

     

     

     

     

     

    728x90
    반응형

    댓글

Designed by Tistory.