| name | register-service |
| description | 새 Spring Boot 서비스를 api-gateway + auth-api 에코시스템에 연결한다.
auth-api 클라이언트 등록 → Gateway 라우팅 추가 → 서비스에 econo-passport 연동 → 동작 확인까지 한 번에 처리.
다음 상황에서 반드시 이 스킬을 사용한다:
- "새 서비스 Gateway에 연결해줘", "새 서비스 auth 연동"
- "서비스 등록해줘", "Gateway 뒤에 붙여줘"
- "/register-service" 직접 호출
- 새 Spring Boot 서비스가 추가되고 인증이 필요할 때
ARGUMENTS: 서비스명, 서비스 경로(선택), 업스트림 URL(선택)
예: "EEOS-BE /Users/mando/study/eeos/EEOS-BE/eeos"
|
register-service
새 서비스를 Gateway 에코시스템에 연결한다. 완료 후 해당 서비스의 모든 API가
at 쿠키 / Bearer AT만으로 인증되고, @PassportAuth Passport passport로 사용자 정보를 받을 수 있다.
전제 조건 파악
시작 전 다음을 확인한다:
- auth-api 주소 —
AUTH_API_ADMIN_URL 환경변수 또는 사용자에게 물어본다 (기본: http://localhost:8081)
- 서비스 업스트림 URL — 서비스가 실행되는 주소 (예:
http://new-service:8080)
- 서비스 경로 접두사 — Gateway에서 이 서비스로 라우팅할 경로 (예:
/api/new-service)
- 서비스 프로젝트 경로 — econo-passport를 추가할 서비스의 소스 경로
Step 1. auth-api에 클라이언트 등록
인증된 에코노 회원이 X-User-Passport 헤더와 함께 호출한다 (Gateway 경유 시 자동 주입).
curl -X POST ${AUTH_API_URL:-http://localhost:8081}/api/v1/clients \
-H "Content-Type: application/json" \
-H "X-User-Passport: <passport>" \
-d '{
"clientName": "<서비스명>",
"redirectUris": ["<redirect URI>"]
}'
응답에서 clientId와 clientSecret(1회 노출) 즉시 저장:
{
"clientId": "...",
"clientSecret": "..."
}
upstreamUrl·pathPrefix·routeId·grantType은 이 요청에 없다. 클라이언트 등록과 라우트 등록은 별개 단계다.
Step 2. Gateway 동적 라우트 등록
GatewayRoutingConfig.java 수정 없이 Admin API로 즉시 반영한다. ADMIN/SUPER_ADMIN Passport가 필요하다.
curl -X POST ${AUTH_API_URL:-http://localhost:8081}/api/v1/admin/routes \
-H "Authorization: Bearer <admin-access-token>" \
-H "Content-Type: application/json" \
-d '{
"pathPrefix": "<경로 접두사>",
"upstreamUrl": "<업스트림 URL>",
"enabled": true
}'
에러 시:
409 ROUTE_PATH_CONFLICT → 이미 등록된 pathPrefix, 다른 경로 사용
400 ROUTE_UPSTREAM_INVALID → SSRF 검증 실패. 허용 스킴(http/https), private IP 차단 확인
403 ROUTE_PROTECTED → 보호 경로 패턴과 충돌. 다른 pathPrefix 사용
이 서비스의 공개 경로(토큰 없이 통과해야 하는 경로)가 있으면 application.yml의 gateway.permitted-paths에 추가 후 api-gateway를 재배포한다.
Step 3. 서비스에 econo-passport 연동
서비스 프로젝트 경로가 주어진 경우 /use-passport 스킬을 호출한다:
/use-passport <서비스명> <프로젝트 경로>
또는 수동으로:
build.gradle.kts
repositories { maven("https://jitpack.io") }
dependencies {
implementation("com.github.JNU-econovation:econo-passport:1.0.3")
}
PassportAuthenticationFilter 등록
SecurityFilterChainConfig(또는 동등한 설정)의 authenticated 체인에 추가:
httpSecurity.addFilterBefore(new PassportAuthenticationFilter(), LogoutFilter.class);
PassportAuthenticationFilter는 @Component로 등록하면 안 됨.
Servlet 필터로 자동 등록 시 SecurityContextHolderFilter 리셋으로 인증 무효화.
MemberArgumentResolver (기존 @Member 방식 서비스)
기존 @Member Long memberId 방식이 있으면 SecurityContext 우선 읽도록 수정:
@Override
public Object resolveArgument(...) {
var auth = SecurityContextHolder.getContext().getAuthentication();
if (auth instanceof JwtAuthentication jwtAuth) {
return jwtAuth.getPrincipal();
}
...
}
Step 4. 동작 확인
동적 라우트 등록은 Gateway 재기동 없이 즉시 반영된다. 아래 테스트로 바로 확인한다.
연결 테스트
curl -c /tmp/test-cookies.txt -X POST http://localhost:8081/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"loginId": "<테스트 계정>", "password": "<비밀번호>"}'
curl -b /tmp/test-cookies.txt \
"http://localhost:8082<pathPrefix>/health-check"
curl -b /tmp/test-cookies.txt \
"http://localhost:8082<pathPrefix>/api/some-endpoint"
완료 체크리스트
트러블슈팅
401이 계속 난다면:
- Gateway 로그 확인: "JWT verification failed" → JWKS 접근 문제
- "Bearer token missing" → at 쿠키가 요청에 없음
at 쿠키 도메인 확인: 로컬에선 COOKIE_SECURE=false 필요
Passport가 서비스까지 안 온다면:
PassportAuthenticationFilter가 @Component로 등록됐는지 확인 → 제거하고 Security 체인에만 등록