[Spring] 로그인 체크박스 - 쿠키 / 세션

merci·2023년 1월 19일
2

Spring

목록 보기
6/21

로그인할때 다음 로그인시 아이디를 기억하겠냐는 체크박스를 구현해보자



쿠키와 세션의 개념을 먼저 보자

쿠키 / 세션

  • 서버는 브라우저가 처음 방문했을때 ( 쿠키가 없다면 ) 세션을 생성하여 응답헤더에 주고 자신의 세션저장소에도 저장한다음 다음 연결시 요청헤더의 쿠키( 세션 ID - key )와 세션저장소의 세션을 비교해서 동일한 연결( 동일한 브라우저 )이라 판단한다.

  • 기본적으로 방문에 대한 기록을 남기는 쿠키도 있겠지만 로그인상태에 관한 쿠키도 존재한다.

  • 메모리 쿠키라면 브라우저 종료시 서버에 저장된 세션 id는 소멸

  • 브라우저는 쿠키를 자동저장하게 만들어져 있다. 앱개발자는 쿠키를 저장하는 기능을 만들어야 한다.

  • 쿠키를 사용할때는 Null인지 확인부터 - NullPointer익셉션 발생함
    ( 쿠키의 수명은 setMaxAge()로 설정 )

  • 쿠키는 도메인별로 관리된다 - 접속한 도메인에서만 쿠키를 받고 보낸다.

  • 세션은 클라이언트의 상태를 저장해서 상태를 저장하지 않는 stateless한 http를 stateful로 만들어주는 기술

  • 서버는 응답헤더의 쿠키헤더( Set-cookie )에 쿠키를 넣어서 리턴
    -> JSESSIONID 의 값으로 세션의 ID ( key ) 를 브라우저와 공유

  • 세션저장소에 유저 오브젝트를 저장( value )하고 요청헤더의 쿠키( JSESSIONID )와 비교해서 로그인상태 확인 - 인증

  • 세션저장소에 세션은 ( key-value 구조 )로 저장됨
    로그인세션은 로그아웃시 삭제됨 ( invalidate() )
    로그인과 상관없이 브라우저 세션도 저장함 ( 단순 방문 )

  • 세션의 값은 오직 서버에만 존재하기 때문에 보안에 좋다
    모든 유저의 세션을 저장하면 서버부하가 커진다 - 토큰으로 보완






로그인 체크박스

  • 로그인 폼

<form action="/login" method="post">
   <input type="text" name="username" placeholder="Enter username" required><br />
   <input type="password" name="password" placeholder="Enter password" required><br />
   유저네임을 기억할까요? <input type="checkbox" name="remember"><br />
   <button type="submit">로그인</button>
</form>

체크박스의 속성 이름은 remember
value를 정하지 않으면 체크 시 디폴트값은 on
체크를 하지 않으면 value값은 null


체크 안했을 때

addHeader - 헤더에 데이터 추가

  • 컨트롤러
@PostMapping("/login")
    public String login(String username, String password, 
    					String remember, HttpServletResponse response) {
        User user = userRepository.findByUsernameAndPassword(username, password);
        response.addHeader("hello", username);  
        if (user == null) { // 로그인 실패시 
            return "redirect:/loginForm";
        } else {
            session.setAttribute("principal", user);
            return "redirect:/";
        }
    }

response.addHeader("hello", username); -> 입력받은 username 을 응답헤더에 넣었다.
브라우저에서 응답헤더를 확인해보면 hello : ssar 을 확인할수 있다.



🔥 addHeader 의 단점
아래와 같이 작성했다면 hello로 한번에 하나만 꺼낼 수 있을까 ?

response.addHeader("hello", username);
response.addHeader("hello", "member");



setHeader - 헤더 덮어쓰기

  • hello 는 test3 으로 덮어써진다
response.addHeader("hello", username);
response.setHeader("hello", "test3");

  • 응답헤더에 쿠키를 넣을때는 다른방법을 이용해야한다.





체크 했을때

addCookie - 쿠키 추가

  • 컨트롤러
	@PostMapping("/login")
	public String login(String username, String password,
    String remember, HttpServletResponse response) {
        User user = userRepository.findByUsernameAndPassword(username, password);
        if (user == null) {     // 로그인 실패시 
        return "redirect:/loginForm";
            } else {
        	if( remember == null){  // 체크 안하면 null, 공백 넣어서 비교
            remember = "";
                }
            if( remember.equals("on")){
           	 	Cookie cookie = new Cookie("remember", username); // 쿠키 생성
                response.addCookie(cookie);
                }else{
            	Cookie cookie = new Cookie("remember", "");
           		cookie.setMaxAge(0);
                response.addCookie(cookie);
                
                }
            session.setAttribute("principal", user);
            return "redirect:/";
        }
    }
  • response.addCookie(cookie); -> 응답헤더( Set-Cookie )에 생성한 쿠키를 추가

  • 로그인 후 응답헤더에 쿠키추가 됨

  • 2개의 쿠키가 브라우저에 저장

  • 다음 요청부터 요청헤더 쿠키에 remember=ssar 이 추가됨

  • 서버 종료후 다시 시작( 세션이 소멸했다고 가정 )해도 브라우저는 remember=ssar 쿠키를 가지고 있다
    이것을 이용해서 다음에 로그인시 아이디를 기억하게 만들 수 있다.


체크한뒤 로그인할 때

EL 표현식은 쿠키에도 접근이 가능하다
( page, request, session, cookie 접근 가능 )

<form action="/login" method="post">
   <input type="text" name="username" value="${cookie.remember.value}"
   								placeholder="Enter username" required> <br/>
   <input type="password" name="password" placeholder="Enter password" required> <br/>
   유저네임을 기억할까요? <input type="checkbox" name="remember"> <br/>
   <button type="submit">로그인</button>
</form>

value="${cookie.remember.value}" 를 사용해서
쿠키중에서 키가 remember 인 value ( ssar ) 를 로그인창에 미리 입력



  • value="${cookie.remember.value}" 를 사용하지 않고 value="${remember}" 로 하는 방법
	Cookie[] cookies = request.getCookies();  // getCookies쿠키 목록 배열 리턴
    String username="";
    for (Cookie cookie : cookies) {
        if (cookie.getName().equals("remember")){
            username = cookie.getValue();
        }
    }
    model.addAttribute("remember", username);
  • getCookies -> 쿠키 배열 리턴 / Model 에 넣어서 접근

  • 로그아웃시 세션 제거 - session.invalidate()

  • 다시 로그인시 응답헤더에 새로운 쿠키 보급





세션을 이용한 동적인 메뉴

로그인 세션으로 판단

  • jstl 태그 이용
	<c:choose>  
        <c:when test="${principal == null}">
            <li>
                <a href="/"></a>
            </li>
            <li>
                <a href="/loginForm">로그인</a>
            </li>
            <li>
                <a href="/joinForm">회원가입</a>
            </li>
        </c:when>
    
        <c:otherwise>
            <li>
                <a href="/"></a>
            </li>
            <li>
                <!-- /user/1/purchase -->
                <a href="/purchase">구매목록</a>
            </li>
            <li>
                <a href="/logout">로그아웃</a>
            </li>
        </c:otherwise>
	</c:choose>
  • <c:when test="${principal == null}"> 는 if문과 동일 - true일 경우
    principal 은 세션에 저장된 유저 오브젝트 ( 존재하면 true )
  • <c:otherwise> false 일 경우
  • 일반적으로 인증( 로그인 ) 관련된 것들은 엔티티 이름을( user ) 붙이지 않는다 - "/userloginForm"
    필터에서 거를 수도 있다


로그인 하면 구매버튼 보이게 설정

<!-- 세션에 principal 존재하면 보이게 -->
	<c:if test="${principal != null}">  
        <form action="/purchase/insert" method="post">
            <div class="purchase_btn">
                <div>
                    <input type="hidden" name="productId" value="${product.id}">
                    <select name="count">
                        <c:forEach begin="1" end="${product.qty}" var="num">
                            <option value="${num}">${product.name} ${num} 개</option>
                        </c:forEach>
                    </select>
                </div>
                <div>
                </div>
                <button type="submit"> 구매 </button>
            </div>
        </form>
    </c:if>
  • 비로그인
  • 로그인
profile
작은것부터

0개의 댓글