반응형
쿠키(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로 바꾸고 다시 요청을 보내면 아래와 같이 다른 사람의 계정으로 로그인 된 것 처럼 할 수 있음
- 이를 해결하기 위해 세션 로그인을 사용할 수 있음
반응형
'Spring Boot > 문법 정리' 카테고리의 다른 글
[Spring Boot] Spring Security를 사용한 로그인 구현 (Form Login) (1) | 2023.01.06 |
---|---|
[Spring Boot] Session을 사용한 로그인 구현 (7) | 2023.01.04 |
[Spring Boot] 로그인 구현 방법 정리 (6) | 2023.01.01 |
[Spring Boot] 연관관계 매핑 - N:M 관계 예제 (0) | 2023.01.01 |
[Spring Boot] 연관관계 매핑 - 1:1 관계 예제 (+ @PostConstruct를 사용한 초기 데이터 생성) (0) | 2023.01.01 |