minOS

로그인 - 요구사항, 쿠키 사용 본문

TIL/김영한의 스프링 MVC 2편 - 백엔드 웹 개발 핵심 기술

로그인 - 요구사항, 쿠키 사용

minOE 2024. 9. 24. 13:49
728x90

로그인 요구사항

- 홈 화면 - 로그인 전
   ㄴ회원 가입
   ㄴ로그인
- 홈 화면 - 로그인 후
   ㄴ본인 이름(누구님 환영합니다.)
   ㄴ상품 관리
   ㄴ로그 아웃
- 보안 요구사항
   ㄴ로그인 사용자만 상품에 접근하고, 관리할 수 있음
   ㄴ로그인 하지 않은 사용자가 상품 관리에 접근하면 로그인 화면으로 이동
- 회원 가입, 상품 관리

 

로그인 - 쿠키 사용

쿼리 파라미터를 계속 유지하면서 보내는 것은 매우 어렵고 번거로운 작업이다. 쿠키를 사용해보자.

쿠키

서버에서 로그인에 성공하면 HTTP 응답에 쿠키를 담아서 브라우저에 전달하자. 그러면 브라우저는 앞으로 해당 쿠키를 지속해서 보내준다.


쿠키 종류
- 영속 쿠키: 만료 날짜를 입력하면 해당 날짜까지 유지
- 세션 쿠키: 만료 날짜를 생략하면 브라우저 종료시 까지만 유지




쿠키 생성 - 쿠키 로그인
@PostMapping("/login")
    public String login(@Validated @ModelAttribute LoginForm loginForm , BindingResult bindingResult, HttpServletResponse response){
        if(bindingResult.hasErrors()){
            return "login/loginForm";
        }

        Member loginMember = loginService.login(loginForm.getLoginId(), loginForm.getPassword());

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

        //로그인 성공 처리 TODO -> 쿠키 만들어서 클라이언트에게 전달

       Cookie idCookie = new Cookie("memberId",String.valueOf(loginMember.getId()));
        response.addCookie(idCookie); // 쿠키에 시간 정보를 주지 않으면 세션 쿠키 (브라우저 종료시 모두 종료)
        return "redirect:/";
    }

로그인에 성공하면 쿠키를 생성하고 `HttpServletResponse` 에 담는다. 쿠키 이름은 `memberId` 이고, 값은 회원의 `id` 를 담아둔다. 웹 브라우저는 종료 전까지 회원의 `id` 를 서버에 계속 보내줄 것이다.



클라이언트 쿠키 전달1 - 로그인 이후 웰컴 페이지 접근
@GetMapping("/") //required = false -> 로그인하지 않은 사용자도 허용(쿠키값 없는)
    public String homeLogin(@CookieValue(name="memberId",required = false) Long memberId, Model model){
        if(memberId == null){
            return "home";
        }
        //로그인 성공한 사용자
        Member loginMember = memberRepository.findById(memberId);
        if(loginMember == null){
            return "home";
        }

        model.addAttribute("member",loginMember);
        return "loginHome";
    }​

- @CookieValue` 를 사용하면 편리하게 쿠키를 조회할 수 있다.
- 로그인 하지 않은 사용자도 홈에 접근할 수 있기 때문에 required = false를 사용한다.


로직 분석
- 로그인 쿠키( `memberId` )가 없는 사용자는 기존 `home` 으로 보낸다. 추가로 로그인 쿠키가 있어도 회원이 없으면 `home` 으로 보낸다.
- 로그인 쿠키( `memberId` )가 있는 사용자는 로그인 사용자 전용 홈 화면인 `loginHome` 으로 보낸다. 추가로 홈 화면에 화원 관련 정보도 출력해야 해서 `member` 데이터도 모델에 담아서 전달한다.

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="utf-8">
  <link th:href="@{/css/bootstrap.min.css}"
        href="../css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container" style="max-width: 600px">
  <div class="py-5 text-center">
    <h2>홈 화면</h2> </div>
  <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 th:action="@{/logout}" method="post">
        <button class="w-100 btn btn-dark btn-lg" type="submit">
          로그아웃
        </button>
      </form>
    </div>
  </div>
  <hr class="my-4">
</div> <!-- /container -->
</body>
</html> ```​

- th:text="|로그인: ${member.name}|"` :로그인에성공한사용자이름을출력한다.
- 상품 관리, 로그아웃 버튼을 노출한다.

로그인 이후 웰컴 페이지 접근 실행

로그인에 성공하면 사용자 이름이 출력되면서 상품 관리, 로그아웃 버튼을 확인할 수 있다. 로그인에 성공시 세션 쿠키 가 지속해서 유지되고, 웹 브라우저에서 서버에 요청시 `memberId` 쿠키를 계속 보내준다

 

 

로그아웃 기능

로그아웃 방법
- 세션 쿠키이므로 웹 브라우저 종료시
- 서버에서 해당 쿠키의 종료 날짜를 0으로 지정

@PostMapping("/logout")
    public String logout(HttpServletResponse response){
        expireCookie(response,"memberId");
        return "redirect:/";

    }
    
private static void expireCookie(HttpServletResponse response, String cookieName) {
        Cookie cookie = new Cookie(cookieName, null);
        cookie.setMaxAge(0);
        response.addCookie(cookie);
    }​

 


실행

로그아웃도 응답 쿠키를 생성하는데 `Max-Age=0` 를 확인할 수 있다. 해당 쿠키는 즉시 종료된다.

 

 

쿠키와 보안 문제

쿠키를 사용해서 `memberId` 를 전달해서 로그인을 유지할 수 있었다. 그런데 여기에는 심각한 보안 문제가 있다.

보안 문제

1) 쿠키 값은 임의로 변경할 수 있다.
ㄴ클라이언트가 쿠키를 강제로 변경하면 다른 사용자가 된다.
ㄴ 실제 웹브라우저 개발자모드 Application Cookie 변경으로 확인
ex) `Cookie: memberId=1` `Cookie: memberId=2` (다른사용자의이름이보임)

2) 쿠키에 보관된 정보는 훔쳐갈 수 있다.
 ㄴ만약 쿠키에 개인정보나, 신용카드 정보가 있다면 이 정보가 웹 브라우저에도 보관되고, 네트워크 요청마다 계속 클라이언트에        서 서버로 전달된다.
 ㄴ쿠키의 정보가 나의 로컬 PC에서 털릴 수도 있고, 네트워크 전송 구간에서 털릴 수도 있다.

3) 해커가 쿠키를 한번 훔쳐가면 평생 사용할 수 있다.
   ㄴ해커가 쿠키를 훔쳐가서 그 쿠키로 악의적인 요청을 계속 시도할 수 있다.




대안

1) 쿠키에 중요한 값을 노출하지 않고, 사용자 별로 예측 불가능한 임의의 토큰(랜덤 값)을 노출하고, 서버에서 토큰과 사용자 id를 매핑해서 인식한다. 그리고 서버에서 토큰을 관리한다.

2) 토큰은 해커가 임의의 값을 넣어도 찾을 수 없도록 예상 불가능 해야 한다
.

3)해커가 토큰을 털어가도 시간이 지나면 사용할 수 없도록 서버에서 해당 토큰의 만료시간을 짧게
(: 30) 유지 한다. 또는 해킹이 의심되는 경우 서버에서 해당 토큰을 강제로 제거하면 된다.



728x90