[Spring_Boot] 로그인 - 쿠키, 세션 & SessionConst

최현석·2022년 12월 1일
0

Spring_Boot

목록 보기
14/31

🧩 쿠키

  • 서버에서 로그인 성공 시 쿠키를 담아 브라우저에 전달하면 브라우저는 해당 쿠키를 저장해두고 해당 사이트에 접속할 때마다 지속해서 해당하는 쿠키를 보내준다.

application.properties

  • thymeleaf에서 url에다 자동으로 포함시켜주는 내용이라고 보면 된다.

    server.servlet.session.tracking-modes=cookie

1) 쿠키 생성하기

  • response.addCookie(idCookie);
    • 생성된 쿠키(idCookie)서버 응답 객체(HttpServletResponse)addCookie를 이용해 담아준다. 그럼 실제로 웹 브라우저에서는 Set-Cookie 프로퍼티에 쿠키정보가 담겨져 반환

LoginController

	@PostMapping("/login")
	public String login(@ModelAttribute LoginForm form, Model model, 
			RedirectAttributes redirectAttributes, HttpServletResponse response) {
		
		Member loginMember = loginService.login(form.getLoginId(), form.getPassword());
		if(loginMember == null) {
			// 로그인 실패
			model.addAttribute("msg", "로그인 실패");
			return "login/loginForm";
		}
		
		// 로그인 성공
        //쿠키에 시간 정보를 주지 않으면 세션 쿠키가 된다. (브라우저 종료시 모두 종료)
		Cookie idCookie = new Cookie("memberId",String.valueOf(loginMember.getId()) );
		response.addCookie(idCookie);
		
		redirectAttributes.addFlashAttribute("msg","로그인성공");
		return "redirect:/";
		
	}

2) 쿠키 조회하기

  • @CookieValue(name = "memberId", required = false) Long memberId
    • 쿠키를 편하게 조회할 수 있도록 도와주는 애노테이션이다. 전송된 쿠키정보중 keymemberId인 쿠키값을 찾아 memberId 변수에 할당해준다.
    • requiredfalse이기에 쿠키정보가 없는 비회원도 접근 가능하다.

HomeController

	private final MemberRepository memberRepository;
    
	@GetMapping
	public String homeV2(@CookieValue(name = "memberId", required = false)Long memberId,
			Model model) {
		
		// 로그인한 사용자가 아니라면 home으로 보낸다.
		if(memberId == null) {
			return "home";
		}
		
		// db조회
		Member loginMember = memberRepository.findById(memberId);
		// 사용자가 없으면 null 처리 필요
		if(loginMember == null) {
			return "home";
		}
		
		// loginHome : 로그인에 성공한 사람만이 볼 수 있는 화면
		model.addAttribute("member",loginMember);
		return "loginHome";
	}

loginHome

  • 로그인 됬을 시 보여주는 화면
  • th:text="|로그인 : ${member.name}|"
    • 로그인한 사용자 이름 입력
  • th:action="@{/logout}"
    • 로그아웃 경로 입력
<h4 class="mb-3" th:text="|로그인 : ${member.name}|">로그인 사용자 이름</h4>
		<hr class="my-4">
		<div class="row">
			<div class="col">
				<button class="w-100 btn btn-secondary btn-lg" type="button"
					th:onclick="|location.href='@{/items}'|">상품 관리</button>
			</div>
			<div class="col">
				<form method="post" th:action="@{/logout}">
					<button class="w-100 btn btn-dark btn-lg"
						onclick="location.href='items.html'" type="submit">
						로그아웃
					</button>
				</form>
			</div>
		</div>

3) 쿠키 없애기(로그아웃)

  • 로그아웃 기능은 쿠키를 삭제하는게 아니라 종료 날짜를 0으로 줘서 바로 만료시킴으로써 삭제할 수 있다.

LoginController

@PostMapping("/logout")
	public String logout(HttpServletResponse response) {
		Cookie cookie = new Cookie("memberId", null);
		// 쿠키 시간설정
		cookie.setMaxAge(0);
		response.addCookie(cookie);
		return "redirect:/";
	}	

요약

  • 요약하면 중요 정보는 서버의 세션 저장소에 key/value로 저장한 뒤 브라우저에서는 key값만 가지고 있도록 하는 것이다. 이 개념을 그림으로 표현하면 다음과 같다.

🧩 세션

서블릿 HttpSession을 이용한 로그인

  • 서블릿에서는 세션 관리를 위해 HttpSession이라는 객체를 제공

session time out

  • 설정 방법(application.properties)
    • server.servlet.session.timeout=시간
      -> 시간 : 초단위, 기본은 1800초(30분), 분단위로 설정, 60초 보다 작은 값은 안된다.
  • 1800초(30분)으로 설정을 해두면 LastAccessTime 이후 timeout 시간이 지나면 WAS 내부에서 해당 세션을 삭제한다.

SessionConst

  • 세션의 대한 key값을 한꺼번에 class에 정의해 놓는 역할만 하는 class
  • Member loginMember = (Member) session.getAttribute(SessionConst.LOGIN_MEMBER);
    위와 같은 로직으로 여러회원이 로그인 할 텐데
    어떻게 하나의 key 값으로 여러개의 Member 객체를 구별 할 수 있는지 궁금하다!
  • HttpSession session = request.getSession(true); 를 하게되면
    사용자 kim이 접속해도 session_kim(kim에 대한 세션) 이 생성되고,
    사용자 jang이 접속해도 session_jang(jang에 대한 세션) 이 생성되어서
    session.getAttribute(SessionConst.LOGIN_MEMBER); 을 하게 되면
    사용자 kim은 session_kim.getAttribute(SessionConst.LOGIN_MEMBER);
    사용자 jang session_jang.getAttribute(SessionConst.LOGIN_MEMBER);
    을 수행하는거라서 충돌이 발생하지 않는다
  1. 사용자 kim이 로그인 요청
  2. 서버는 Session을 생성하고 생성한 SessionId를 쿠키에 담아서 클라이언트에게 전송
  3. 클라이언트 요청시 쿠키에 담긴 SessionId를 서버에 전송
  4. 서버는 Session 저장소에서 SessionId에 맞는 Session을 찾음
  5. 찾은 Session에서 해당 키값(SessionConst.LOGIN_MEMBER)에 맞는 객체(Member) 반환
public class SessionConst {
	public static final String LOGIN_MEMBER = "loginMember";
}

LoginController

  • request.getSession(true);

    • 세션이 있으면 기존 세션을 반환한다.
    • 세션이 없으면 새로운 세션을 생성해서 반환한다.
  • request.getSession(false);

    • 세션이 있으면 기존 세션을 반환한다.
    • 세션이 없으면 새로운 세션을 생성하지 않고, null을 반환
  • 인수를 전달하지 않을 경우 기본 값으로 true이다.

  • session.invalidate();

    • 세션을 제거하는 메서드다.
@PostMapping("/login")
	public String loginV2(@ModelAttribute LoginForm form, Model model, 
			RedirectAttributes redirectAttributes, HttpServletRequest request) {
		
		Member loginMember = loginService.login(form.getLoginId(), form.getPassword());
		if(loginMember == null) {
			// 로그인 실패
			model.addAttribute("msg", "로그인 실패");
			return "login/loginForm";
		}
		
		// 로그인 성공
        // session변수에 요청온거에서 getSession 획득
		HttpSession session = request.getSession();
		// 세션에 로그인 회원 정보 보관
		session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);
		redirectAttributes.addFlashAttribute("msg","로그인성공");
		return "redirect:/";
		
	}
    
    @PostMapping("/logout")
	public String logoutV2(HttpServletRequest request) {
		// 세션을 삭제
		HttpSession session = request.getSession(false);
		if(session != null) {
			session.invalidate();
		}
		return "redirect:/";
	}

HomeController

  • @SessionAttribute(name = SessionConst.LOGIN_MEMBER, required = false)
    • 이전에 사용한 @CookieValue와 비슷하다. 클라이언트로부터 전달받은 내용의 세션중에서 key가 일치하는게 있는지 찾는다. requiredfalse이니 만약 못찾으면 null이 할당될 것이다
// @GetMapping
	public String homeV3(HttpServletRequest request ,Model model) {
		HttpSession session = request.getSession(false);
		
		// 로그인한 사용자가 아니라면 home으로 보낸다.
		if(session == null) {
			return "home";
		}
		Member loginMember = (Member)session.getAttribute(SessionConst.LOGIN_MEMBER);
		
		// 사용자가 없으면 null 처리 필요
		if(loginMember == null) {
			return "home";
		}
		
		// loginHome : 로그인에 성공한 사람만이 볼 수 있는 화면
		model.addAttribute("member",loginMember);
		return "loginHome";
	}
    
// session attribute 사용
@GetMapping
	public String homeV4(
			// session attribute를 뒤져서 member에 값을 넣어준다.
			@SessionAttribute(name=SessionConst.LOGIN_MEMBER,required = false)Member loginMember
			,Model model) {
		
		// 사용자가 없으면 null 처리 필요
		if(loginMember == null) {
			return "home";
		}
		
		// loginHome : 로그인에 성공한 사람만이 볼 수 있는 화면
		model.addAttribute("member",loginMember);
		return "loginHome";
	}

정리

  • 쿠키를 가지고 로그인 상태를 관리하는것은 코드도 복잡해지고 보안상 취약하다.

  • 중요한 정보는 클라이언트에 노출시켜선 안되고 서버에서 관리해야 한다.

  • 중요 정보는 서버에서 관리하며 정보에 접근할 추리불가능하고 중복이 되지않는 키를 이용해 서버와 클라이언트를 연결하도록 하며 이를 세션이라 한다.

0개의 댓글