# bibimbap 인증: 자체 PBKDF2 해시 (BCrypt 아님)

Spring Security 미사용. 로그인은 `UserController` 자체 구현 password 검증.

password_hash 포맷: `pbkdf2_sha256$<iter>$<base64(salt)>$<base64(hash)>` ($ 4필드)
- 알고리즘 `PBKDF2WithHmacSHA256`, iterations=210000, keyLength=256bit(=32byte), salt=16byte SecureRandom
- base64 = 표준(padding 포함). 검증: `UserController.verifyPassword`
  (`src/main/java/com/pandoli365/bibimbap/controller/api/UserController.java:46-48, 550-584`).
  parts[0]=="pbkdf2_sha256" && len==4 확인 → 동일 iter/salt 재계산 → `MessageDigest.isEqual` 상수시간 비교.

## 함정 (Why save)
외부 BCrypt 생성기로 만든 해시는 **무효** — 형식 자체가 다름. 코드 안 보면 "BCrypt 일 것"이라는
기본 가정이 강해, 로그인 가능 더미/테스트/관리자 계정 seed 마다 반복적으로 부딪힌다.

## 로그인 가능 계정 seed 시 필수 조건
- `users.status = 'ACTIVE'` (UserController.java:148-149, 미충족 시 로그인 거부)
- `user_auth_identities.provider = 'email'` (단일 provider, UserController.java:42)
- `provider_user_id` = 정규화(trim + lowercase) 이메일 (UserController.java:527-533)
- 양쪽 `is_delete = false`
- 세션 귀속 키: 로그인 성공 시 session attribute `userId`(Long) 저장 → 리뷰/댓글 작성 가드가 이 키를 읽음.

## 평문→해시 생성 (Python, Java 와 byte-identical)
```python
import hashlib, base64, os
salt = os.urandom(16)
dk = hashlib.pbkdf2_hmac('sha256', pw.encode(), salt, 210000, dklen=32)
h = f"pbkdf2_sha256$210000${base64.b64encode(salt).decode()}${base64.b64encode(dk).decode()}"
```
로그인 동작 보장은 실제 `verifyPassword` 코드 경로로 positive/negative 둘 다 확인할 것
(호스트 JDK 없으면 `mem:java-crypto-verify-via-jdk-container-when-no-host-jdk` 참조).

근거 세션: .atp/work-session/20260618-121419 (더미 seed). 관련 산출물 `db/seed-dev.sql`.
