diff --git a/WebContent/js/djb/apispec/app.js b/WebContent/js/djb/apispec/app.js index 4d2ba55..9bb7dca 100644 --- a/WebContent/js/djb/apispec/app.js +++ b/WebContent/js/djb/apispec/app.js @@ -25,6 +25,10 @@ components: [], // 재사용 스키마(POC: 빈 상태) }; + // GW authtype(oauth/api_key 등, 서버 DETAIL_SPEC 응답). 인증 badge 라벨 판별용. + // (enricher 는 oauth·api_key 를 모두 apiKey-in-header 로 모델링하므로 scheme.type 만으로 구분 불가) + var djbAuthType = ''; + // ============================================================ // 2. 유틸 // ============================================================ @@ -755,22 +759,44 @@ // ============================================================ // 9. 인증 / 보안 (기본정보 Step 에 병합된 섹션) // ============================================================ + // 테스트베드 프록시 사용 여부(포탈 djb.gateway.use-proxy / token-use-proxy). DJB_CTX 미지정 시 기본 true. + function useProxyOn() { return !(window.DJB_CTX && window.DJB_CTX.useProxy === false); } + function tokenProxyOn() { return !(window.DJB_CTX && window.DJB_CTX.tokenUseProxy === false); } + + // 인증 스킴 → 실제 인증방식 라벨/색상. GW enricher 는 oauth·api_key 를 모두 apiKey-in-header 로 모델링하므로 + // scheme.type("apiKey")만으로는 구분 불가 → authtype(djbAuthType) 및 scheme key(djbOAuth/djbApiKey)로 판별. + function schemeAuthLabel(s) { + var at = (djbAuthType || '').trim().toLowerCase(); + if (at === 'oauth' || at === 'oauth2' || at === 'ca' || s.key === 'djbOAuth') { + return { label: 'OAUTH', cls: 'bg-purple-100 text-purple-700' }; + } + if (at === 'api_key' || at === 'apikey' || s.key === 'djbApiKey') { + return { label: 'API KEY', cls: 'bg-emerald-100 text-emerald-700' }; + } + return { label: (s.type || '').toUpperCase(), cls: 'bg-slate-100 text-slate-700' }; + } + function renderAuthSection() { var schemes = state.data.securitySchemes || []; + var proxyOn = useProxyOn(); + var notice = + '
' + + '
Swagger 테스트 호출: ' + (proxyOn ? '포탈 프록시 경유' : '직접 호출') + '' + + (proxyOn ? ' — /api/call-api 가 인증 토큰·헤더를 대신 주입합니다.' + : ' — 브라우저가 대상 주소로 직접 호출(대상 CORS 허용 필요).') + '
' + + '
토큰 발급: ' + (tokenProxyOn() ? '포탈 프록시 경유' : '직접 호출') + '
' + + '
프록시 여부는 포탈 설정 djb.gateway.use-proxy / djb.gateway.token-use-proxy 로 제어됩니다.
' + + '
'; return card( sectionTitle('인증 / 보안', '게이트웨이(GW)에 설정된 인증만 표시됩니다. (읽기 전용)') + + notice + (schemes.length ? schemes.map((s, i) => renderSecuritySchemeCard(s, i)).join('') : '
인증 없음
') ); } function renderSecuritySchemeCard(s, idx) { - const typeBadge = { - apiKey: 'bg-emerald-100 text-emerald-700', - http: 'bg-blue-100 text-blue-700', - oauth2: 'bg-purple-100 text-purple-700', - openIdConnect: 'bg-slate-100 text-slate-700' - }[s.type] || 'bg-slate-100 text-slate-700'; + const auth = schemeAuthLabel(s); let body = ''; if (s.type === 'apiKey') { @@ -797,7 +823,7 @@
${escapeHtml(s.key)} - ${escapeHtml(s.type)} + ${escapeHtml(auth.label)} 🔒 GW
${body} @@ -1030,7 +1056,9 @@ ).join('') + (rt === 'sample' ? `
응답 샘플을 바로 리턴합니다.
` - : `
실제 호출 주소: ${escapeHtml(resolvedCallUrl())}
`) + + : `
실제 호출 주소: ${escapeHtml(resolvedCallUrl())}
` + + `
호출 방식: ${useProxyOn() ? '포탈 프록시 경유' : '직접 호출'}` + + (useProxyOn() ? ' (/api/call-api 경유, 인증 헤더 자동 주입)' : ' (브라우저 → 대상 직접 호출, 대상 CORS 허용 필요)') + `
`) + (rt === 'gw' ? `
GW 주소는 포탈 설정(PTL_PROPERTY, 그룹 Portal)의 djb.gateway.base-url 값을 사용합니다.
` : '') + '
') + (rt === 'mock' ? fieldRow('Mock Server URL', input(p.mockUrl || '', 'data-portal="mockUrl"', { placeholder: 'https://mock.example.com/api/v1/foo' }), { hint: 'Mock 응답을 제공할 전체 호출 URL(경로 포함). 응답 유형이 Mock 일 때 이 주소로 그대로 호출하며, 어댑터/오퍼레이션 경로를 뒤에 덧붙이지 않습니다.' }) : '') @@ -1950,6 +1978,7 @@ if (res && res.testbedSpec) { try { state.data = specToData(JSON.parse(res.testbedSpec)); } catch (e) { console.error('specToData', e); } } + djbAuthType = (res && res.authType) || ''; // 인증 badge 라벨 판별용(oauth/api_key) // 자동생성 규칙(title/tag/summary=API명, 예제, 서버prepend, opId)은 백엔드에서 적용됨 → 그대로 로드 state.step = 1; state.responseStatusTab = state.data.responses && state.data.responses['200'] ? '200' : (state.data.responses ? Object.keys(state.data.responses)[0] : '200'); var _nm = decodeEntities(ctx.apiName || '') || state.data.info.title.value || ''; @@ -2029,6 +2058,7 @@ try { state.data = specToData(JSON.parse(res.testbedSpec)); } catch (e) { toast('자동 생성 실패: 스펙 파싱 오류', 'error'); return; } } + djbAuthType = (res && res.authType) || ''; // 인증 badge 라벨 판별용(oauth/api_key) // 구조(title/tag/summary/opId/스키마/보안/헤더)는 백엔드(generateSpec)가 적용한 기초 스펙 그대로 로드. // 추가자료(예제·설명자료)는 프론트에서 스키마 기준 생성 → 재생성 버튼과 동일 결과. frontAutoGenerate(); diff --git a/WebContent/jsp/onl/transaction/apim/djbApiSpecManPopup.jsp b/WebContent/jsp/onl/transaction/apim/djbApiSpecManPopup.jsp index 63db71b..4338767 100644 --- a/WebContent/jsp/onl/transaction/apim/djbApiSpecManPopup.jsp +++ b/WebContent/jsp/onl/transaction/apim/djbApiSpecManPopup.jsp @@ -67,6 +67,8 @@ saveUrl: "?cmd=SAVE&serviceType=APIGW", swaggerBase: "", gwAddress: "${gwAddress}", + useProxy: ${useProxy}, + tokenUseProxy: ${tokenUseProxy}, orgPopupUrl: "", groupPopupUrl: "?cmd=GROUP_POPUP", jsonUrl: "" diff --git a/src/main/java/com/eactive/eai/rms/onl/transaction/apim/DjbApiSpecController.java b/src/main/java/com/eactive/eai/rms/onl/transaction/apim/DjbApiSpecController.java index b25c0e9..428bb0e 100644 --- a/src/main/java/com/eactive/eai/rms/onl/transaction/apim/DjbApiSpecController.java +++ b/src/main/java/com/eactive/eai/rms/onl/transaction/apim/DjbApiSpecController.java @@ -72,6 +72,9 @@ public class DjbApiSpecController extends OnlBaseAnnotationController { // 별도 swagger.gw.address 프로퍼티는 두지 않는다. private static final String GW_BASE_URL_KEY = "djb.gateway.base-url"; private static final String GW_BASE_URL_DEFAULT = "PortalMock"; + // 테스트베드 프록시 사용 여부(포탈 DjbTestbedGatewayProperty 와 동일 키). 마법사에 호출/인증 프록시 경유 안내용. + private static final String USE_PROXY_KEY = "djb.gateway.use-proxy"; + private static final String TOKEN_USE_PROXY_KEY = "djb.gateway.token-use-proxy"; // "PortalMock" 은 포탈이 요청 origin 으로 치환하는 sentinel 이라 admin 미리보기용 서버 주소로는 부적합 → 대체값 사용. private static final String GW_PREVIEW_FALLBACK = "http://127.0.0.1:39310"; @@ -85,9 +88,30 @@ public class DjbApiSpecController extends OnlBaseAnnotationController { gw = GW_PREVIEW_FALLBACK; // PortalMock → admin 미리보기용 구체 주소로 대체 } model.addAttribute("gwAddress", gw.trim()); + // 테스트베드 프록시(/api/call-api) 사용 여부 — mock/gw 호출·인증 프록시 경유 안내 표기용 + model.addAttribute("useProxy", resolveBoolProperty(USE_PROXY_KEY, true)); + model.addAttribute("tokenUseProxy", resolveBoolProperty(TOKEN_USE_PROXY_KEY, true)); return "/onl/transaction/apim/djbApiSpecManPopup"; } + /** 포탈 그룹 프로퍼티(불리언, 레거시 Y/N 포함)를 읽어 boolean 으로 해석. 미설정/파싱불가 시 default. */ + private boolean resolveBoolProperty(String key, boolean def) { + String v = portalPropertyService.getOrCreateProperty( + "Portal", key, def ? "true" : "false", + "테스트베드 프록시(/api/call-api) 사용 여부(true/false)"); + if (v == null) { + return def; + } + String s = v.trim(); + if (s.equalsIgnoreCase("true") || s.equalsIgnoreCase("Y")) { + return true; + } + if (s.equalsIgnoreCase("false") || s.equalsIgnoreCase("N")) { + return false; + } + return def; + } + // API 그룹 선택 팝업(iframe 모달 대상). 그룹 목록 그리드는 apiGroupMan.json?cmd=LIST 재사용. @GetMapping(value = "/onl/transaction/apim/djbApiSpecMan.view", params = "cmd=GROUP_POPUP") public String viewGroupPopup() {