인증
인증(Authentication)이란 사용자가 누구인지 신원을 확인하는 과정으로, "너 누구야?"에 대한 검증입니다.
핵심 개념
[현실 세계 비유]
신분증 제시 → 본인 확인 → 인증
출입증 확인 → 출입 가능 여부 → 인가
[웹 서비스]
아이디/비밀번호 입력 → 서버가 본인 확인 → 인증
인증된 사용자가 API 접근 → 권한 확인 → 인가
인증은 항상 인가보다 먼저 수행되며, 인증 없이 인가는 불가능합니다.
인증의 세 가지 요소
| 요소 | 설명 | 예시 |
|---|---|---|
| 지식 기반 (Something you know) | 사용자만 아는 정보 | 비밀번호, PIN, 보안 질문 |
| 소유 기반 (Something you have) | 사용자만 가진 물건 | 휴대폰 OTP, 인증서, 보안 카드 |
| 속성 기반 (Something you are) | 사용자의 고유 특성 | 지문, 홍채, 얼굴 인식 |
[단일 요소 인증]
비밀번호만으로 인증 → 탈취 시 바로 뚫림
[다중 요소 인증 MFA — 2개 이상 조합]
비밀번호(지식) + 휴대폰 OTP(소유) → 하나가 탈취돼도 안전
웹 서비스의 대표적 인증 방식
1. 세션 기반 인증
클라이언트 → 서버: 아이디/비밀번호 전송
서버: DB 조회 → 비밀번호 검증 → 세션 생성
서버 → 클라이언트: Set-Cookie: JSESSIONID=abc123
이후 요청: Cookie: JSESSIONID=abc123 → 서버가 세션 조회로 인증 확인
@PostMapping("/login")
public ResponseEntity<String> login(@RequestBody LoginRequest request,
HttpSession session) {
Member member = memberRepository.findByLoginId(request.getLoginId())
.orElseThrow(() -> new IllegalArgumentException("사용자 없음"));
if (!passwordEncoder.matches(request.getPassword(), member.getPassword())) {
throw new IllegalArgumentException("비밀번호 불일치");
}
session.setAttribute("loginMember", member.getId());
return ResponseEntity.ok("로그인 성공");
}
2. 토큰 기반 인증 (JWT)
클라이언트 → 서버: 아이디/비밀번호 전송
서버: DB 조회 → 비밀번호 검증 → JWT 발급
서버 → 클라이언트: { "accessToken": "eyJhbG..." }
이후 요청: Authorization: Bearer eyJhbG... → 서버가 서명 검증으로 인증 확인
@PostMapping("/login")
public ResponseEntity<TokenResponse> login(@RequestBody LoginRequest request) {
Member member = memberRepository.findByLoginId(request.getLoginId())
.orElseThrow(() -> new IllegalArgumentException("사용자 없음"));
if (!passwordEncoder.matches(request.getPassword(), member.getPassword())) {
throw new IllegalArgumentException("비밀번호 불일치");
}
String token = jwtProvider.createToken(member.getId(), member.getRole());
return ResponseEntity.ok(new TokenResponse(token));
}
3. OAuth 2.0 (소셜 로그인)
사용자 → 클라이언트: "구글로 로그인"
클라이언트 → 구글: 인증 요청 (리다이렉트)
사용자 → 구글: 구글 아이디/비밀번호 입력
구글 → 클라이언트: 인가 코드 발급
클라이언트 → 구글: 인가 코드로 Access Token 요청
구글 → 클라이언트: Access Token 발급
클라이언트 → 구글 API: Access Token으로 사용자 정보 조회
→ 자체 서비스에서 세션 또는 JWT 발급
직접 비밀번호를 관리하지 않고, 신뢰할 수 있는 제3자(Google, Kakao 등)에게 인증을 위임하는 방식입니다.
인증 방식 비교
| 항목 | 세션 기반 | JWT 기반 | OAuth 2.0 |
|---|---|---|---|
| 인증 정보 저장 | 서버 (메모리/Redis) | 클라이언트 (토큰) | 외부 인증 서버 |
| Stateless | X | O | 방식에 따라 다름 |
| 서버 확장 | 세션 공유 필요 | 자유로움 | 자유로움 |
| 강제 로그아웃 | 쉬움 | 어려움 | 제한적 |
| 적합 대상 | SSR 웹 서비스 | SPA, 모바일, API | 소셜 로그인, 서드파티 연동 |
비밀번호 저장 — 인증의 핵심 보안
// X 절대 하면 안 되는 것 — 평문 저장
member.setPassword("1234");
// O 올바른 방법 — 단방향 해시 저장
member.setPassword(passwordEncoder.encode("1234"));
// → $2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
// 검증 시 해시 비교
passwordEncoder.matches("1234", member.getPassword()); // true
[왜 단방향 해시인가]
평문 저장 → DB 유출 시 비밀번호 즉시 노출
양방향 암호화 → 복호화 키가 유출되면 전체 비밀번호 노출
단방향 해시 → 원본 복원 불가 → DB 유출돼도 비밀번호를 알 수 없음
BCrypt는 매번 다른 Salt를 자동 생성하므로
같은 비밀번호도 해시 결과가 매번 다름 → 레인보우 테이블 공격 방어
면접 포인트
- 인증(Authentication)은 "누구인가", 인가(Authorization)는 "무엇을 할 수 있는가" 로 명확히 구분해야 합니다.
- 비밀번호는 반드시 BCrypt 등 단방향 해시로 저장해야 하며, 평문 저장이나 양방향 암호화는 보안상 부적절합니다.
- 실무에서는 단일 방식만 사용하기보다 OAuth로 소셜 로그인 + JWT로 자체 인증 같은 조합을 사용하는 것이 일반적입니다.