Spring ( Session, Token, JWT )

2025. 3. 30. 20:56·[내일배움캠프-Sparta]/Spring 6기
반응형

주말에 약간의 시간을 활용해서 진도를 살짝 나가보았다.

1️⃣ Session

✅ Session ?

⚡ 서버에서 중요한 정보를 보관하며 로그인 연결을 유지하는 방법이다. 

Cookie를 사용한 방식은 중요한 정보를 Client에서 보관하고 있기 때문에 이를, 서버 쪽에서 관리를 하여야한다.

Session 생성 순서

  1. 로그인에 성공하면 Server에서 임의로 만든 Session ID를 생성한다.
    • Session ID는 예측이 불가능해야 한다.
    • UUID와 같은 값을 활용한다.
  2. 생성된 Session ID와 조회한 User 인스턴스를 서버의 Session 저장소에 저장한다.
    • 서버에 유저와 관련된 중요한 정보를 저장한다.

※ Session 동작 순서

1. 로그인

  • 상태유지를 위해 Cookie를 사용한다.
    • 서버는 클라이언트에 Set-Cookie: SessionId=임의생성값 을 전달한다.
    • 클라이언트는 Cookie 저장소에 전달받은 SessionId 값을 저장한다.
  • Sessions을 사용하면 유저와 관련된 정보는 클라이언트에 없다.

결론적으로 웹 브라우저에는 id값만 저장 된다는 것이다.

2. 로그인 이후 

  • 클라이언트는 모든 요청에 Cookie 의 SessionId를 전달한다.
  • 서버에서는 Cookie를 통해 전달된 SessionId로 Session 저장소를 조회한다.
  • 로그인 시 저장하였던 Session 정보를 서버에서 사용한다.

 

  • Session 특징
    1. Session을 사용하여 서버에서 민감한 정보들을 저장한다.
      • 예측이 불가능한 세션 ID를 사용하여 쿠키값을 변조해도 문제 ❌
      • 세션 ID에 중요한 정보는 들어있지 않다.
      • 시간이 지나면 세션이 만료되도록 설정
      • 해킹이 의심되는 경우 해당 세션을 제거
    2. Session은 단지 Cookie를 사용하여 클라이언트가 아닌 서버에서 데이터를 저장해두는 방법이다.
    3. Servlet은 Session 을 자체적으로 지원한다.
    4. Session은 Memory를 사용하기 때문에 리소스를 낭비하면 안된다. 💥

✅ HttpSession

Servlet이 공식적으로 지원하는 Session인 HttpSession은 Session 구현에 필요한 다양한 기능들을 지원한다.

 

Servlet을 통해 HttpSession 을 생성하게되면 SessionId 가 JSESSIONID로 생성되고 JSESSIONID의 Value는 예측 불가능한 랜덤값으로 생성된다.

@PostMapping("/session-login")
public String login(
        @Valid @ModelAttribute LoginRequestDto dto,
        HttpServletRequest request
) {

    LoginResponseDto responseDto = userService.login(dto.getUserName(), dto.getPassword());
    Long userId = responseDto.getId();

    // 실패시 예외처리
    if (userId == null) {
        return "session-login";
    }

    // 로그인 성공시 로직
    // Session의 Default Value는 true이다.
    // Session이 request에 존재하면 기존의 Session을 반환하고,
    // Session이 request에 없을 경우에 새로 Session을 생성한다.
    HttpSession session = request.getSession();

    // 회원 정보 조회
    UserResponseDto loginUser = userService.findById(userId);

    // Session에 로그인 회원 정보를 저장한다.
    session.setAttribute(Const.LOGIN_USER, loginUser);

    // 로그인 성공시 리다이렉트
    return "redirect:/session-home";
}


@PostMapping("/session-logout")
public String logout(HttpServletRequest request) {
    // 로그인하지 않으면 HttpSession이 null로 반환된다.
    HttpSession session = request.getSession(false);
    // 세션이 존재하면 -> 로그인이 된 경우
    if(session != null) {
        session.invalidate(); // 해당 세션(데이터)을 삭제한다.
    }

    return "redirect:/session-home";
}
  • request.getSession();
    • request.getSession(true);
      • Default 설정
      • Request 객체 내에 Session이 존재한다면 기존 Session을 반환
      • Request 객체 내에 Session이 없으면 새로운 Session을 생성해서 반환
    • request.getSession(false);
      • Request 객체 내에 Session이 존재한다면 기존 Session을 반환
      • Request 객체 내에 Session이 없으면 null을 반환
  • session.setAttribute(Const.LOGIN_USER, responseDto);
    • Session에 Data를 저장하는 방법으로 request.setAttribute(); 와 비슷
    • 하나의 Session에 여러개의 데이터를 메모리에 저장할 수 있다.

✅ Spring의 Session

📣 Spring에서 Session 쉽게 다루도록 @SessionAttribute 라는 어노테이션 제공함.

 

@SessionAttribute

  • Session을 새로 생성하는 기능 ❌
  • 이미 로그인이 완료된 사용자를 찾는 경우 즉, Session이 있는 경우에 사용

🔴 처음 로그인을 시도하는 경우

  • 완전히 처음 로그인을 시도하는 경우 URL 뒤에 JSSESSIONID=값이 함께 전달된다.
  • 해당 값은 굳이 필요가 없다. 내부적으로 Set-Cookie로 SessionID와 값을 넣어주기 때문
  • Cookie를 지원하지 않으면 URL을 통해 Session을 유지하는 방법에 사용된다.
    • 하지만, 사용하려면 모든 요청 URL에 jsessionId값이 전달 되어야한다.
  • Template Engine을 사용하면 jsessionId를 URL에 자동으로 포함

✅ Session 정보

HttpSession은 Session을 간편하게 사용할 수 있도록 다양한 기능을 지원

@Slf4j
@RestController
public class SessionController {

    @GetMapping("/session")
    public String session(HttpServletRequest request) {
        HttpSession session = request.getSession(false);

        if (session == null) {
            return "세션이 없습니다.";
        }

        // session 정보 조회
        log.info("session.getId()={}", session.getId());
        log.info("session.getMaxInactiveInterval()={}", session.getMaxInactiveInterval());
        log.info("session.getCreationTime()={}", session.getCreationTime());
        log.info("session.getLastAccessedTime()={}", session.getLastAccessedTime());
        log.info("session.isNew()={}", session.isNew());

        return "세션 조회 성공!";
    }
    
}
  • session 정보
    1. session.getId();
      • jsessionId 값을 조회할 수 있다.
    2. session.getMaxInactiveInterval();
      • 세션의 유효시간
      • second 단위 default는 30분(1800초)이다.
    3. session.getCreationTime();
      • 세션 생성시간
      ex) Sat Dec 9 15:40:23 KST 2024
    4. session.getLastAccessedTime();
      • 해당 세션에 마지막으로 접근한 시간
      ex) Sat Dec 9 15:40:23 KST 2024
    5. session.isNew();
      • 새로 생성된 세션인지 여부

✅ 문제점, 한계

📚 Session은 logout 기능을 사용하여 session.invalidate(); 가 되어야 삭제되지만 대부분의 사용자들은 로그아웃을 굳이 하지않고, 브라우저를 종료한다.

  • Session의 문제점
    • HTTP는 Connectionless 특성을 가지고 있어서 서버가 브라우저 종료 여부를 판별하지 못한다.
    • 서버에서 Session을 언제 삭제해야 하는지 판단하기 힘들다.
    • JSESSIONID의 값을 탈취 당한 경우 해당 값으로 악의적인 요청을 할 수 있다.
    • 세션은 서버 메모리에 생성되고 자원은 한정적이기 때문에 꼭 필요한 경우만 생성해야 한다.
    ex) 로그인한 유저가 100만명 이라면..? 💥 서버 다운
  • Session 생명주기 ❗ ❗
    • 기본적으로 30분을 기준으로 세션을 삭제한다.
    • 실제 로그인 후 30분 이상의 시간동안 사용중인 사용자의 세션 또한 삭제된다.
    • 다시 로그인 해야하는 경우가 발생한다.
  • HttpSession 사용
    • 세션 생성시점 30분이 아닌 서버에 최근 Session을 요청한 시간을 기준으로 30분을 유지한다.
    • HttpSession은 기본적으로 해당 방식으로 세션의 생명주기를 관리한다.
    • Session 정보에서 LastAccessedTime 을 기준으로 30분이 지나면 WAS가 내부적으로 세션을 삭제한다.
    • 마치, 고용24, 금융 웹 페이지에서 사용자가 아무런 동작이 없으면 30분후 자동으로 로그아웃되는 것.

✅ 인증/인가, 쿠키, 세션 정리

  1. 인증(Authentication)
    • 사용자가 누구인지 확인하는 과정
    ex) 로그인
  2. 인가(Authorization)
    • 사용자가 어떤 권한을 가지고 있는지 결정하는 과정
    • 반드시 인증이 선행되어야 한다.
    ex) 회원만 조회 가능한 게시글, 본인이 작성한 게시글 수정
  • 쿠키(Cookie)
    • 정의
      • 웹 브라우저(Client 측)에 저장되는 데이터
    • 용도
      • 사용자의 방문 기록, 로그인 상태 유지, 개인 맞춤 설정 등을 저장, HTTP 특성 극복, 광고 정보
    • 특징
      • 클라이언트 측에 저장되며, 서버에 요청을 보낼 때마다 포함되어 전송됨.
    • 수명
      • 만료 날짜 설정할 수 있음, 세션 쿠키(브라우저 종료 시 삭제)와 영속 쿠키(지정된 기간 동안 유지)로 구분됨.
  • 세션(Session)
    • 정의
      • 사용자와 서버 간의 상태를 유지하기 위한 방법.
    • 용도
      • 로그인 정보, 사용자 활동 등을 서버 측에서 관리.
    • 특징
      • 서버 측에서 저장되며, 세션 ID가 쿠키에 저장되어 클라이언트와 연결됨.
    • 수명
      • 브라우저를 닫거나 일정 시간이 지나면 만료됨.
    • Session 관리
      1. 세션은 메모리를 사용한다.
        • 세션이 많아지면 서버의 장애가 발생할 수 있다.(메모리 리소스 부족)
        • 최소한의 데이터만 저장해야 한다.
      2. HttpSession은 LastAccessedTime을 기준으로 30분의 생명주기를 가지고 있다.
      3. 세션의 시간을 너무 오래 유지하여도 메모리 리소스가 부족할 수 있다.
        • 적당한 시간 설정이 꼭 필요하다.(Default 30분)

2️⃣ Token

✅ Token

Web Application이나 API에서 인증(Authentication)과 인가(Authorization) 과정에서 사용되며 사용자 또는 시스템의 신원과 권한을 증명하고 요청의 유효성을 검증하는 데 사용되는 디지털 문자열

  • Token을 사용하는 이유
    1. Token은 서버가 아닌 클라이언트에 저장되어 서버의 부담을 덜 수 있다.
    2. Cookie는 웹 브라우저에만 존재하여 모바일 앱 등의 다양한 클라이언트에서 인증을 처리할 수 없다.
    3. Token 방식은 Stateless를 기반으로 하여 확장성이 뛰어나다.
    4. 인증된 사용자임을 확인하기 위한 고유한 서명을 포함하여 위조된 요청인지 확인할 수 있다.
  • Token의 단점
    1. Cookie/Session 방식보다 Token 자체의 데이터 용량이 많다.
      • 요청이 많아지면 그만큼 트래픽이 증가한다.
    2. Payload(전송되는 데이터)는 암호화되지 않아서 중요한 데이터를 담을 수 없다.
    3. Token을 탈취당하면 대처하기 어려워 만료 시간(30분)을 설정한다.

3️⃣ JWT ( JSON Web Token )

✅ JWT 

인증에 필요한 정보들을 암호화시킨 JSON 형태의 Token을 의미.

JSON 데이터 포맷을 사용해 정보를 효율적으로 저장하고 암호화로 서버의 보안성을 높임.

JWT 구조

XXXXXX.YYYYYY.ZZZZZZ
(Header).(Payload).(Signature)

1. Header

  • 토큰의 타입과 해싱 알고리즘을 정의한다.
{
	"alg": "HS256",
	"typ": "JWT"
}

2. Payload 

  • 실제로 인증과 관련된 데이터(Claims)를 담고 있다.
  • Claims의 종류
    • Registered Claims : 미리 정의된 Claims
      • iss(issuer) : 발행자
      • exp(expiration time) : 만료시간
      • sub(subject) : 제목
      • iat(issued At) : 발행 시간
      • jti(JWT ID) : 토큰의 고유 식별자
    • Public Claims : 사용자가 정의할 수 있는 클레임, 공개용 정보 전달 목적
    • Private Claims : 사용자 지정 클레임, 당사자들 간에 정보를 공유하기 위한 목적
{
  "sub": "1234567890",
  "name": "Sparta",
  "exp": 1682563600
}

3. Signature ( 서버 )

  • Header와 Payload를 서버의 Secret Key로 서명하여 암호화 한다.
  • 암호화는 Header에서 정의한 알고리즘(alg)을 활용한다.
  • 서명을 통해 서버는 Token이 변조되지 않았음을 확인할 수 있다.
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret
)

 

💡 base64UrlEncode는 값을 URL에서 사용할 수 있도록 +, / 를 각각 -, _로 표기한다.

💡 Header와 Payload는 Encoding된 값이기 때문에 복호화 혹은 값을 수정할 수 있지만 Signature는 서버에서 관리하는 값이기 때문에 Secret Key가 유출되지 않는 이상 복호화 할 수 없다.

✅ JWT 인증

JWT는 Base64로 인코딩되어 쉽게 복호화 할 수 있다.

Payload가 그대로 노출되기 때문에 비밀번호나 민감한 정보를 저장하지 않는다.

JWT 인증 과정

  1. 클라이언트의 로그인 요청
  2. 로그인에 성공했다면 Header, Payload에 Secret Key를 사용하여 Signature를 만든다.
    • 이후 Base64로 Encoding 한다.
    • 일반적으로 Cookie에 담아 클라이언트에게 JWT를 발급한다.
  3. 발급받은 JWT를 저장 후 서버에 요청할 때 Authorization Header에 JWT를 담아 보낸다.
  4. 서버에서 JWT의 유효성 검사를 통해 통과한다면 인증에 성공하여 요청을 처리해준다.
    • JWT 만료, 위변조 여부를 검사한다.

JWT 유효성 검사

  1. A의 JWT를 B가 탈취
  2. B가 탈취한 JWT를 임의로 수정
  3. B가 수정한 JWT로 Server에 요청
  4. 서버는 Signature를 사용하여 유효성 검사(Signature 불일치)
    • Header, Payload를 서버의 Secret Key값을 이용해 Signature를 다시 만들어 비교한다.
    • 임의로 조작된 데이터를 판별할 수 있다.

💡 JSON Web Token의 목적은 정보 보호가 아닌, 위조 방지에 있다.

✅ JWT 장단점

  • JWT 장점
    1. Signature로 서버의 보안성이 증가한다.
    2. Token 자체가 필요한 정보(유저 및 검증 정보)들을 모두 가지고 있다.
    3. 서버는 인증 정보와 관련된 별도의 저장소를 사용하지 않는다.
    4. 서버의 수평 확장성(Scale Out)이 높아진다.
    5. Cookie가 없는 다른 환경에서도 인증/인가를 적용할 수 있다.
    6. DB를 조회하지 않아도 된다.

💡 Mobile의 경우 App을 자주 닫거나 백그라운드로 전환하여 Session 방식을 사용하지 않는다.

  • JWT 단점
    1. Payload는 암호화 된 것이 아니라 민감한 정보를 다루지 못한다.
    2. Token의 길이가 길어서 트래픽이 증가하면 네트워크에 부하가 증가한다.
    3. 클라이언트 측에서 Token을 관리하기 때문에 탈취당하면 대처하기 어렵다.

✅ Access Token, Refresh Token

Token은 클라이언트에서 관리하여 탈취당할 위험성이 높기 때문에 만료 시간 설정이 필요

➡  발생하는 단점을 극복하기 위해 Access Token과 Refresh Token을 사용

  • Token의 유형
    1. Access Token
      • 사용자 인증 후 서버가 발급하는 유저 정보가 담긴 토큰이다.
      • 유효 기간 동안 API나 리소스에 접근할 때 사용한다.
    2. Refresh Token
      • Access Token은 보안을 위해 짧은 수명을 가진다.
      • Access Token이 만료된 경우 재발급 받기위해 사용한다.
      • 주로 데이터베이스에 유저 정보와 같이 저장한다.

⚡ Access Token, Refresh Token 인증

  1. 클라이언트의 로그인 요청
  2. 로그인에 성공했다면 Header, Payload에 Secret Key를 사용하여 Signature를 만든다.
  3. 발급받은 JWT를 저장 후 서버에 요청할 때 Authorization Header에 JWT(Access Token)를 담아 보낸다.
  4. 서버에서 JWT의 유효성 검사를 통해 통과한다면 인증에 성공하여 요청을 처리해준다.
  5. Access Token이 만료 되었다면 Refresh Token 으로 토큰 재발급을 요청한다.
  6. 서버로부터 Access Token을 재발급 받는다.

💡 JWT를 Access Token만을 사용하여 인증한다면 탈취되어 보안에 취약 💥 . 유효 시간을 부여해  보안 문제를 해결하지만 유효 시간이 짧다면 로그인을 자주 해야하기 때문에 Refresh Token을 적용한다.

Access Token + Refresh Token을 둘다 사용해주는게 바람직하다.

반응형

'[내일배움캠프-Sparta] > Spring 6기' 카테고리의 다른 글

TIL 30 [ Spring ( Data JPA ), 스탠다드반 ( Optional ) ]  (0) 2025.04.01
TIL 29 [ Spring ( Filter, 객체와 RDB, JPA , 영속성 컨텍스트, Entity 제작 ) ]  (0) 2025.03.31
TIL 28 [ Spring ( Bean Validation, Cookie ) ]  (0) 2025.03.28
TIL 27 [ Spring ( SOLID 객체지향, Container와 Bean, 싱글톤 ) + 스탠다드반 (DI / IOC) ]  (0) 2025.03.27
TIL 26 [ Spring 일정관리 앱 과제 해설 + 학습법 & 동기부여 세션 ]  (0) 2025.03.26
'[내일배움캠프-Sparta]/Spring 6기' 카테고리의 다른 글
  • TIL 30 [ Spring ( Data JPA ), 스탠다드반 ( Optional ) ]
  • TIL 29 [ Spring ( Filter, 객체와 RDB, JPA , 영속성 컨텍스트, Entity 제작 ) ]
  • TIL 28 [ Spring ( Bean Validation, Cookie ) ]
  • TIL 27 [ Spring ( SOLID 객체지향, Container와 Bean, 싱글톤 ) + 스탠다드반 (DI / IOC) ]
dimenshun
dimenshun
한 소년의 개발 일기
    반응형
  • dimenshun
    Dev Life Notes
    dimenshun
  • 전체
    오늘
    어제
    • 분류 전체보기 (268)
      • CS (23)
        • 자료구조 (0)
        • 알고리즘 (0)
        • 컴퓨터 구조 (8)
        • 네트워크 (6)
        • 운영체제 (3)
        • DB ( + SQLD ) (5)
        • SW공학 (1)
      • 프로그래밍 (3)
        • Java (0)
        • Spring (0)
        • HTML,CSS (3)
        • JavaScript (0)
      • 개발 툴 (7)
        • Git(버전관리) (1)
        • Docker (3)
        • AWS (2)
        • JSP (1)
      • 코딩테스트(Algorithm) (125)
        • 백준 (6)
        • 프로그래머스 (119)
      • [내일배움캠프-Sparta] (110)
        • Spring 6기 (106)
        • KPT 회고 (3)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    배포
    세션
    Til
    네트워크
    Testcode
    Python
    SQL
    spring
    웹
    KPT
    개발자
    CPU
    SQLD
    메모리
    코딩테스트
    AWS
    cs
    알고리즘
    It
    network
    Java
    내일배움캠프
    db
    OS
    docker
    컴퓨터구조
    운영체제
    web
    백엔드
    트랜잭션
  • hELLO· Designed By정상우.v4.10.3
dimenshun
Spring ( Session, Token, JWT )
상단으로

티스토리툴바