14-2. 세션 기반 인증 (Session-Based Authentication)

박은서's avatar
Feb 25, 2026
14-2. 세션 기반 인증 (Session-Based Authentication)

1. 세션 기반 인증(Session-Based Authentication)

  • 웹 보안에서 가장 전통적이고 널리 쓰이는 로그인 방식

1️⃣ 세션 인증

1) 세션 인증이란?

“로그인 상태를 서버가 기억하는 방식”
사용자가 로그인하면 서버가 “이 사용자는 인증된 사용자다”라고 기억(저장)
➡️ 그 기억을 식별하기 위해 사용하는 것 세션 ID (Session ID)

2) 세션 인증의 핵심 개념

세션 인증을 이해하려면 3가지만 알면 됩니다:
요소
역할
사용자 (브라우저)
로그인 요청을 보냄
서버
로그인 성공 여부 판단 & 상태 기억
세션 ID
로그인 상태를 식별하는 표식

3) 동작 원리 (큰 그림)

로그인 성공 → 서버가 세션 생성 → 세션 ID 발급 → 브라우저가 저장 → 이후 요청마다 제출

2️⃣ 실행 흐름 (단계별)

0) 예시 시나리오

  • POST /login : 아이디/비밀번호 로그인
  • GET /me : 로그인한 사용자 정보(보호 API)
  • POST /logout : 로그아웃
➡️ 세션에 저장할 값 : LOGIN_USER 라는 키로 userId, roles 등 저장

1) 로그인 요청 → 컨트롤러에서 아이디/비밀번호 받기

  • 실행 흐름
    • 사용자가 아이디 / 비밀번호 입력 → 서버로 전송
  • 코드
    • // LoginRequest.java public record LoginRequest(String username, String password) {}
      // AuthController.java @RestController public class AuthController { private final UserService userService; public AuthController(UserService userService) { this.userService = userService; } @PostMapping("/login") public ResponseEntity<?> login(@RequestBody LoginRequest req, HttpServletRequest request) { // 2) 서버가 인증 확인 (아래 단계에서 계속) User user = userService.authenticate(req.username(), req.password()); // 3) 서버가 세션 생성 + 사용자 정보 연결 HttpSession session = request.getSession(true); // 없으면 새로 생성 session.setAttribute("LOGIN_USER", new SessionUser(user.id(), user.username(), user.roles())); // 선택: 세션 타임아웃(초) 설정 (예: 30분) session.setMaxInactiveInterval(30 * 60); // 4) 세션 ID 쿠키는 보통 자동으로 내려감(JSESSIONID) return ResponseEntity.ok().build(); } }
      ➡️ 여기서 request.getSession(true) 호출 순간이 **“서버가 세션 생성”**에 해당
      이후 응답을 보낼 때 컨테이너(톰캣)가 Set-Cookie: JSESSIONID=... 를 자동으로 내려줌

2) 서버가 인증 확인 → 비밀번호 검증 로직(서비스)

  • 실행 흐름
    • 비밀번호 맞음 → 로그인 성공
  • 코드
    • // UserService.java @Service public class UserService { private final UserRepository userRepository; private final PasswordEncoder passwordEncoder; // BCrypt 권장 public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder) { this.userRepository = userRepository; this.passwordEncoder = passwordEncoder; } public User authenticate(String username, String rawPassword) { User user = userRepository.findByUsername(username) .orElseThrow(() -> new UnauthorizedException("Invalid credentials")); if (!passwordEncoder.matches(rawPassword, user.passwordHash())) { throw new UnauthorizedException("Invalid credentials"); } return user; } }
      ➡️ 비밀번호는 반드시 해시(BCrypt 등)로 저장하고 matches로 비교

3) 서버가 세션 생성

  • 서버 내부
    • 세션 저장소 생성 세션 ID 생성 (예: XH23KJASD89) 사용자 정보 연결
  • 개념
    • 세션 ID → 사용자 정보 매핑
    • XH23KJASD89 → 홍길동 사용자
  • 코드 (세션에 저장할 사용자 모델 정의)
    • // SessionUser.java public record SessionUser(Long userId, String username, List<String> roles) {}

4) 세션 ID를 브라우저에 전달

  • 서버 → 브라우저
    • 쿠키(Cookie)에 세션 ID 저장 (자동 저장)

5) 이후 모든 요청에서 세션 검사 → Filter(또는 Interceptor)

  • 보호가 필요한 경로(/me, /api/** 등)에 대해 요청마다
    • 요청 쿠키의 JSESSIONID로 세션이 잡힘(컨테이너가 해줌)
    • 우리는 session.getAttribute("LOGIN_USER")가 있는지 확인
  • 서블릿 필터 코드
    • // SessionAuthFilter.java @Component public class SessionAuthFilter implements Filter { private static final Set<String> PUBLIC_PATHS = Set.of( "/login", "/health" ); @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; String path = request.getRequestURI(); if (isPublic(path)) { chain.doFilter(req, res); return; } HttpSession session = request.getSession(false); // 없으면 만들지 않음 SessionUser loginUser = (session == null) ? null : (SessionUser) session.getAttribute("LOGIN_USER"); if (loginUser == null) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return; } // 필요하면 request attribute로 내려서 컨트롤러에서 쉽게 사용 request.setAttribute("LOGIN_USER", loginUser); chain.doFilter(req, res); } private boolean isPublic(String path) { if (PUBLIC_PATHS.contains(path)) return true; return false; } }
      핵심 차이
      • getSession(true) : 세션 만들기
      • getSession(false) : 세션 없으면 그냥 null (보호 요청에서 이게 중요)

6) 보호 API에서 “로그인 사용자” 사용하기

// UserController.java @RestController public class UserController { @GetMapping("/me") public ResponseEntity<?> me(HttpServletRequest request) { SessionUser user = (SessionUser) request.getAttribute("LOGIN_USER"); return ResponseEntity.ok(user); } }
  • 컨트롤러에서 HttpSession 직접 꺼내도 됨

7) 로그아웃(세션 무효화)

// AuthController.java 안에 추가 @PostMapping("/logout") public ResponseEntity<?> logout(HttpServletRequest request) { HttpSession session = request.getSession(false); if (session != null) { session.invalidate(); // 세션 폐기 } return ResponseEntity.ok().build(); }
  • invalidate()서버에서 즉시 끊기를 가능하게 하는 게 세션 방식의 강점

8) “세션 ID 쿠키” 보안 설정은 어디서 하냐?

  • 세션 쿠키(JSESSIONID)는 대개 자동이지만, 보안 속성은 설정해야 함
  • application.yml (톰캣/스프링 부트 기본 세션 쿠키 설정)
    • server: servlet: session: timeout: 30m cookie: http-only: true secure: true # HTTPS에서만 전송 same-site: lax # 크로스 사이트 정책에 따라 Lax/Strict/None
    • HttpOnly=true: JS로 쿠키 접근 제한(XSS 완화)
    • Secure=true: HTTPS에서만 전송
    • SameSite: CSRF/크로스사이트 요청 완화(단, 요구사항 따라 조정)

9) (중요) “직접 구현 세션 인증”에서 자주 빠지는 보안 포인트

  • CSRF: 브라우저 쿠키 기반이면 기본적으로 CSRF 위험이 생깁니다. (특히 state-changing 요청)
    • → 최소한 SameSite, 가능하면 CSRF 토큰 도입 고려
  • 세션 고정(Session Fixation): 로그인 전 세션을 로그인 후에도 그대로 쓰면 위험할 수 있음
    • → 로그인 성공 시 session.invalidate() 후 새 세션 발급 같은 패턴 고려(스프링 시큐리티는 기본 방어를 제공합니다)
  • 세션 만료/재발급: 민감한 작업(비번 변경 등) 후 세션 재발급 고려

10) 결론: “단계별 실행흐름 ↔ 코드” 매핑 한 줄 요약

  • (1) 로그인 요청: @PostMapping("/login")에서 자격 증명 수신
  • (2) 인증 확인: passwordEncoder.matches() 등으로 검증
  • (3) 세션 생성: request.getSession(true)
  • (4) 세션 ID 발급/쿠키 저장: 톰캣이 자동 처리(JSESSIONID Set-Cookie)
  • (5) 이후 요청 인증: Filter/Interceptor에서 getSession(false) + getAttribute("LOGIN_USER")

3️⃣ 비유로 이해하기 (가장 쉬운 설명)

세션 인증 = 헬스장 출입 팔찌 시스템

1) 등록할 때

  • 신분증 확인 → 팔찌 지급

2) 이후 출입할 때

  • 팔찌만 보여주면 됨
    • ➡️ 팔찌 = 세션 ID
      ➡️ 헬스장 DB = 서버 세션 저장소

4️⃣ 왜 세션 인증이 안전한가?

중요 포인트 👇
✔ 비밀번호는 매번 보내지 않음
✔ 서버가 상태를 직접 관리
✔ 민감 정보는 서버 내부에만 존재

5️⃣ 세션 인증의 단점

1) ❌ 서버 메모리 사용

  • 사용자마다 세션 저장 필요
    • 사용자 많음 → 서버 부담 증가

2) ❌ 확장성 문제

  • 서버 여러 대일 경우
    • 세션 공유 필요 (Redis 등 사용)

3) ❌ 세션 탈취 위험

  • 공격자가 세션 ID 획득 시
    • 로그인 없이 사용자 행세 가능
  • 그래서 사용하는 보안 기술 👇
    • HTTPS
    • HttpOnly 쿠키
    • Secure 쿠키
    • 세션 만료 시간

6️⃣ 세션 vs 토큰 인증 간단 비교

구분
세션 인증
토큰 인증 (JWT 등)
상태 저장
서버
클라이언트
서버 부담
적음
확장성
불리
유리
전통 웹
많이 사용
현대 API / 모바일
Share article