domain port만으로는 HTTP 요청이 움직이지 않아요. JDBC와 Spring Security가 바깥 구현을 맡아요.이 글은 Auth API 실습의 2편이에요. 1편의 domain port,
IdentityFacade, token 설정에 JDBC adapter를 붙여요. 이어서 JWT 발급, 두 SecurityFilterChain, controller와 통합 test를 차례대로 추가해요.
1편: domain과 application 준비하기
프로젝트 생성부터 1편 checkpoint까지 아직 끝내지 않았다면 먼저 진행해요.
- 1편에서 이어가기
- 완성본부터 확인하기
auth-api 디렉터리에서 아래 명령이 성공해야 해요.14절까지 진행하려면
curl과 jq가 필요해요. 지금 curl --version과 jq --version을 실행해 두면 서버를 띄운 뒤 도구 설치 때문에 흐름이 끊기지 않아요.8. domain port를 JDBC adapter로 구현해요
여기까지 안쪽 코드는 “회원을 저장해 달라”, “활성 refresh token을 찾아 달라”고 요청만 했어요. 이 절에서는 준비된 설정과 schema를 사용해 그 요청을 실제 DB 작업으로 바꿔요.UserAccountRepository와 RefreshTokenRepository의 실제 DB 구현을 만들어요. Spring Data가 interface를 대신 구현하게 하지 않고 JdbcClient로 SQL을 눈에 보이게 적어요.
src/main/java/me/nvim/blog/auth/identity/infrastructure/jdbc
JdbcInstantReader.java
JdbcRefreshTokenRepository.java
JdbcUserAccountRepository.java
EMAIL_ALREADY_USED 업무 오류로 바꿔요. 아래 세 파일을 모두 만든 뒤 refresh token의 조회와 폐기 조건을 살펴볼게요.
여기서 자세히 볼 쪽은 refresh token이에요. 원문에 application secret을 섞어 HMAC-SHA-256을 계산하고, DB에는 16진수 hash만 저장해요. 활성 token 조회와 폐기의 핵심 SQL은 다음 두 조건을 공유해요.
JdbcInstantReader.java
JdbcInstantReader.java
src/main/java/me/nvim/blog/auth/identity/infrastructure/jdbc/JdbcInstantReader.java
JdbcRefreshTokenRepository.java
JdbcRefreshTokenRepository.java
src/main/java/me/nvim/blog/auth/identity/infrastructure/jdbc/JdbcRefreshTokenRepository.java
JdbcUserAccountRepository.java
JdbcUserAccountRepository.java
src/main/java/me/nvim/blog/auth/identity/infrastructure/jdbc/JdbcUserAccountRepository.java
findActive로 읽은 뒤에도 revokeIfActive의 update 결과를 다시 확인하는 이유는 동시 요청 때문이에요. 같은 token을 두 요청이 함께 읽더라도 updated == 1을 얻은 한 요청만 새 token으로 진행할 수 있어요. 저장과 조회가 모두 같은 hash(rawToken)을 호출하므로 DB에는 원문이 남지 않아요.
9. password와 token 발급 adapter를 연결해요
DB adapter가 저장 port를 채웠다면, 아직 비어 있는 기술 경계는 password hash와 인증이에요. domain은 password를 어떤 algorithm으로 hash하는지, JWT를 어떤 library로 서명하는지 몰라야 하죠. 이번에는 그 port들을 Spring Security 구현과 연결할게요. domain port와 Spring Security 사이를 잇는 네 class를 만들어요.src/main/java/me/nvim/blog/auth/identity/infrastructure/security
DatabaseUserDetailsService.java
JwtTokenIssuer.java
SpringPasswordHasher.java
UserPrincipal.java
UserPrincipal은 UserAccount를 UserDetails로 바꾸고 role 문자열을 GrantedAuthority로 옮겨요. DatabaseUserDetailsService는 email을 소문자로 정규화해 계정을 찾고, SpringPasswordHasher는 domain의 PasswordHasher를 PasswordEncoder로 구현해요. 네 파일은 모두 필요하며, JwtTokenIssuer가 실제 token 모양을 결정해요.
실제로 token 모양을 결정하는 JwtTokenIssuer는 본문에서 볼게요.
JWT adapter는 RSA private key로 access token을 서명하고 32 byte 무작위 refresh token을 만들어요. JWT에는 iss, sub, aud, iat, exp와 우리 claim인 email, roles가 들어가요.
UserPrincipal.java
UserPrincipal.java
src/main/java/me/nvim/blog/auth/identity/infrastructure/security/UserPrincipal.java
DatabaseUserDetailsService.java
DatabaseUserDetailsService.java
src/main/java/me/nvim/blog/auth/identity/infrastructure/security/DatabaseUserDetailsService.java
SpringPasswordHasher.java
SpringPasswordHasher.java
src/main/java/me/nvim/blog/auth/identity/infrastructure/security/SpringPasswordHasher.java
JwtTokenIssuer.java
JwtTokenIssuer.java
src/main/java/me/nvim/blog/auth/identity/infrastructure/security/JwtTokenIssuer.java