쿠키, 세션, 토큰

형석이의 성장일기·2024년 4월 2일
1

Spring Security

목록 보기
1/6

쿠키(Cookie)

  • 쿠키란 사용자가 웹 사이트에 접속 시, 브라우저에 저장되는 작은 텍스트 파일임
  • 이를 토대로 유저 정보를 저장할 수 있음
  • 만료 설정에 따라 두 가지 쿠키로 나뉨
    • 영속 쿠키 : 만료 날짜를 입력한 경우, 만료 날짜까지 유지
    • 세션 쿠키 : 만료 날짜를 따로 입력하지 않은 경우, 브라우저 종료까지 유지

쿠키 사용 이유

  • 쿠키, 세션, 토큰 이 세 가지 모두 사용 이유가 동일함
  • HTTP 통신은 기본적으로 Stateless 이기 때문에 상태가 저장되지 않음
  • 그러면 매번 기능을 사용할 때, 로그인과 같은 인증 인가 과정을 거쳐야 함 → 너무 귀찮음
  • 이런 상황을 편리하게 만들기 위해 쿠키가 나오게 됨!

쿠키 생성 및 관리

주로 HttpServletResponse 에 addCookie 를 사용해 클라이언트에게 리턴하는 방식을 사용함


쿠키 예제 코드

쿠키를 사용한 로그인

@Controller
@RequiredArgsConstructor
public class UserController {

    private final UserService userService;
		
		...

    @GetMapping("/home")
    public String home(@CookieValue(name = "userId", required = false) Long userId, Model model) {
        if (userId == null) return "home";
        UserDto userDto = userService.cookieLogin(userId);
        if (userDto == null) return "home";
        model.addAttribute(userDto);
        return "home";
    }
}
  • @CookieValue 을 사용해 유저에게 저장되어 있는 쿠키를 가져옴
  • required = false 을 통해 로그인하지 않은 유저도 허용함

쿠키를 사용한 로그아웃

@Controller
@RequiredArgsConstructor
public class UserController {

    private final UserService userService;
    
    ...

    @PostMapping("/logout")
    public String cookieLogout(HttpServletResponse response) {
        Cookie cookie = new Cookie("userId", null);
        cookie.setMaxAge(0);
        response.addCookie(cookie);
        return "redirect:/";
    }
}
  • 유저의 브라우저에 저장되어 있는 쿠키의 만료 날짜를 0으로 변경함
  • response 객체에 해당 쿠키를 추가함

쿠키의 문제점

  • 쿠키의 가장 큰 문제는 브라우저 상에서 임의로 변경 가능하다는 점임
  • 유저가 브라우저에 있는 쿠키를 변경하면 다른 유저로 접근 가능

쿠키의 문제점 대안

  • 쿠키에 어떤 유저인지 판단할 수 없는 임의의 값을 넣음
    • 중요한 정보는 백엔드에 저장함
    • 백엔드에서 복호화를 통해 어떤 유저인지 해석함
  • 토큰의 만료 날짜를 짧게 설정함 (5분 ~ 30분)

💡 이러한 대안으로 사용되는 방법이 쿠키 + 세션 임!


세션(Session)

  • 쿠키의 보안 문제를 해결하기 위해 중요한 정보를 모두 서버에 저장해야 함
  • 이렇게 서버에 중요한 정보를 보관하고 연결을 유지하는 방법을 세션이라 힘
  • 주로 세션 저장소로는 Tomcat Session, MySQL 과 같은 DB, Redis, Memcached 와 같은 인메모리 저장소

세션 예제 코드

HttpSession 사용

@Controller
@RequiredArgsConstructor
public class UserController {

    private final UserService userService;
    
    ...

    @PostMapping("/login")
    public String sessionLogin(@Validated @ModelAttribute LoginForm form,
                          HttpServletRequest request) {
        if (bindingResult.hasErrors()) {
            return "login/loginForm";
        }

        UserDto loginUser = userService.login(form.getLoginId(), form.getPassword());

        if (loginUser == null) return "login/loginForm";

        // 로그인 성공 처리를 위한 HttpSession
        HttpSession session = request.getSession();
        session.setAttribute("loginUser", loginUser);

        return "redirect:/";
    }
}
  • userService 에서 정상적으로 로그인 된 경우 HttpSession 에 유저 정보를 저장함
  • SessionID 을 클라이언트에게 제공함

@SessionAttribute 사용

@Controller
@RequiredArgsConstructor
public class UserController {

    ...
    
    @GetMapping("/")
    public String home(@SessionAttribute(name = "loginUser", required = false) UserDto loginUser, Model model) {
        if (loginUser == null) {
            return "home";
        }

        model.addAttribute("user", loginUser);
        return "loginHome";
    }
}
  • @SessionAttribute 어노테이션을 사용해 세션 정보를 가져올 수 있음

두 방식의 차이

  • HttpSession 은 따로 형 변환이 필요함
  • @SessionAttribute 은 형 변환 필요없음

💡 여기서 더 나아가면 JWT 토큰의 개념이 나옴


JWT(Json Web Token)

  • JWT란, Json 객체에 인증에 필요한 정보들을 담은 뒤, 비밀키로 암호화한 토큰을 의미함
  • 주로 인증/인가 기능에 사용됨

JWT 구조

  • Header, Payload, Signature 로 구성됨

  • Header
    • typ : 토큰의 타입을 지정함. 여기선 JWT 가 들어감
    • alg : 암호화(해싱) 알고리즘을 지정함. 주로 HS256, RSA 가 사용됨. 이 부분은 토큰을 검증할 때 사용되는 signature 에서 사용됨
    • 만약
      {
      	"typ": "JWT",
      	"alg": "HS256"
      }
      이러한 헤더를 사용한다면 헤더엔
      eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
      이런 문자열이 들어가게 됨
  • Payload
    • 토큰에 담을 정보가 클레임(Claim) 형태로 담김
    • 클레임이란?
      • Key/Value 형태로 된 값을 의미함
    • 저장되는 정보에 따라 등록 클레임, 공개 클레임, 비공개 클레임 으로 나뉨
    • 참고로 Payload 는 암호화된 부분이 아님! 단순이 인코딩된 값이기 때문에 제 3자가 조작이 가능함
  • Signature
    • 토큰을 인코딩하거나 유효성 검증을 할 때 사용하는 고유한 암호화 코드임
    • 동작 과정
      1. Header 와 Payload 의 값을 각각 BASE64로 인코딩
      2. 인코딩한 값을 비밀 키를 사용해 Header 에서 정의한 alg 으로 해싱
      3. 이 값을 다시 BASE64 로 인코딩

이렇게 생성된 JWT 를

{
	"Authorization": "Bearer 생성된 JWT"
}

이런 식으로 리턴함

JWT 장점

  • JWT에 인증에 필요한 정보가 있기 때문에 인증을 위한 별도의 저장소가 필요없음 (만약 Refresh Token 을 사용한다면 DB 나 Redis 같은 별도의 저장소 필요)
  • 네이버나 카카오같은 소셜 로그인과 함께 사용 가능
  • 모바일에서도 잘 동작함

JWT 단점

  • 토큰의 길이가 길기 때문에 네트워크 부하에 관여함
  • 정보를 전달할 때 사용되는 Payload 부분은 따로 암호화를 거치지 않기 때문에 굉장히 중요한 정보는 JWT 를 사용해 전달할 수 없음
  • 토큰을 탈취당한다면 대처가 어려움

JWT의 가장 큰 단점이 토큰을 탈취당한다면 대처가 어려움

이러한 단점을 극복하기 위해 AccessTokenRefreshToken

profile
이사중 .. -> https://gudtjr2949.tistory.com/

0개의 댓글

관련 채용 정보