본문 바로가기
카테고리 없음

[Spring] 로그인 - 서블릿 HTTP 세션을 이용

by drCode 2023. 11. 15.
728x90
반응형

https://drcode-devblog.tistory.com/560

 

[Spring] 쿠키와 보안 문제 & 세션 동작 방식 및 세션 직접 구현하기

https://drcode-devblog.tistory.com/559 [Spring] 로그인 구현, 쿠키 적용 지난 포스팅에 이어서 로그인을 구현해보면 아래와 같다. LoginService.java package hello.login.domain.login; import hello.login.domain.member.Member; import

drcode-devblog.tistory.com

지난 포스팅에 이어서 이번에는 서블릿 HTTP 세션을 이용해서 로그인처리를 다뤄보겠다.

 

세션이라는 개념은 대부분의 웹 애플리케이션에 필요한 것이다.

어쩌면 웹이 등장하면서 부터 나온 문제이다.

 

서블릿은 세션을 위해 HttpSession 이라는 기능을 제공하는데, 지금까지 나온 문제들을 해결해준다.

위 포스팅에서 직접 구현한 세션의 개념이 이미 구현되어 있고, 더 잘 구현되어 있다

 

 

HttpSession 소개

서블릿이 제공하는 HttpSession 도 결국 우리가 직접 만든 SessionManager 와 같은 방식으로 동작한다.

서블릿을 통해 HttpSession 을 생성하면 다음과 같은 쿠키를 생성한다.

쿠키 이름이 JSESSIONID 이고, 값은 추정 불가능한 랜덤 값이다.

 

Cookie: JSESSIONID=5B78E23B513F50164D6FDD8C97B0AD05

 

HttpSession 사용

서블릿이 제공하는 HttpSession 을 사용하도록 개발해보자.

 

SessionConst

package hello.login.web;

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

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

 

LoginController - loginV3()

@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());
    log.info("loginMember={}", loginMember);

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

    // 로그인 성공 처리
    // 세션이 있으면 세션 반환, 없으면 신규 세션을 생성
    HttpSession session = request.getSession(); // default 파라미터 : true
    // 세션에 로그인 회원 정보 보관
    session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);
    return "redirect:/";
}

기존 loginV2() 의 @PostMapping("/login") 주석 처리

 

세션 생성과 조회

세션을 생성하려면 request.getSession(true) 를 사용하면 된다.

public HttpSession getSession(boolean create);

 

세션의 create 옵션에 대해 알아보면,

 - request.getSession(true)

    : 세션이 있으면 기존 세션을 반환한다.

    : 세션이 없으면 새로운 세션을 생성해서 반환한다.

 - request.getSession(false)

    : 세션이 있으면 기존 세션을 반환한다.

    : 세션이 없으면 새로운 세션을 생성하지 않는다. null 을 반환한다.

 - request.getSession()

    : 신규 세션을 생성하는 request.getSession(true) 와 동일하다.

 

세션에 로그인 회원 정보 보관

session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);

세션에 데이터를 보관하는 방법은 request.setAttribute(..) 와 비슷하다.

하나의 세션에 여러 값을 저장할 수 있다.

 

LoginController - logoutV3()

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

    return "redirect:/";
}

기존 logoutV2() 의 @PostMapping("/logout") 주석 처리

session.invalidate() : 세션을 제거한다.

 

HomeController - homeLoginV3()

@GetMapping("/")
// 자동으로 타입컨버팅 해준다. 쿠키는 원래 String
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";

}

기존 homeLoginV2() 의 @GetMapping("/") 주석 처리

 

 - request.getSession(false) : request.getSession() 를 사용하면 기본 값이 create: true 이므로,

로그인 하지 않을 사용자도 의미없는 세션이 만들어진다.

따라서 세션을 찾아서 사용하는 시점에는 create: false 옵션을 사용해서 세션을 생성하지 않아야 한다.      

 - session.getAttribute(SessionConst.LOGIN_MEMBER) : 로그인 시점에 세션에 보관한 회원 객체를 찾는다.

 

실행

JSESSIONID 쿠키가 적절하게 생성되는 것을 확인하자.

 

@SessionAttribute

스프링은 세션을 더 편리하게 사용할 수 있도록 @SessionAttribute 을 지원한다.

 

이미 로그인 된 사용자를 찾을 때는 다음과 같이 사용하면 된다. 참고로 이 기능은 세션을 생성하지 않는다. @SessionAttribute(name = "loginMember", required = false) Member loginMember

 

HomeController - homeLoginV3Spring()

@GetMapping("/")
// 자동으로 타입컨버팅 해준다. 쿠키는 원래 String
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";

}

 

homeLoginV3() 의 @GetMapping("/") 주석 처리

 

세션을 찾고, 세션에 들어있는 데이터를 찾는 번거로운 과정을 스프링이 한번에 편리하게 처리해주는 것을 확인할 수 있다.

 

TrackingModes

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

 

http://localhost:8080/;jsessionid=F59911518B921DF62D09F0DF8F83F872

 

이것은 웹 브라우저가 쿠키를 지원하지 않을 때 쿠키 대신 URL을 통해서 세션을 유지하는 방법이다.

이 방법을 사용하려면 URL에 이 값을 계속 포함해서 전달해야 한다.

타임리프 같은 템플릿은 엔진을 통해서 링크를 걸면 jsessionid 를 URL에 자동으로 포함해준다.

서버 입장에서 웹 브라우저가 쿠키를 지원하는지 하지 않는지 최초에는 판단하지 못하므로,

쿠키 값도 전달하고, URL에 jsessionid 도 함께 전달한다.

 

URL 전달 방식을 끄고 항상 쿠키를 통해서만 세션을 유지하고 싶으면 다음 옵션을 넣어주면 된다.

이렇게 하면 URL에 jsessionid 가 노출되지 않는다.

 

application.properties

server.servlet.session.tracking-modes=cookie

 

728x90
반응형

댓글