인증(세션 vs 토큰) 개요
세션(Session) 방식
- 발급 방식:
- 사용자가 로그인을 하면, 서버는 "세션 키"를 발급
- 서버 내부 DB나 메모리에 사용자 id - 세션 키를 매핑해 저장
- 클라이언트에게 세션 키를 전달하고, 클라이언트는 이 키를 쿠키 등에 저장
- 검증 방식:
- 클라이언트 요청 시 세션 키를 전송
- 서버는 자신이 가진 세션 저장소를 참조해 해당 세션 키의 유효성 및 해당 사용자와의 매칭 여부 확인
- 유효한 경우 응답 제공
- 한계:
- 서버 확장(수평 확장) 시 세션 공유 문제 발생
- 세션 정보를 모든 서버에 공유하기 위해 추가적인 DB나 캐시(레디스 등) 필요
JWT(Token) 방식
- JWT란?
- RFC 7519에 정의된 표준화된 토큰 포맷
- 헤더(Header), 페이로드(Payload), 시그니처(Signature)로 구성된 JSON 기반 토큰
- 전자 서명(Digital Signature)을 포함하여 토큰 자체로 인증 유효성 검증 가능 → Stateless한 인증 방식
- 발급 방식:
- 사용자가 로그인을 하면, 서버는 사용자 정보 등을 담은 JWT를 생성
- 생성 시 비밀키(대칭키)나 개인키(비대칭키)를 사용해 서명
- 서명된 JWT를 클라이언트에게 전달
- 검증 방식:
- 클라이언트는 요청 시 Authorization: Bearer <JWT> 헤더로 토큰 전송
- 서버는 토큰을 받아 헤더, 페이로드, 시그니처로 분리 후, 서명 알고리즘(HS256, RS256 등)에 따라 토큰의 무결성 검증
- 서명 검증이 유효하다면, 페이로드의 정보(사용자 id, 만료시간 등) 기반으로 권한 확인 후 응답
- 장점:
- 서버 확장성 우수 (Stateless)
- 별도의 세션 저장소 필요 없음
- 단점:
- 토큰 자체가 탈취당하면 토큰 만료시간 전까지 방어 불가
- 토큰 무효화(Invalidation)를 서버 측에서 강제로 하기 어려움
Refresh 토큰
- 개념:
- Access 토큰보다 긴 유효기간을 가진 토큰
- DB에 저장해두며, 만료 전 재발급 요청 시 유효성 검증 가능
- Access 토큰 만료 시, Refresh 토큰 유효하면 새로운 Access 토큰 재발급 가능
- 필요성:
- Access 토큰 유효기간을 짧게 하여 보안 강화
- 사용자가 자주 로그인하지 않고도 새로운 토큰 발급 가능
- 한계:
- Refresh 토큰도 탈취당하면 방법 없음
- 이중 인증(2FA) 등 별도 보안 레이어 추가 필요
관련 개념 설명
- RFC(Request For Comments):
- 인터넷 기술 표준을 정의하는 문서 시리즈
- JWT는 RFC 7519 문서에 정의됨
- 서명(Signature):
- 토큰의 무결성과 신뢰성을 증명하기 위한 전자적 서명
- "이 토큰은 내가 발급한 것"이라는 서버 측 인증서 같은 역할
- Stateless:
- 서버가 클라이언트 상태를 직접 관리하지 않는 아키텍처 스타일
- JWT를 사용하면 토큰 자체가 상태 정보를 포함하고 서명으로 진위 여부 검증 가능 → 추가 DB 조회 최소화
- HS256, RS256 등 알고리즘:
- HS256: HMAC-SHA256 기반 대칭키 방식(서버에 비밀키 하나)
- RS256: RSA-SHA256 기반 비대칭키 방식(공개키/개인키 쌍 활용)
// 예시를 위한 패키지 설치 (설명용)
// npm install jsonwebtoken
const jwt = require('jsonwebtoken'); // jsonwebtoken 라이브러리 불러오기
// 비밀키 설정 (HS256 방식 예)
// 실제로는 환경변수나 안전한 저장소에 보관해야 함
const SECRET_KEY = 'your_secret_key_here'; // 대칭키
// 1. JWT 발급 함수
function generateJWT(userId) {
// 페이로드 정의
const payload = {
sub: userId, // 사용자 식별자
iat: Math.floor(Date.now() / 1000), // 토큰 발급 시간(Unix Timestamp)
exp: Math.floor(Date.now() / 1000) + (60 * 10) // 10분 후 만료 예제
};
// 토큰 발급
const token = jwt.sign(payload, SECRET_KEY, { algorithm: 'HS256' });
// sign 함수는 페이로드와 비밀키를 통해 서명된 JWT 발급
return token;
}
// 2. JWT 검증 함수
function verifyJWT(token) {
try {
// verify 함수로 토큰 검증
const decoded = jwt.verify(token, SECRET_KEY);
// 비밀키를 사용해 서명이 유효한지 검증, 유효하면 디코딩된 페이로드 반환
return { valid: true, decoded };
} catch (err) {
// 검증 실패 시 에러 처리
return { valid: false, error: err };
}
}
// 예시 동작
const userId = 'user1234'; // 가상의 사용자 아이디
const accessToken = generateJWT(userId); // JWT 발급
console.log('발급된 Access Token:', accessToken);
const verificationResult = verifyJWT(accessToken); // 토큰 검증
if (verificationResult.valid) {
console.log('토큰 유효. 페이로드:', verificationResult.decoded);
} else {
console.log('토큰 무효!', verificationResult.error);
}
Use Flow
1. 유저정보, 시크릿 키로 POST 리퀘 - 액세스토큰, 리프레시 토큰 발급
액세스토큰은 1시간 리프레시 토큰은 7일로 발급
2. 미들웨어 함수를 만듦
- 액세스토큰이 만료되면 리프레시 토큰을 체크할 것
- 리프레시 토큰을 DB에 저장했다면 ( 서버 간 정보 동기화의 필요성 )
ㄴ 맞는지 확인
ㄴ 맞으면 액세스토큰 재발급, 리프레시 토큰 갱신, 업데이트
- 리프레시 토큰을 DB에 저장하지 않았다면 ( Serverless 하나 보안이 위험 )
ㄴ 별도 DB 확인 과정 없이, 맞으면 액세스 토큰 재발급, 리프레시 토큰 갱신
'Frontend' 카테고리의 다른 글
왜 BrowserRouter는 새로고침하면 404 에러가 뜰까 (2) | 2024.11.17 |
---|---|
HashRouter의 #는 어떤 역할을 하는가 (0) | 2024.11.17 |
해시 라우터 그리고 package의 homepage 필드 (0) | 2024.11.17 |
아이폰에서의 100vh는 뷰포트의 높이가 아니다?! (1) | 2024.10.08 |
@import vs link (0) | 2024.09.23 |