Add API retrieval by organization code and enhance SSO user handling

- Implemented `getApisByOrgCode` method in `ObpApiService` to fetch APIs based on organization code.
- Added corresponding endpoint in `ObpApiController`.
- Updated `SsoController` to handle user creation without synchronization for existing users.
- Introduced new properties for SSO synchronization settings in `KjbProperty`.
This commit is contained in:
Rinjae
2026-01-14 15:09:09 +09:00
parent ed3da1b367
commit 4fa53631df
5 changed files with 214 additions and 32 deletions
+1
View File
@@ -5,6 +5,7 @@
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%@ include file="/jsp/common/include/localemessage.jsp" %>
<c:set var="themeColor" value="<%=System.getProperty(\"theme.color\")%>" scope="session" />
<%
String topPage = request.getContextPath() + StringUtils.defaultString((String) request.getAttribute("topPage"), "/top_04.do");
@@ -4,7 +4,15 @@ import com.eactive.apim.portal.portalorg.entity.PortalOrg;
import com.eactive.eai.data.jpa.BaseRepository;
import com.eactive.eai.rms.data.EMSDataSource;
import java.util.Optional;
@EMSDataSource
public interface PortalOrgRepository extends BaseRepository<PortalOrg, String> {
/**
* 기관코드로 기관 조회
* @param orgCode 기관코드 (OBP 파트너코드)
* @return 기관 정보
*/
Optional<PortalOrg> findByOrgCode(String orgCode);
}
@@ -106,39 +106,49 @@ public class SsoController implements InterceptorSkipController {
return "redirect:/loginForm.do";
}
// 기본 정보
Properties info = ssoModule.getUserInfos(ssoId);
// 확장 정보
Properties extendedInfo = ssoModule.getUserExFields(ssoId);
if (prop.isSsoSyncInfo()) {
// 기본 정보
Properties info = ssoModule.getUserInfos(ssoId);
// 확장 정보
Properties extendedInfo = ssoModule.getUserExFields(ssoId);
// 필수 필드 검증
try {
validateRequiredFields(info, extendedInfo);
} catch (IllegalStateException e) {
log.warn("SSO 필수 필드 검증 실패: {}", e.getMessage());
session.setAttribute("resultMsg", e.getMessage());
return "redirect:/loginForm.do";
// 필수 필드 검증
try {
validateRequiredFields(info, extendedInfo);
} catch (IllegalStateException e) {
log.warn("SSO 필수 필드 검증 실패: {}", e.getMessage());
session.setAttribute("resultMsg", e.getMessage());
return "redirect:/loginForm.do";
}
// 사용자 정보 로깅 (개인정보 마스킹 처리)
String userId = info.getProperty("USERID");
String name = info.getProperty("NAME");
String email = info.getProperty("EMAIL");
String cellPhoneNo = extendedInfo != null ? extendedInfo.getProperty("CELLPHONENO") : null;
log.info("SSO 로그인 시도 - userId: {}", userId);
log.debug("SSO 사용자 정보 - userId: {}, name: {}, phone: {}, email: {}",
userId,
MaskingUtils.maskName(name),
maskPhoneNumber(cellPhoneNo),
maskEmail(email));
// 사용자 생성 또는 업데이트
UserInfo userInfo = findOrCreateUser(info, extendedInfo);
// 로그인 세션 설정 (MainController 위임)
log.info("SSO 로그인 성공 - userId: {}", userId);
return mainController.setupSsoLoginSession(req, userInfo);
} else {
// 동기화 비활성화 모드: 기존 사용자는 업데이트 없이, 신규 사용자는 최소 정보로 생성
log.info("SSO 동기화 비활성화 모드로 로그인 처리 - ssoId: {}", ssoId);
UserInfo userInfo = findOrCreateUserWithoutSync(ssoId);
log.info("SSO 로그인 성공 (동기화 비활성화 모드) - userId: {}", ssoId);
return mainController.setupSsoLoginSession(req, userInfo);
}
// 사용자 정보 로깅 (개인정보 마스킹 처리)
String userId = info.getProperty("USERID");
String name = info.getProperty("NAME");
String email = info.getProperty("EMAIL");
String cellPhoneNo = extendedInfo != null ? extendedInfo.getProperty("CELLPHONENO") : null;
log.info("SSO 로그인 시도 - userId: {}", userId);
log.debug("SSO 사용자 정보 - userId: {}, name: {}, phone: {}, email: {}",
userId,
MaskingUtils.maskName(name),
maskPhoneNumber(cellPhoneNo),
maskEmail(email));
// 사용자 생성 또는 업데이트
UserInfo userInfo = findOrCreateUser(info, extendedInfo);
// 로그인 세션 설정 (MainController 위임)
log.info("SSO 로그인 성공 - userId: {}", userId);
return mainController.setupSsoLoginSession(req, userInfo);
}
}
@@ -174,6 +184,52 @@ public class SsoController implements InterceptorSkipController {
}
}
/**
* 사용자 조회 또는 생성 (동기화 비활성화 모드)
* - 기존 사용자: 정보 업데이트 없이 그대로 반환
* - 신규 사용자: userId를 username으로 사용하여 최소 정보로 생성
*/
private UserInfo findOrCreateUserWithoutSync(String ssoId) throws Exception {
// 대소문자 구분 없이 검색
Optional<UserInfo> existingUser = userInfoService.findByUserIdIgnoreCase(ssoId);
if (existingUser.isPresent()) {
log.debug("동기화 비활성화 모드 - 기존 사용자 발견, 정보 업데이트 없이 로그인 처리: {}", ssoId);
return existingUser.get();
} else {
return createNewUserWithMinimalInfo(ssoId);
}
}
/**
* 최소 정보로 신규 사용자 생성 (동기화 비활성화 모드)
* - username을 userId와 동일하게 설정
*/
private UserInfo createNewUserWithMinimalInfo(String userId) throws Exception {
UserInfo userInfo = new UserInfo();
userInfo.setUserid(userId);
userInfo.setUsername(userId); // username = userId
userInfo.setPassword(Seed.encrypt("!" + userId));
userInfo.setRoleidnfiname(CommonConstants.DEPT_DEVELOPER);
userInfoService.save(userInfo);
// 기본 역할 할당
String roleId = monitoringContext.getStringProperty(
MonitoringContext.USER_DEFAULT_RULE, "newbie");
UserRole userRole = new UserRole();
userRole.setId(new UserRoleId(userId, roleId));
userRoleService.save(userRole);
// 서비스 타입 할당
DataSourceTypeManager.getDynamicDataSourceTypes().stream()
.map(DataSourceType::getName)
.forEach(type -> userServiceTypeService.insertUserServiceType(userId, type));
log.info("동기화 비활성화 모드 - 신규 사용자 생성 완료 (최소 정보): {}", userId);
return userInfo;
}
/**
* 신규 사용자 생성
*/
@@ -6,6 +6,7 @@ import com.eactive.eai.rms.onl.apim.common.BaseRestController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@@ -42,7 +43,8 @@ public class ObpApiController extends BaseRestController {
)
@ResponseBody
public String getApis(
@RequestParam(value = "pretty", required = false) Boolean pretty) {
@RequestParam(value = "pretty", required = false) Boolean pretty
) {
log.debug("OBP API 목록 조회 요청: pretty={}", pretty);
long startTime = System.currentTimeMillis();
@@ -50,6 +52,7 @@ public class ObpApiController extends BaseRestController {
DataSourceContextHolder.setDataSourceType(
DataSourceTypeManager.getDataSourceType(DataSourceTypeManager.APIGW));
try {
List<ObpApiDTO> apis = obpApiService.findAllApis();
@@ -66,4 +69,46 @@ public class ObpApiController extends BaseRestController {
throw e;
}
}
/**
* 기관코드별 API 목록 조회
*
* <p>해당 기관의 활성 앱(appstatus='1')이 사용하는 API만 반환</p>
*
* @param orgCode 기관코드 (OBP 파트너코드)
* @param pretty true면 JSON 들여쓰기 적용
* @return JSON Array (기관 미존재 시 빈 배열)
*/
@RequestMapping(
value = "/kjb/{orgCode}/apis.json",
produces = JSON_CONTENT_TYPE
)
@ResponseBody
public String getApisByOrgCode(
@PathVariable("orgCode") String orgCode,
@RequestParam(value = "pretty", required = false) Boolean pretty
) {
log.debug("OBP API 목록 조회 요청 (기관별): orgCode={}, pretty={}", orgCode, pretty);
long startTime = System.currentTimeMillis();
// APIGW 데이터소스 강제 설정
DataSourceContextHolder.setDataSourceType(
DataSourceTypeManager.getDataSourceType(DataSourceTypeManager.APIGW));
try {
List<ObpApiDTO> apis = obpApiService.findApisByOrgCode(orgCode);
String json = toJson(apis, pretty);
long elapsed = System.currentTimeMillis() - startTime;
log.info("OBP API 목록 조회 완료 (기관별): orgCode={}, count={}, size={}, elapsed={}ms",
orgCode, apis.size(), getFormattedJsonSize(json), elapsed);
return json;
} catch (Exception e) {
log.error("OBP API 목록 조회 실패 (기관별): orgCode={}", orgCode, e);
throw e;
}
}
}
@@ -1,7 +1,12 @@
package com.eactive.eai.rms.onl.apim.obp;
import com.eactive.apim.portal.app.entity.Credential;
import com.eactive.apim.portal.app.repository.CredentialRepository;
import com.eactive.apim.portal.apispec.entity.ApiSpecInfo;
import com.eactive.apim.portal.portalorg.entity.PortalOrg;
import com.eactive.eai.data.entity.onl.message.QEAIMessageEntity;
import com.eactive.eai.data.entity.onl.stdmessage.QStandardMessageInfo;
import com.eactive.eai.rms.data.entity.onl.apim.portalorg.PortalOrgRepository;
import com.querydsl.core.Tuple;
import com.querydsl.jpa.impl.JPAQueryFactory;
import org.apache.commons.lang3.StringUtils;
@@ -9,7 +14,10 @@ import org.springframework.stereotype.Service;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
/**
@@ -23,6 +31,14 @@ public class ObpApiService {
@PersistenceContext
private EntityManager entityManager;
private final PortalOrgRepository portalOrgRepository;
private final CredentialRepository credentialRepository;
public ObpApiService(PortalOrgRepository portalOrgRepository, CredentialRepository credentialRepository) {
this.portalOrgRepository = portalOrgRepository;
this.credentialRepository = credentialRepository;
}
private JPAQueryFactory getQueryFactory() {
return new JPAQueryFactory(entityManager);
}
@@ -88,4 +104,60 @@ public class ObpApiService {
.authType(authtype)
.build();
}
/**
* 기관코드에 해당하는 법인이 이용하는 API 목록 조회
*
* <p>활성 앱(appstatus='1')의 API만 조회</p>
*
* @param orgCode 기관코드 (OBP 파트너코드)
* @return API 목록 (기관 미존재 시 빈 리스트)
*/
public List<ObpApiDTO> findApisByOrgCode(String orgCode) {
// 1. 기관코드로 기관 조회
Optional<PortalOrg> orgOpt = portalOrgRepository.findByOrgCode(orgCode);
if (!orgOpt.isPresent()) {
return Collections.emptyList();
}
PortalOrg org = orgOpt.get();
// 2. 해당 기관의 활성 앱 조회 (appstatus='1')
List<Credential> activeApps = credentialRepository.findActiveAppsByOrgIds(
Collections.singletonList(org.getId()));
if (activeApps.isEmpty()) {
return Collections.emptyList();
}
// 3. 앱들의 API ID 목록 수집
Set<String> apiIds = activeApps.stream()
.flatMap(app -> app.getApiList().stream())
.map(ApiSpecInfo::getApiId)
.collect(Collectors.toSet());
if (apiIds.isEmpty()) {
return Collections.emptyList();
}
// 4. 해당 API만 조회
QEAIMessageEntity he01 = QEAIMessageEntity.eAIMessageEntity;
QStandardMessageInfo hs04 = QStandardMessageInfo.standardMessageInfo;
List<Tuple> results = getQueryFactory()
.select(
he01.eaisvcname,
he01.eaisvcdesc,
he01.useyn,
he01.authtype,
hs04.apifullpath
)
.from(he01)
.leftJoin(hs04).on(he01.eaisvcname.eq(hs04.eaisvcname))
.where(he01.eaisvcname.in(apiIds))
.orderBy(he01.eaisvcname.asc())
.fetch();
return results.stream()
.map(this::toDto)
.collect(Collectors.toList());
}
}