앱 백엔드 개발을 해오다가 스프링부트 입문 후, 오랜만에 웹 개발을 하니까 쿠키와 세션쪽이 갑자기 좀 헷갈렸어서 정리해둔 내용입니다
일단 딱딱한 정의부터 ㅋㅋ
쿠키는 브라우저(클라이언트)에 저장되는 데이터고 서버와 클라이언트가 상태를 유지하거나 인증 정보를 기억하기 위해 사용됨
| 속성 | 설명 |
|---|---|
name=value | 쿠키의 이름과 값 (기본) |
Expires, Max-Age | 유효 기간 설정 (없으면 세션) |
Path | 어느 경로 요청에서만 쿠키를 포함시킬지 |
Domain | 어느 특정 서브도메인까지 전송할지 |
Secure | HTTPS에서만 전송됨 (사실상 필수) |
SameSite | CSRF 공격 방어용 옵션 (Lax: 기본, Strict, None), 실무에선 필수로 챙기는듯 함 |
HttpOnly | 자바스크립트에서 쿠키 접근 불가. 보안 강화용 (사실상 필수) |
쿠키에 HttpOnly 옵션을 붙이면 자바스크립트가 못 읽는데
못된 놈들이 XSS 공격을 통해 쿠키를 읽어갈 수 있는걸 방지할 수 있다 (보조수단 느낌) (인증토큰 탈취당하면…… ㄱ-)
그래서 보안이 강해진다 ㅋㅋ
| 항목 | 쿠키 | 세션 | JWT |
|---|---|---|---|
| 저장 위치 | 브라우저 (쿠키 스토리지) | 서버 + 브라우저 (세션 ID 쿠키) | 브라우저 (로컬/세션스토리지, 메모리, 쿠키) |
| 상태 저장 | 쿠키에 직접 id 값 저장, jwt를 저장하기도 함 | 서버가 세션에 사용자 정보 저장 | 토큰 안에 사용자 정보를 포함 (stateless)(보통 subject는(sub) → userId값임), DB에 RefreshToken을 저장해서 상태를 부분적으로 관리하는 경우도 많음 |
| 전송 방식 | 쿠키 자동 전송 | sessionId 같은 값이 포함된 쿠키 자동 전송 | Authorization: Bearer 헤더로 수동 전송 |
| 인증 방식 | 쿠키만 있으면 서버가 적절히 판단함 | 서버가 id 보고 날 기억함 | 토큰 자체가 신분증 역할을 함 |
애초에 HttpOnly 쿠키면 자바스크립트에서 못 꺼내기 때문에 프론트 개발자가 쿠키 컨트롤을 할 수 없다.
어라랍쇼 그럼 스프링 시큐리티 쓸 때 쿠키헤더에 담긴 쿠키... 어떻게 꺼내서 사용자 인증을 하지????
스프링 시큐리티를 쓸 때 JWT 방식 인증을 하려면 커스텀 Jwt 필터를 구현해 HttpSecurity 체인에 추가해줘야 함
그러면 구현한 커스텀Jwt필터 내부에 request.getCookies() 같은 코드를 통해 쿠키를 직접 파싱하고 sub(userId)가 담긴 accessToken을 꺼내서 유저 검증을 하게 됨
(스프링 시큐리티 필터 체인이 기본적으로 UsernamePasswordAuthenticationFilter 앞단이나 SecurityContextPersistenceFilter 앞단에서 JWT 인증 필터를 넣는 방식)
그 과정속에서 accessToken속 sub를 뽑아내기위해 jjwt 라이브러리의 JwtParser같은 게 쓰이고
accessToken 속에서 뽑은 userId로 OAuth2User(Principal임)를 뽑는 직접 구현한 서비스의 loadUser 메서드를 통해 인증된 유저 Principal을 생성하고
SecurityContextHolder에 인증된 유저 Principal을 set하면 사용자 인증이 완료된다.
추후 개발할 때는 적절히 SecurityContextHolder에 저장돼 있을 유저 도메인을
컨트롤러에서 @AuthenticationPrincipal CustomOAuth2User authentication처럼 꺼내서 쓰면 됨.
@Override
@PostMapping
public ResponseEntity<ReservationResponse> reserve(
@RequestBody @Valid ReservationRequest request,
@AuthenticationPrincipal CustomOAuth2User authentication // 저장된 유저 principal
) {
ReservationResponse result = seatReservationService.reserve(
request.performanceId(),
request.scheduleId(),
authentication.getUser().getId(),
request.quantity()
);
return ResponseEntity.status(HttpStatus.CREATED).body(result);
}
프론트가 토큰 저장, 토큰 전송을 직접 컨트롤해야 함
앱 개발 계획이 있다면 JWT로 처음부터 인증을 구현하는 편이고, SPA 환경에서는 주로 사용됨.
필터에서 쿠키를 뽑았던 부분만 request.getHeader("Authorization")로 바뀌고 적절히 파싱해서 accessToken을 뽑아내면 된다