인증정보 -> 개발자포탈 정보 반영

This commit is contained in:
Rinjae
2026-01-17 19:33:57 +09:00
parent 07801fe149
commit 11789e861b
5 changed files with 335 additions and 1 deletions
@@ -285,6 +285,56 @@
} }
}); });
$("#btn_sync_portal").click(function() {
var clientId = $('input[name=clientId]').val();
var clientName = $('input[name=clientName]').val();
if (!clientId) {
alert("Client ID가 없습니다.");
return;
}
if (!confirm("'" + clientName + "'의 개발자포탈 정보를 반영하시겠습니까?\n\n" +
"APIGW 인증 정보를 포탈 DB에 반영합니다. (없으면 생성, 있으면 업데이트)")) {
return;
}
$.ajax({
type: "POST",
url: url,
dataType: "json",
data: {
cmd: 'SYNC_PORTAL',
clientId: clientId
},
success: function(response) {
if (response.success) {
var msg = "포탈 정보 반영 완료";
if (response.action === 'INSERTED') {
msg = "포탈 정보를 새로 생성했습니다.";
} else if (response.action === 'UPDATED') {
msg = "포탈 정보를 업데이트했습니다.";
} else if (response.action === 'SKIPPED_NO_ORG') {
msg = "반영을 건너뜁니다.\n법인(orgId)이 설정되지 않은 클라이언트입니다.";
} else if (response.action === 'SKIPPED_INVALID_API') {
msg = "반영을 건너뜁니다.\n존재하지 않는 API가 포함되어 있습니다.\n\n";
if (response.invalidApis && response.invalidApis.length > 0) {
msg += "누락된 API: " + response.invalidApis.join(", ");
}
}
alert(msg);
} else {
alert("반영 실패\n\n" + response.message);
}
},
error: function(xhr) {
alert("반영 중 오류가 발생했습니다.");
}
});
});
$("#btn_popup_api").click(function () { $("#btn_popup_api").click(function () {
const args = {}; const args = {};
const url2 = api_url_view += "?cmd=POPUP"; const url2 = api_url_view += "?cmd=POPUP";
@@ -417,6 +467,8 @@
</div><!-- end content_top --> </div><!-- end content_top -->
<div class="content_middle"> <div class="content_middle">
<div class="search_wrap"> <div class="search_wrap">
<button type="button" class="cssbtn" id="btn_sync_portal" level="W" status="DETAIL"><i class="material-icons">sync</i> 개발자포탈 정보 반영
</button>
<button type="button" class="cssbtn" id="btn_delete" level="W" status="DETAIL"><i class="material-icons">delete</i> <%= localeMessage.getString("button.delete") %> <button type="button" class="cssbtn" id="btn_delete" level="W" status="DETAIL"><i class="material-icons">delete</i> <%= localeMessage.getString("button.delete") %>
</button> </button>
<button type="button" class="cssbtn" id="btn_modify" level="W" status="DETAIL,NEW"><i <button type="button" class="cssbtn" id="btn_modify" level="W" status="DETAIL,NEW"><i
@@ -55,4 +55,6 @@ public class CredentialUI implements Serializable, com.eactive.eai.data.Data {
private List<ApiSpecInfoUI> apiList = new ArrayList<>(); private List<ApiSpecInfoUI> apiList = new ArrayList<>();
private String appIconFileId; private String appIconFileId;
private String appDescription;
} }
@@ -121,4 +121,37 @@ public class ClientController extends OnlBaseAnnotationController {
response.put("hasMtls", hasMtls); response.put("hasMtls", hasMtls);
return ResponseEntity.ok(response); return ResponseEntity.ok(response);
} }
/**
* APIGW 인증관리 정보를 개발자포탈에 반영합니다.
* 포탈에 없으면 신규 생성, 있으면 업데이트합니다.
*
* @param clientId 클라이언트 ID
* @return 반영 결과 (success, action, message, invalidApis)
*/
@PostMapping(value = "/onl/admin/authserver/clientMan.json", params = "cmd=SYNC_PORTAL")
public ResponseEntity<Map<String, Object>> syncPortalData(String clientId) {
try {
SyncResult result = clientManService.syncPortalData(clientId);
Map<String, Object> response = new HashMap<>();
response.put("success", result.isSuccess());
response.put("message", result.getMessage());
response.put("action", result.getAction().name());
// 유효하지 않은 API 목록 포함
if (result.getInvalidApis() != null && !result.getInvalidApis().isEmpty()) {
response.put("invalidApis", result.getInvalidApis());
}
return ResponseEntity.ok(response);
} catch (Exception e) {
// 에러 처리
Map<String, Object> error = new HashMap<>();
error.put("success", false);
error.put("message", "반영 중 오류: " + e.getMessage());
error.put("action", "ERROR");
return ResponseEntity.status(500).body(error);
}
}
} }
@@ -1,9 +1,20 @@
package com.eactive.eai.rms.onl.manage.authserver.client; package com.eactive.eai.rms.onl.manage.authserver.client;
import com.eactive.apim.portal.app.entity.Credential;
import com.eactive.apim.portal.portalorg.entity.PortalOrg; import com.eactive.apim.portal.portalorg.entity.PortalOrg;
import com.eactive.eai.data.entity.onl.eaimsg.EAIMessageEntity;
import com.eactive.eai.rms.common.datasource.DataSourceContextHolder;
import com.eactive.eai.rms.common.datasource.DataSourceType;
import com.eactive.eai.rms.common.datasource.DataSourceTypeManager;
import com.eactive.eai.rms.data.entity.onl.apim.portalorg.PortalOrgService; import com.eactive.eai.rms.data.entity.onl.apim.portalorg.PortalOrgService;
import com.eactive.eai.rms.data.entity.onl.eaimsg.EAIMessageRepository;
import com.eactive.eai.rms.onl.apim.approval.credential.CredentialManService;
import com.eactive.eai.rms.onl.apim.approval.credential.CredentialService;
import com.eactive.eai.rms.onl.apim.approval.credential.CredentialUI;
import com.eactive.eai.rms.onl.apim.portalorg.PortalOrgUI; import com.eactive.eai.rms.onl.apim.portalorg.PortalOrgUI;
import com.eactive.eai.rms.onl.apim.portalorg.PortalOrgUIMapper; import com.eactive.eai.rms.onl.apim.portalorg.PortalOrgUIMapper;
import com.eactive.eai.rms.onl.transaction.apim.ui.ApiSpecInfoUI;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
@@ -15,8 +26,10 @@ import com.eactive.eai.rms.common.base.OnlBaseService;
import com.eactive.eai.rms.data.entity.onl.authserver.ClientEntityService; import com.eactive.eai.rms.data.entity.onl.authserver.ClientEntityService;
import com.eactive.eai.rms.data.entity.onl.authserver.ClientSearch; import com.eactive.eai.rms.data.entity.onl.authserver.ClientSearch;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Service @Service
@@ -27,16 +40,25 @@ public class ClientManService extends OnlBaseService {
private ClientUIMapper clientUIMapper; private ClientUIMapper clientUIMapper;
private PortalOrgService portalOrgService; private PortalOrgService portalOrgService;
private PortalOrgUIMapper portalOrgUIMapper; private PortalOrgUIMapper portalOrgUIMapper;
private CredentialManService credentialManService;
private CredentialService credentialService;
private EAIMessageRepository eaiMessageRepository;
@Autowired @Autowired
public ClientManService(ClientEntityService clientEntityService, public ClientManService(ClientEntityService clientEntityService,
ClientUIMapper clientUIMapper, ClientUIMapper clientUIMapper,
PortalOrgService portalOrgService, PortalOrgService portalOrgService,
PortalOrgUIMapper portalOrgUIMapper) { PortalOrgUIMapper portalOrgUIMapper,
CredentialManService credentialManService,
CredentialService credentialService,
EAIMessageRepository eaiMessageRepository) {
this.clientEntityService = clientEntityService; this.clientEntityService = clientEntityService;
this.clientUIMapper = clientUIMapper; this.clientUIMapper = clientUIMapper;
this.portalOrgService = portalOrgService; this.portalOrgService = portalOrgService;
this.portalOrgUIMapper = portalOrgUIMapper; this.portalOrgUIMapper = portalOrgUIMapper;
this.credentialManService = credentialManService;
this.credentialService = credentialService;
this.eaiMessageRepository = eaiMessageRepository;
} }
@@ -83,4 +105,161 @@ public class ClientManService extends OnlBaseService {
.map(ClientEntity::getClientid) .map(ClientEntity::getClientid)
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
/**
* AGW 인증관리(ClientEntity)의 정보를 개발자포탈(Credential)에 반영합니다.
* 포탈에 없으면 신규 생성(INSERT), 있으면 업데이트(UPDATE)합니다.
*
* @param clientId 클라이언트 ID
* @return 반영 결과 (INSERTED, UPDATED, SKIPPED_NO_ORG, SKIPPED_INVALID_API, ERROR)
*/
public SyncResult syncPortalData(String clientId) {
try {
// 1. APIGW 스키마에서 ClientEntity 조회
ClientEntity clientEntity = clientEntityService.getById(clientId);
// 2. 유효성 검증 - orgId 확인
String orgId = clientEntity.getOrgid();
if (StringUtils.isBlank(orgId)) {
return SyncResult.builder()
.success(false)
.action(SyncResult.SyncAction.SKIPPED_NO_ORG)
.message("법인(orgId)이 설정되지 않은 클라이언트입니다.")
.build();
}
// 3. API 유효성 검증 (APIGW 스키마)
List<String> invalidApis = new ArrayList<>();
if (clientEntity.getApiList() != null) {
for (EAIMessageEntity apiEntity : clientEntity.getApiList()) {
String eaiSvcName = apiEntity.getEaiSvcName();
// APIGW에 실제 API 존재하는지 확인
if (!eaiMessageRepository.existsById(eaiSvcName)) {
invalidApis.add(eaiSvcName);
}
}
}
// 유효하지 않은 API가 있으면 반영 중단
if (!invalidApis.isEmpty()) {
return SyncResult.builder()
.success(false)
.action(SyncResult.SyncAction.SKIPPED_INVALID_API)
.invalidApis(invalidApis)
.message("존재하지 않는 API: " + String.join(", ", invalidApis))
.build();
}
// 4. MONITORING 스키마로 전환
DataSourceType monitoringType = DataSourceTypeManager.getDataSourceType("MONITORING");
DataSourceContextHolder.setDataSourceType(monitoringType);
// 5. 기존 Credential 확인
Optional<Credential> existingCredential = credentialService.getRepository()
.findOne((root, query, cb) -> cb.and(
cb.equal(root.get("clientid"), clientId),
cb.equal(root.get("orgid"), orgId)
));
// 6. 데이터 변환 (ClientEntity → CredentialUI)
CredentialUI credentialUI = convertToCredentialUI(clientEntity);
SyncResult.SyncAction action;
if (existingCredential.isPresent()) {
// 업데이트: 기존 값 유지해야 할 필드 처리
Credential existing = existingCredential.get();
if (StringUtils.isNotBlank(existing.getAppDescription())) {
credentialUI.setAppDescription(existing.getAppDescription());
}
if (StringUtils.isNotBlank(existing.getAppIconFileId())) {
credentialUI.setAppIconFileId(existing.getAppIconFileId());
}
if (StringUtils.isNotBlank(existing.getServer())) {
credentialUI.setServer(existing.getServer());
}
// 7. 포탈 스키마에 업데이트
credentialManService.update(credentialUI);
action = SyncResult.SyncAction.UPDATED;
} else {
// 신규 생성: 기본값 설정
if (StringUtils.isBlank(credentialUI.getAppDescription())) {
credentialUI.setAppDescription("APIGW에서 반영된 인증정보");
}
// 7. 포탈 스키마에 신규 생성
credentialManService.insert(credentialUI);
action = SyncResult.SyncAction.INSERTED;
}
return SyncResult.builder()
.success(true)
.action(action)
.message("포탈 정보 반영 완료")
.build();
} catch (Exception e) {
logger.error("포탈 정보 반영 중 오류 발생", e);
return SyncResult.builder()
.success(false)
.action(SyncResult.SyncAction.ERROR)
.message("반영 중 오류: " + e.getMessage())
.build();
}
}
/**
* ClientEntity를 CredentialUI로 변환합니다.
*
* @param clientEntity APIGW 클라이언트 엔티티
* @return CredentialUI 포탈 인증정보 UI 객체
*/
private CredentialUI convertToCredentialUI(ClientEntity clientEntity) {
CredentialUI credentialUI = new CredentialUI();
// 기본 인증정보
credentialUI.setClientid(clientEntity.getClientid());
credentialUI.setClientname(clientEntity.getClientname());
credentialUI.setClientsecret(clientEntity.getClientsecret());
// OAuth 설정
credentialUI.setScope(clientEntity.getScope());
credentialUI.setGranttypes(clientEntity.getGranttypes());
credentialUI.setAccesstokenvalidityseconds(clientEntity.getAccesstokenvalidityseconds());
credentialUI.setRefreshtokenvalidityseconds(clientEntity.getRefreshtokenvalidityseconds());
// 보안 설정
credentialUI.setAllowedips(clientEntity.getAllowedips());
credentialUI.setAuthorities(clientEntity.getAuthorities());
credentialUI.setRedirecturi(clientEntity.getRedirecturi());
credentialUI.setSecuritykey(clientEntity.getSecuritykey());
credentialUI.setAutoapprove(clientEntity.getAutoapprove());
credentialUI.setResourceids(clientEntity.getResourceids());
// 조직 정보
credentialUI.setOrgid(clientEntity.getOrgid());
credentialUI.setOrgname(clientEntity.getOrgname());
// 앱 상태
credentialUI.setAppstatus(clientEntity.getAppstatus());
credentialUI.setDailytokenlimit(Integer.parseInt(
StringUtils.defaultIfBlank(clientEntity.getDailytokenlimit(), "0")));
// 수정 정보
credentialUI.setModifiedby(clientEntity.getModifiedby());
credentialUI.setModifiedon(clientEntity.getModifiedon());
// API 목록 변환
List<ApiSpecInfoUI> apiList = new ArrayList<>();
if (clientEntity.getApiList() != null) {
for (EAIMessageEntity apiEntity : clientEntity.getApiList()) {
ApiSpecInfoUI apiUI = new ApiSpecInfoUI();
apiUI.setApiId(apiEntity.getEaiSvcName());
apiList.add(apiUI);
}
}
credentialUI.setApiList(apiList);
return credentialUI;
}
} }
@@ -0,0 +1,68 @@
package com.eactive.eai.rms.onl.manage.authserver.client;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 개발자포탈 정보 반영 결과 DTO
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SyncResult {
/**
* 반영 성공 여부
*/
private boolean success;
/**
* 결과 메시지
*/
private String message;
/**
* 반영 액션 타입
*/
private SyncAction action;
/**
* 존재하지 않는 API 목록 (SKIPPED_INVALID_API인 경우)
*/
private List<String> invalidApis;
/**
* 반영 액션 타입
*/
public enum SyncAction {
/**
* 신규 생성
*/
INSERTED,
/**
* 기존 데이터 업데이트
*/
UPDATED,
/**
* orgId 없어서 건너뜀
*/
SKIPPED_NO_ORG,
/**
* 유효하지 않은 API 존재로 건너뜀
*/
SKIPPED_INVALID_API,
/**
* 오류 발생
*/
ERROR
}
}