반응형

쿠키(Cookie) 란?

  • 쿠키 : 사용자가 웹사이트 접속시 사용자의 개인 장치에 다운로드되고 브라우저에 저장되는 작은 텍스트 파일
  • 웹사이트는 이 쿠키를 이용해 사용자의 장치를 인식하고 일부 데이터를 저장하기도 함

쿠키를 사용한 로그인 구현

개념

  • 쿠키를 사용하여 로그인 기능을 구현할 수 있음
  • 로그인 성공 시 서버가 쿠키에 사용자 정보를 넣어줌
  • 클라이언트 측에서는 다음 요청을 할 때마다 이 쿠키를 서버에 같이 보내줌
  • 서버에서는 이 쿠키를 확인해 로그인 했는지와 유저 정보, 권한 등을 확인할 수 있음

쿠키 생성 방법

  • new Cookie() 메서드를 통해 쿠키 생성
  • 쿠키 생성 시 Key, Value 값을 넣어줄 수 있음
    • Cookie cookie = new Cookie("userId", String.valueOf(user.getId()));
  • 방금 만든 쿠키의 유효 시간을 다음과 같이 정해줄 수도 있음
    • cookie.setMaxAge(60 * 60); // 쿠키 유효 시간 : 1시간
  • 유효 시간까지 정해준 쿠키를 HttpServletResponse 객체인 response에 addCookie를 통해 쿠키를 태워서 전송
    • response.addCookie(cookie);

쿠키 확인 방법

  • 쿠키를 넣어줬으면 다음 요청부터는 넣어준 쿠키가 자동으로 넘어오게 됨
  • 아래와 같이 @CookieValue 어노테이션을 통해 쿠키값을 받아올 수 있음
    • @CookieValue(name = "userId", required = false) Long userId

쿠키 파기 방법

  • 쿠키를 파기하는 방법은 생성한 쿠키와 똑같은 Key 값을 넣어주고 Value 값은 null을 넣어주고 다시 생성
    • Cookie cookie = new Cookie("userId", null);
  • 이 쿠키를 setMaxAge(0)을 통해 유효 시간을 0초로 설정해 줌
    • cookie.setMaxAge(0);
  • 이 쿠키를 이제 HttpServletResponse 객체인 response에 addCookie를 통해 다시 넣어주면 쿠키를 파기할 수 있음
    • response.addCookie(cookie);

쿠키를 사용한 로그인 구현 (Controller)

  • Entity, DTO, Repository, Service, 화면은 미리 만들어 놓음
    • [Spring Boot] 로그인 구현 방법 정리 참고
    • 공통 화면 사용을 위해 모든 요청에 아래 코드 추가
      • model.addAttribute("loginType", "cookie-login");
      • model.addAttribute("pageName", "쿠키 로그인");
@Controller
@RequiredArgsConstructor
@RequestMapping("/cookie-login")
public class CookieLoginController {

    private final UserService userService;

    @GetMapping(value = {"", "/"})
    public String home(@CookieValue(name = "userId", required = false) Long userId, Model model) {
        model.addAttribute("loginType", "cookie-login");
        model.addAttribute("pageName", "쿠키 로그인");

        User loginUser = userService.getLoginUser(userId);

        if(loginUser != null) {
            model.addAttribute("nickname", loginUser.getNickname());
loginUser.getNickname());
        }

        return "home";
    }

    @GetMapping("/join")
    public String joinPage(Model model) {
        model.addAttribute("loginType", "cookie-login");
        model.addAttribute("pageName", "쿠키 로그인");

        model.addAttribute("joinRequest", new JoinRequest());
        return "join";
    }

    @PostMapping("/join")
    public String join(@Valid @ModelAttribute JoinRequest joinRequest, BindingResult bindingResult, Model model) {
        model.addAttribute("loginType", "cookie-login");
        model.addAttribute("pageName", "쿠키 로그인");

        // loginId 중복 체크
        if(userService.checkLoginIdDuplicate(joinRequest.getLoginId())) {
            bindingResult.addError(new FieldError("joinRequest", "loginId", "로그인 아이디가 중복됩니다."));
        }
        // 닉네임 중복 체크
        if(userService.checkNicknameDuplicate(joinRequest.getNickname())) {
            bindingResult.addError(new FieldError("joinRequest", "nickname", "닉네임이 중복됩니다."));
        }
        // password와 passwordCheck가 같은지 체크
        if(!joinRequest.getPassword().equals(joinRequest.getPasswordCheck())) {
            bindingResult.addError(new FieldError("joinRequest", "passwordCheck", "바밀번호가 일치하지 않습니다."));
        }

        if(bindingResult.hasErrors()) {
            return "join";
        }

        userService.join(joinRequest);
        return "redirect:/cookie-login";
    }

    @GetMapping("/login")
    public String loginPage(Model model) {
        model.addAttribute("loginType", "cookie-login");
        model.addAttribute("pageName", "쿠키 로그인");

        model.addAttribute("loginRequest", new LoginRequest());
        return "login";
    }

    @PostMapping("/login")
    public String login(@ModelAttribute LoginRequest loginRequest, BindingResult bindingResult,
                        HttpServletResponse response, Model model) {
        model.addAttribute("loginType", "cookie-login");
        model.addAttribute("pageName", "쿠키 로그인");

        User user = userService.login(loginRequest);

        // 로그인 아이디나 비밀번호가 틀린 경우 global error return
        if(user == null) {
            bindingResult.reject("loginFail", "로그인 아이디 또는 비밀번호가 틀렸습니다.");
        }

        if(bindingResult.hasErrors()) {
            return "login";
        }

        // 로그인 성공 => 쿠키 생성
        Cookie cookie = new Cookie("userId", String.valueOf(user.getId()));
        cookie.setMaxAge(60 * 60);  // 쿠키 유효 시간 : 1시간
        response.addCookie(cookie);

        return "redirect:/cookie-login";
    }

    @GetMapping("/logout")
    public String logout(HttpServletResponse response, Model model) {
        model.addAttribute("loginType", "cookie-login");
        model.addAttribute("pageName", "쿠키 로그인");

        Cookie cookie = new Cookie("userId", null);
        cookie.setMaxAge(0);
        response.addCookie(cookie);
        return "redirect:/cookie-login";
    }

    @GetMapping("/info")
    public String userInfo(@CookieValue(name = "userId", required = false) Long userId, Model model) {
        model.addAttribute("loginType", "cookie-login");
        model.addAttribute("pageName", "쿠키 로그인");

        User loginUser = userService.getLoginUser(userId);

        if(loginUser == null) {
            return "redirect:/cookie-login/login";
        }

        model.addAttribute("user", loginUser);
        return "info";
    }

    @GetMapping("/admin")
    public String adminPage(@CookieValue(name = "userId", required = false) Long userId, Model model) {
        model.addAttribute("loginType", "cookie-login");
        model.addAttribute("pageName", "쿠키 로그인");

        User loginUser = userService.getLoginUser(userId);

        if(loginUser == null) {
            return "redirect:/cookie-login/login";
        }

        if(!loginUser.getRole().equals(UserRole.ADMIN)) {
            return "redirect:/cookie-login";
        }

        return "admin";
    }
}

결과

  • 메인 페이지 (로그인 하지 않은 상태)

  • 회원 가입 페이지

               

  • 로그인 페이지

             

  • 메인 페이지 (로그인 한 상태)

  • 유저 정보 페이지

  • 관리자 페이지 (USER은 진입 불가, ADMIN 계정으로 다시 로그인 후 진입)

문제점

  • 쿠키 로그인 시 아래와 같이 쿠키가 발급됨

  • 쿠키에 유저 정보가 들어가 있기 때문에 보안에 매우 취약함
    • 이 상태에서 userId를 1로 바꾸고 다시 요청을 보내면 아래와 같이 다른 사람의 계정으로 로그인 된 것 처럼 할 수 있음

반응형

↓ 클릭시 이동

복사했습니다!