- 테스트베드 프록시 설정 키 및 기능 추가(DjbApiSpecController)
eapim-admin CI / build (push) Waiting to run

- 인증 스킴 라벨 판별 로직(schemeAuthLabel) 및 프록시 사용 여부 표시 로직 추가
- JSP/JS: 프록시 여부 관련 UI 및 데이터 연계 처리
This commit is contained in:
Rinjae
2026-07-23 20:26:11 +09:00
parent 406dd34e28
commit 26679cf649
3 changed files with 64 additions and 8 deletions
+38 -8
View File
@@ -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 =
'<div class="mb-3 text-xs bg-slate-50 border border-slate-200 rounded p-2 leading-relaxed text-slate-600">'
+ '<div>Swagger 테스트 호출: <b>' + (proxyOn ? '포탈 프록시 경유' : '직접 호출') + '</b>'
+ (proxyOn ? ' — <code class="font-mono">/api/call-api</code> 가 인증 토큰·헤더를 대신 주입합니다.'
: ' — 브라우저가 대상 주소로 직접 호출(대상 CORS 허용 필요).') + '</div>'
+ '<div>토큰 발급: <b>' + (tokenProxyOn() ? '포탈 프록시 경유' : '직접 호출') + '</b></div>'
+ '<div class="text-slate-400 mt-1">프록시 여부는 포탈 설정 <code class="font-mono">djb.gateway.use-proxy</code> / <code class="font-mono">djb.gateway.token-use-proxy</code> 로 제어됩니다.</div>'
+ '</div>';
return card(
sectionTitle('인증 / 보안', '게이트웨이(GW)에 설정된 인증만 표시됩니다. (읽기 전용)') +
notice +
(schemes.length
? schemes.map((s, i) => renderSecuritySchemeCard(s, i)).join('')
: '<div class="text-sm text-slate-500 py-2">인증 없음</div>')
);
}
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 @@
<div class="border border-slate-200 rounded-lg p-3 mb-3 locked-card">
<div class="flex items-center gap-2 mb-3">
<code class="text-sm font-semibold text-slate-800">${escapeHtml(s.key)}</code>
<span class="px-2 py-0.5 text-[10px] uppercase rounded ${typeBadge}">${escapeHtml(s.type)}</span>
<span class="px-2 py-0.5 text-[10px] uppercase rounded ${auth.cls}">${escapeHtml(auth.label)}</span>
<span class="text-xs text-slate-400 ml-1">🔒 GW</span>
</div>
${body}
@@ -1030,7 +1056,9 @@
).join('') +
(rt === 'sample'
? `<div class="mt-2 text-xs text-slate-500">응답 샘플을 바로 리턴합니다.</div>`
: `<div class="mt-2 text-xs text-slate-500">실제 호출 주소: <code id="resolved-call-url" class="font-mono text-slate-700 break-all">${escapeHtml(resolvedCallUrl())}</code></div>`) +
: `<div class="mt-2 text-xs text-slate-500">실제 호출 주소: <code id="resolved-call-url" class="font-mono text-slate-700 break-all">${escapeHtml(resolvedCallUrl())}</code></div>`
+ `<div class="mt-1 text-xs text-slate-500">호출 방식: <b>${useProxyOn() ? '포탈 프록시 경유' : '직접 호출'}</b>`
+ (useProxyOn() ? ' (<code class="font-mono">/api/call-api</code> 경유, 인증 헤더 자동 주입)' : ' (브라우저 → 대상 직접 호출, 대상 CORS 허용 필요)') + `</div>`) +
(rt === 'gw' ? `<div class="mt-1 text-xs text-slate-400">GW 주소는 포탈 설정(PTL_PROPERTY, 그룹 Portal)의 <code class="font-mono">djb.gateway.base-url</code> 값을 사용합니다.</div>` : '') +
'</div>') +
(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();
@@ -67,6 +67,8 @@
saveUrl: "<c:url value='/onl/transaction/apim/djbApiSpecMan.json'/>?cmd=SAVE&serviceType=APIGW",
swaggerBase: "<c:url value='/plugins/swaggerUI/'/>",
gwAddress: "${gwAddress}",
useProxy: ${useProxy},
tokenUseProxy: ${tokenUseProxy},
orgPopupUrl: "<c:url value='/onl/transaction/apim/apiSpecMan.view'/>",
groupPopupUrl: "<c:url value='/onl/transaction/apim/djbApiSpecMan.view'/>?cmd=GROUP_POPUP",
jsonUrl: "<c:url value='/onl/transaction/apim/djbApiSpecMan.json'/>"
@@ -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() {