KjbProperty 리팩토링

This commit is contained in:
Rinjae
2026-01-15 15:20:25 +09:00
parent e406a743a8
commit 06e38b44f6
9 changed files with 326 additions and 149 deletions
@@ -293,6 +293,12 @@
fncSsoLogin(); fncSsoLogin();
}); });
// SMS 인증번호 입력 필드에서 엔터키 누르면 확인
$("#smsAuthCode").keydown(function(event){
if ( event.which == 13 ) {
verifySmsAuthCode();
}
});
}); });
function fncSsoLogin() { function fncSsoLogin() {
+209
View File
@@ -0,0 +1,209 @@
# SMS 2차 인증 운영 가이드
## 개요
eLink Monitoring Admin 로그인 시 ID/PW 인증 후 SMS 인증번호를 통한 2차 인증을 수행합니다.
SSO 로그인에는 적용되지 않습니다.
---
## 로그인 흐름
### 일반 로그인 (SMS 인증 활성화 시)
```
1. 로그인 페이지 접속
2. ID/PW 입력 → [Login] 버튼 클릭
3. SMS 인증 모달 표시 (등록된 휴대폰으로 인증번호 발송)
4. 인증번호 6자리 입력 → [확인] 버튼 클릭
5. 로그인 완료
```
### SSO 로그인
```
1. 로그인 페이지 접속
2. [SSO Login] 버튼 클릭
3. SSO 인증 진행
4. 로그인 완료 (SMS 인증 없음)
```
---
## 설정 관리
### 설정 위치
**모니터링 프로퍼티** (DB 테이블: `TSEAIRM24`, 그룹: `Monitoring`)
### 설정 항목
| 프로퍼티 키 | 기본값 | 설명 |
|------------|--------|------|
| `kjb.sms_auth.enabled` | `true` | SMS 인증 사용 여부 |
| `kjb.sms_auth.mode` | `real` | 인증번호 생성 모드 |
| `kjb.sms_auth.fixed_value` | `654321` | fixed 모드 시 사용할 고정 인증번호 |
### 인증번호 생성 모드 (`kjb.sms_auth.mode`)
| 모드 | 설명 | 용도 |
|------|------|------|
| `real` | 6자리 랜덤 숫자 생성 | **운영 환경** |
| `hhmm00` | 현재시각(HHmm) + "00" (예: 14:35 → `143500`) | 개발/테스트 |
| `fixed` | `kjb.sms_auth.fixed_value` 값 사용 | 개발/테스트 |
---
## 비상 로그인
SSO 장애 또는 SMS 발송 장애 시 ID/PW만으로 로그인할 수 있는 비상 모드입니다.
### 사용 방법
```
https://{서버주소}/monitoring/loginForm.do?emergency
```
### 동작
- SSO 로그인 버튼 숨김
- SMS 2차 인증 우회
- ID/PW만으로 로그인 완료
### 주의사항
- 비상 상황에서만 사용
- 사용 시 서버 로그에 기록됨: `비상 로그인 모드 활성화 - SSO/SMS 인증 우회`
---
## SMS 인증 화면 안내
### 인증 모달
| 항목 | 설명 |
|------|------|
| 마스킹된 전화번호 | `010****1234` 형식으로 표시 |
| 남은 시간 | 인증번호 유효시간 카운트다운 (60초) |
| 재발송 버튼 | 20초 후 활성화, 남은 시간 표시 (예: `재발송 (15초)`) |
### 인증 규칙
- 인증번호: 6자리 숫자
- 유효시간: **1분**
- 재발송 가능: 발송 후 **20초** 경과 시
---
## 오류 상황 및 대응
### 1. "휴대폰번호가 등록되어 있지 않아 로그인할 수 없습니다"
**원인**: 사용자 정보에 휴대폰번호가 없음
**대응**:
- 관리자가 해당 사용자의 휴대폰번호 등록
- 또는 비상 로그인 사용 (`/loginForm.do?emergency`)
### 2. "인증번호 발송에 실패했습니다"
**원인**: UMS 서버 연동 오류
**대응**:
- UMS 서버 상태 확인
- 비상 로그인 사용 (`/loginForm.do?emergency`)
- 또는 SMS 인증 비활성화 (`kjb.sms_auth.enabled = false`)
### 3. "인증번호가 만료되었습니다"
**원인**: 1분 이내에 인증번호 입력하지 않음
**대응**:
- 모달 닫고 다시 로그인 시도
- 새 인증번호 발송됨
### 4. "인증번호가 일치하지 않습니다"
**원인**: 잘못된 인증번호 입력
**대응**:
- 정확한 6자리 숫자 입력
- 재발송 버튼으로 새 인증번호 요청 가능
---
## 로그 확인
### 로그 위치
서버 로그 파일 (Logback 설정에 따름)
### 주요 로그 메시지
| 로그 | 의미 |
|------|------|
| `SMS 인증번호 발송 완료 - guid: xxx, phone: 010****1234` | 인증번호 발송 성공 |
| `SMS 인증번호 검증 성공` | 인증 성공 |
| `SMS 인증번호 불일치` | 인증 실패 (잘못된 번호) |
| `SMS 인증번호 만료됨` | 인증 실패 (시간 초과) |
| `비상 로그인 모드 활성화 - SSO/SMS 인증 우회` | 비상 모드 접속 |
### 디버그 로그 (개발/테스트용)
`-DLOGBACK_LOG_LEVEL=DEBUG` 설정 시 인증번호가 로그에 출력됩니다:
```
SMS 인증번호 생성 (real 모드): 123456
SMS 인증번호 발송 - authCode: 123456
```
> **주의**: 운영 환경에서는 DEBUG 로그 비활성화 권장
---
## 초기 설정 (신규 구축 시)
### DB 데이터 입력
```sql
-- SMS 인증 설정 초기화
INSERT INTO EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL)
VALUES ('Monitoring', 'kjb.sms_auth.enabled', 'true');
INSERT INTO EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL)
VALUES ('Monitoring', 'kjb.sms_auth.mode', 'real');
INSERT INTO EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL)
VALUES ('Monitoring', 'kjb.sms_auth.fixed_value', '654321');
```
### UMS 연동 설정 확인
SMS 발송을 위해 UMS 관련 프로퍼티가 설정되어 있어야 합니다:
- `kjb.ums.host_url`
- `kjb.ums.api_key`
- 기타 UMS 연동 설정
---
## FAQ
### Q: SMS 인증을 일시적으로 비활성화하려면?
```sql
UPDATE EMSADM.TSEAIRM24
SET PRPTY2VAL = 'false'
WHERE PRPTYGROUPNAME = 'Monitoring' AND PRPTYNAME = 'kjb.sms_auth.enabled';
```
### Q: 특정 사용자만 SMS 인증을 우회하려면?
현재는 사용자별 설정이 없습니다.
- 해당 사용자에게 비상 로그인 URL 안내: `/loginForm.do?emergency`
- 또는 전체 SMS 인증 비활성화
### Q: 인증번호를 미리 알 수 있나요? (테스트용)
`kjb.sms_auth.mode` 설정 변경:
- `hhmm00`: 현재시각 기반 (예: 14:35 → `143500`)
- `fixed`: 고정값 사용 (`kjb.sms_auth.fixed_value`)
### Q: SSO 로그인에도 SMS 인증이 적용되나요?
아니요. SMS 2차 인증은 ID/PW 로그인에만 적용됩니다.
@@ -21,6 +21,8 @@ import com.eactive.eai.rms.common.util.ContainerUtil;
import com.eactive.eai.rms.data.entity.man.monitoringProperty.MonitoringPropertyId; import com.eactive.eai.rms.data.entity.man.monitoringProperty.MonitoringPropertyId;
import com.eactive.eai.rms.data.entity.man.monitoringProperty.service.MonitoringPropertyGroupService; import com.eactive.eai.rms.data.entity.man.monitoringProperty.service.MonitoringPropertyGroupService;
import com.eactive.eai.rms.data.entity.man.monitoringProperty.service.MonitoringPropertyService; import com.eactive.eai.rms.data.entity.man.monitoringProperty.service.MonitoringPropertyService;
import com.eactive.ext.kjb.common.KjbPropertyHolder;
import com.eactive.ext.kjb.util.KjbPropertyInjector;
@Service("monitoringContext") @Service("monitoringContext")
@Transactional(transactionManager = "transactionManagerForEMS") @Transactional(transactionManager = "transactionManagerForEMS")
@@ -43,6 +45,7 @@ class MonitoringContextImpl implements MonitoringContext {
public void refresh() { public void refresh() {
init(); init();
KjbPropertyHolder.reload();
} }
public void refresh(String propName) { public void refresh(String propName) {
@@ -82,6 +85,10 @@ class MonitoringContextImpl implements MonitoringContext {
setPortMapper(monitoringContextDAO.getPortMap()); setPortMapper(monitoringContextDAO.getPortMap());
// KjbPropertyHolder 초기화
KjbPropertyInjector injector = new KjbPropertyInjector(monitoringPropertyService, "Monitoring");
KjbPropertyHolder.initialize(injector::inject);
logger.info("SSO AgentId : " + System.getProperty(EAI_AGENTID)); logger.info("SSO AgentId : " + System.getProperty(EAI_AGENTID));
} catch (Exception e) { } catch (Exception e) {
@@ -33,8 +33,6 @@ import org.springframework.web.servlet.View;
import com.eactive.eai.common.seed.Seed; import com.eactive.eai.common.seed.Seed;
import com.eactive.eai.rms.ext.kjb.smsauth.SmsAuthService; import com.eactive.eai.rms.ext.kjb.smsauth.SmsAuthService;
import com.eactive.ext.kjb.common.KjbProperty;
import com.eactive.ext.kjb.util.KjbPropertyInjector;
import com.eactive.eai.data.converter.LocalDateTimeFormatters; import com.eactive.eai.data.converter.LocalDateTimeFormatters;
import com.eactive.eai.rms.common.context.MonitoringContext; import com.eactive.eai.rms.common.context.MonitoringContext;
import com.eactive.eai.rms.common.datasource.DataSourceType; import com.eactive.eai.rms.common.datasource.DataSourceType;
@@ -266,13 +264,11 @@ public class MainController implements InterceptorSkipController {
} }
/** /**
* SmsAuthService 인스턴스 조회 (lazy initialization) * SmsAuthService 인스턴스 조회 (singleton)
*/ */
private SmsAuthService getSmsAuthService() { private SmsAuthService getSmsAuthService() {
try { try {
KjbPropertyInjector injector = new KjbPropertyInjector(monitoringPropertyService, "Monitoring"); return SmsAuthService.getInstance();
KjbProperty kjbProperty = injector.inject(new KjbProperty());
return new SmsAuthService(kjbProperty);
} catch (Exception e) { } catch (Exception e) {
logger.warn("SmsAuthService 초기화 실패 - SMS 인증 비활성화 처리", e); logger.warn("SmsAuthService 초기화 실패 - SMS 인증 비활성화 처리", e);
return null; return null;
@@ -4,10 +4,6 @@ import com.eactive.apim.portal.user.entity.UserInfo;
import com.eactive.eai.rms.common.interceptor.InterceptorSkipController; import com.eactive.eai.rms.common.interceptor.InterceptorSkipController;
import com.eactive.eai.rms.common.login.LoginVo; import com.eactive.eai.rms.common.login.LoginVo;
import com.eactive.eai.rms.common.login.MainController; import com.eactive.eai.rms.common.login.MainController;
import com.eactive.eai.rms.common.util.CommonConstants;
import com.eactive.eai.rms.data.entity.man.monitoringProperty.service.MonitoringPropertyService;
import com.eactive.ext.kjb.common.KjbProperty;
import com.eactive.ext.kjb.util.KjbPropertyInjector;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
@@ -23,27 +19,14 @@ import javax.servlet.http.HttpSession;
@RequestMapping("/sms-auth") @RequestMapping("/sms-auth")
public class SmsAuthController implements InterceptorSkipController { public class SmsAuthController implements InterceptorSkipController {
public static final String PROP_GROUP_ID = "Monitoring";
private final MainController mainController; private final MainController mainController;
private final KjbPropertyInjector kjbPropertyInjector;
private SmsAuthService smsAuthService; public SmsAuthController(MainController mainController) {
private KjbProperty kjbProperty;
public SmsAuthController(MonitoringPropertyService monitoringPropertyService,
MainController mainController) {
this.mainController = mainController; this.mainController = mainController;
this.kjbPropertyInjector = new KjbPropertyInjector(monitoringPropertyService, PROP_GROUP_ID);
reloadProperty();
} }
/** private SmsAuthService getSmsAuthService() {
* 프로퍼티 리로드 (DB 변경 대응) return SmsAuthService.getInstance();
*/
private void reloadProperty() {
this.kjbProperty = kjbPropertyInjector.inject(new KjbProperty());
this.smsAuthService = new SmsAuthService(kjbProperty);
} }
/** /**
@@ -52,11 +35,8 @@ public class SmsAuthController implements InterceptorSkipController {
@RequestMapping(value = "/send.json", method = RequestMethod.POST) @RequestMapping(value = "/send.json", method = RequestMethod.POST)
@ResponseBody @ResponseBody
public SmsAuthDto sendAuthCode(HttpServletRequest request) { public SmsAuthDto sendAuthCode(HttpServletRequest request) {
// 프로퍼티 리로드 (DB 변경 대응)
reloadProperty();
HttpSession session = request.getSession(); HttpSession session = request.getSession();
UserInfo userInfo = smsAuthService.getUserInfoFromSession(session); UserInfo userInfo = getSmsAuthService().getUserInfoFromSession(session);
if (userInfo == null) { if (userInfo == null) {
log.warn("SMS 인증 세션 정보 없음 - send 요청"); log.warn("SMS 인증 세션 정보 없음 - send 요청");
@@ -66,7 +46,7 @@ public class SmsAuthController implements InterceptorSkipController {
.build(); .build();
} }
if (!smsAuthService.hasValidPhoneNumber(userInfo)) { if (!getSmsAuthService().hasValidPhoneNumber(userInfo)) {
log.warn("유효한 휴대폰번호 없음 - userId: {}", userInfo.getUserid()); log.warn("유효한 휴대폰번호 없음 - userId: {}", userInfo.getUserid());
return SmsAuthDto.builder() return SmsAuthDto.builder()
.sent(false) .sent(false)
@@ -75,19 +55,19 @@ public class SmsAuthController implements InterceptorSkipController {
} }
// 인증번호 생성 및 발송 // 인증번호 생성 및 발송
String authCode = smsAuthService.generateAuthCode(); String authCode = getSmsAuthService().generateAuthCode();
boolean sent = smsAuthService.sendAuthCode(userInfo.getCphnno(), authCode); boolean sent = getSmsAuthService().sendAuthCode(userInfo.getCphnno(), authCode);
if (sent) { if (sent) {
// 세션에 인증번호 저장 (기존 UserInfo, LoginVo 유지) // 세션에 인증번호 저장 (기존 UserInfo, LoginVo 유지)
LoginVo loginVo = smsAuthService.getLoginVoFromSession(session); LoginVo loginVo = getSmsAuthService().getLoginVoFromSession(session);
smsAuthService.setupAuthSession(session, userInfo, loginVo, authCode); getSmsAuthService().setupAuthSession(session, userInfo, loginVo, authCode);
return SmsAuthDto.builder() return SmsAuthDto.builder()
.sent(true) .sent(true)
.maskedPhone(smsAuthService.maskPhoneNumber(userInfo.getCphnno())) .maskedPhone(getSmsAuthService().maskPhoneNumber(userInfo.getCphnno()))
.expiresAt(smsAuthService.getExpiresAt(session)) .expiresAt(getSmsAuthService().getExpiresAt(session))
.resendableAt(smsAuthService.getResendableAt(session)) .resendableAt(getSmsAuthService().getResendableAt(session))
.build(); .build();
} else { } else {
return SmsAuthDto.builder() return SmsAuthDto.builder()
@@ -103,11 +83,8 @@ public class SmsAuthController implements InterceptorSkipController {
@RequestMapping(value = "/resend.json", method = RequestMethod.POST) @RequestMapping(value = "/resend.json", method = RequestMethod.POST)
@ResponseBody @ResponseBody
public SmsAuthDto resendAuthCode(HttpServletRequest request) { public SmsAuthDto resendAuthCode(HttpServletRequest request) {
// 프로퍼티 리로드 (DB 변경 대응)
reloadProperty();
HttpSession session = request.getSession(); HttpSession session = request.getSession();
UserInfo userInfo = smsAuthService.getUserInfoFromSession(session); UserInfo userInfo = getSmsAuthService().getUserInfoFromSession(session);
if (userInfo == null) { if (userInfo == null) {
log.warn("SMS 인증 세션 정보 없음 - resend 요청"); log.warn("SMS 인증 세션 정보 없음 - resend 요청");
@@ -117,29 +94,29 @@ public class SmsAuthController implements InterceptorSkipController {
.build(); .build();
} }
if (!smsAuthService.canResend(session)) { if (!getSmsAuthService().canResend(session)) {
long waitTime = smsAuthService.getResendWaitTime(session); long waitTime = getSmsAuthService().getResendWaitTime(session);
log.debug("재발송 대기 중 - 남은 시간: {}ms", waitTime); log.debug("재발송 대기 중 - 남은 시간: {}ms", waitTime);
return SmsAuthDto.builder() return SmsAuthDto.builder()
.sent(false) .sent(false)
.errorMessage(String.format("%.0f초 후에 재발송이 가능합니다.", Math.ceil(waitTime / 1000.0))) .errorMessage(String.format("%.0f초 후에 재발송이 가능합니다.", Math.ceil(waitTime / 1000.0)))
.resendableAt(smsAuthService.getResendableAt(session)) .resendableAt(getSmsAuthService().getResendableAt(session))
.build(); .build();
} }
// 새 인증번호 생성 및 발송 // 새 인증번호 생성 및 발송
String authCode = smsAuthService.generateAuthCode(); String authCode = getSmsAuthService().generateAuthCode();
boolean sent = smsAuthService.sendAuthCode(userInfo.getCphnno(), authCode); boolean sent = getSmsAuthService().sendAuthCode(userInfo.getCphnno(), authCode);
if (sent) { if (sent) {
LoginVo loginVo = smsAuthService.getLoginVoFromSession(session); LoginVo loginVo = getSmsAuthService().getLoginVoFromSession(session);
smsAuthService.setupAuthSession(session, userInfo, loginVo, authCode); getSmsAuthService().setupAuthSession(session, userInfo, loginVo, authCode);
return SmsAuthDto.builder() return SmsAuthDto.builder()
.sent(true) .sent(true)
.maskedPhone(smsAuthService.maskPhoneNumber(userInfo.getCphnno())) .maskedPhone(getSmsAuthService().maskPhoneNumber(userInfo.getCphnno()))
.expiresAt(smsAuthService.getExpiresAt(session)) .expiresAt(getSmsAuthService().getExpiresAt(session))
.resendableAt(smsAuthService.getResendableAt(session)) .resendableAt(getSmsAuthService().getResendableAt(session))
.build(); .build();
} else { } else {
return SmsAuthDto.builder() return SmsAuthDto.builder()
@@ -157,8 +134,8 @@ public class SmsAuthController implements InterceptorSkipController {
public SmsAuthDto verifyAuthCode(HttpServletRequest request, public SmsAuthDto verifyAuthCode(HttpServletRequest request,
@RequestParam("authCode") String authCode) { @RequestParam("authCode") String authCode) {
HttpSession session = request.getSession(); HttpSession session = request.getSession();
UserInfo userInfo = smsAuthService.getUserInfoFromSession(session); UserInfo userInfo = getSmsAuthService().getUserInfoFromSession(session);
LoginVo loginVo = smsAuthService.getLoginVoFromSession(session); LoginVo loginVo = getSmsAuthService().getLoginVoFromSession(session);
if (userInfo == null || loginVo == null) { if (userInfo == null || loginVo == null) {
log.warn("SMS 인증 세션 정보 없음 - verify 요청"); log.warn("SMS 인증 세션 정보 없음 - verify 요청");
@@ -168,16 +145,16 @@ public class SmsAuthController implements InterceptorSkipController {
.build(); .build();
} }
if (smsAuthService.isExpired(session)) { if (getSmsAuthService().isExpired(session)) {
log.warn("SMS 인증번호 만료됨 - userId: {}", userInfo.getUserid()); log.warn("SMS 인증번호 만료됨 - userId: {}", userInfo.getUserid());
smsAuthService.clearAuthSession(session); getSmsAuthService().clearAuthSession(session);
return SmsAuthDto.builder() return SmsAuthDto.builder()
.success(false) .success(false)
.errorMessage("인증번호가 만료되었습니다. 다시 시도해주세요.") .errorMessage("인증번호가 만료되었습니다. 다시 시도해주세요.")
.build(); .build();
} }
if (!smsAuthService.validateAuthCode(session, authCode)) { if (!getSmsAuthService().validateAuthCode(session, authCode)) {
log.warn("SMS 인증번호 불일치 - userId: {}", userInfo.getUserid()); log.warn("SMS 인증번호 불일치 - userId: {}", userInfo.getUserid());
return SmsAuthDto.builder() return SmsAuthDto.builder()
.success(false) .success(false)
@@ -187,7 +164,7 @@ public class SmsAuthController implements InterceptorSkipController {
// 인증 성공 - 로그인 완료 처리 // 인증 성공 - 로그인 완료 처리
try { try {
smsAuthService.clearAuthSession(session); getSmsAuthService().clearAuthSession(session);
// MainController의 loadUserInfoAndSetupSession 호출 // MainController의 loadUserInfoAndSetupSession 호출
String result = mainController.loadUserInfoAndSetupSession(request, session, loginVo, userInfo); String result = mainController.loadUserInfoAndSetupSession(request, session, loginVo, userInfo);
@@ -236,7 +213,7 @@ public class SmsAuthController implements InterceptorSkipController {
@ResponseBody @ResponseBody
public SmsAuthDto getStatus(HttpServletRequest request) { public SmsAuthDto getStatus(HttpServletRequest request) {
HttpSession session = request.getSession(); HttpSession session = request.getSession();
UserInfo userInfo = smsAuthService.getUserInfoFromSession(session); UserInfo userInfo = getSmsAuthService().getUserInfoFromSession(session);
if (userInfo == null) { if (userInfo == null) {
return SmsAuthDto.builder() return SmsAuthDto.builder()
@@ -246,10 +223,10 @@ public class SmsAuthController implements InterceptorSkipController {
} }
return SmsAuthDto.builder() return SmsAuthDto.builder()
.enabled(smsAuthService.isEnabled()) .enabled(getSmsAuthService().isEnabled())
.maskedPhone(smsAuthService.maskPhoneNumber(userInfo.getCphnno())) .maskedPhone(getSmsAuthService().maskPhoneNumber(userInfo.getCphnno()))
.expiresAt(smsAuthService.getExpiresAt(session)) .expiresAt(getSmsAuthService().getExpiresAt(session))
.resendableAt(smsAuthService.getResendableAt(session)) .resendableAt(getSmsAuthService().getResendableAt(session))
.build(); .build();
} }
} }
@@ -3,6 +3,7 @@ package com.eactive.eai.rms.ext.kjb.smsauth;
import com.eactive.apim.portal.user.entity.UserInfo; import com.eactive.apim.portal.user.entity.UserInfo;
import com.eactive.eai.rms.common.login.LoginVo; import com.eactive.eai.rms.common.login.LoginVo;
import com.eactive.ext.kjb.common.KjbProperty; import com.eactive.ext.kjb.common.KjbProperty;
import com.eactive.ext.kjb.common.KjbPropertyHolder;
import com.eactive.ext.kjb.common.KjbUmsException; import com.eactive.ext.kjb.common.KjbUmsException;
import com.eactive.ext.kjb.ums.KjbUmsService; import com.eactive.ext.kjb.ums.KjbUmsService;
import com.eactive.ext.kjb.ums.UmsBizWorkCode; import com.eactive.ext.kjb.ums.UmsBizWorkCode;
@@ -17,6 +18,7 @@ import java.time.format.DateTimeFormatter;
@Slf4j @Slf4j
public class SmsAuthService { public class SmsAuthService {
private static volatile SmsAuthService instance;
// 세션 키 // 세션 키
public static final String SESSION_KEY_AUTH_CODE = "SMS_AUTH_CODE"; public static final String SESSION_KEY_AUTH_CODE = "SMS_AUTH_CODE";
@@ -33,20 +35,36 @@ public class SmsAuthService {
public static final String MODE_HHMM00 = "hhmm00"; public static final String MODE_HHMM00 = "hhmm00";
public static final String MODE_FIXED = "fixed"; public static final String MODE_FIXED = "fixed";
private final KjbProperty kjbProperty;
private final KjbUmsService kjbUmsService;
private final SecureRandom random = new SecureRandom(); private final SecureRandom random = new SecureRandom();
public SmsAuthService(KjbProperty kjbProperty) { private SmsAuthService() {
this.kjbProperty = kjbProperty; // 싱글톤
this.kjbUmsService = new KjbUmsService(kjbProperty); }
public static SmsAuthService getInstance() {
if (instance == null) {
synchronized (SmsAuthService.class) {
if (instance == null) {
instance = new SmsAuthService();
}
}
}
return instance;
}
private static KjbProperty getProperty() {
return KjbPropertyHolder.get();
}
private KjbUmsService getUmsService() {
return KjbUmsService.getInstance();
} }
/** /**
* SMS 인증이 필요한지 확인 * SMS 인증이 필요한지 확인
*/ */
public boolean isRequired(UserInfo userInfo) { public boolean isRequired(UserInfo userInfo) {
if (!kjbProperty.isSmsAuthEnabled()) { if (!getProperty().isSmsAuthEnabled()) {
log.debug("SMS 인증 비활성화 상태"); log.debug("SMS 인증 비활성화 상태");
return false; return false;
} }
@@ -74,10 +92,11 @@ public class SmsAuthService {
* 인증번호 생성 * 인증번호 생성
*/ */
public String generateAuthCode() { public String generateAuthCode() {
String mode = kjbProperty.getSmsAuthMode(); KjbProperty prop = getProperty();
String mode = prop.getSmsAuthMode();
if (MODE_FIXED.equalsIgnoreCase(mode)) { if (MODE_FIXED.equalsIgnoreCase(mode)) {
String fixedValue = kjbProperty.getSmsAuthFixedValue(); String fixedValue = prop.getSmsAuthFixedValue();
log.debug("SMS 인증번호 생성 (fixed 모드): {}", fixedValue); log.debug("SMS 인증번호 생성 (fixed 모드): {}", fixedValue);
return fixedValue; return fixedValue;
} else if (MODE_HHMM00.equalsIgnoreCase(mode)) { } else if (MODE_HHMM00.equalsIgnoreCase(mode)) {
@@ -105,7 +124,7 @@ public class SmsAuthService {
.authNumber(authCode) .authNumber(authCode)
.build(); .build();
kjbUmsService.sendKakaoAlimTalk(guid, UmsBizWorkCode.VERIFY_PHONE, cleanedPhone, content); getUmsService().sendKakaoAlimTalk(guid, UmsBizWorkCode.VERIFY_PHONE, cleanedPhone, content);
log.info("SMS 인증번호 발송 완료 - guid: {}, phone: {}****{}", log.info("SMS 인증번호 발송 완료 - guid: {}, phone: {}****{}",
guid, cleanedPhone.substring(0, 3), cleanedPhone.substring(cleanedPhone.length() - 4)); guid, cleanedPhone.substring(0, 3), cleanedPhone.substring(cleanedPhone.length() - 4));
log.debug("SMS 인증번호 발송 - authCode: {}", authCode); log.debug("SMS 인증번호 발송 - authCode: {}", authCode);
@@ -265,6 +284,13 @@ public class SmsAuthService {
* SMS 인증 활성화 여부 * SMS 인증 활성화 여부
*/ */
public boolean isEnabled() { public boolean isEnabled() {
return kjbProperty.isSmsAuthEnabled(); return getProperty().isSmsAuthEnabled();
}
/**
* 테스트용 인스턴스 리셋
*/
public static void resetForTest() {
instance = null;
} }
} }
@@ -8,7 +8,6 @@ import com.eactive.eai.rms.common.datasource.DataSourceTypeManager;
import com.eactive.eai.rms.common.interceptor.InterceptorSkipController; import com.eactive.eai.rms.common.interceptor.InterceptorSkipController;
import com.eactive.eai.rms.common.login.MainController; import com.eactive.eai.rms.common.login.MainController;
import com.eactive.eai.rms.common.util.CommonConstants; import com.eactive.eai.rms.common.util.CommonConstants;
import com.eactive.eai.rms.data.entity.man.monitoringProperty.service.MonitoringPropertyService;
import com.eactive.eai.rms.data.entity.man.user.UserInfoService; import com.eactive.eai.rms.data.entity.man.user.UserInfoService;
import com.eactive.eai.rms.data.entity.man.user.UserRole; import com.eactive.eai.rms.data.entity.man.user.UserRole;
import com.eactive.eai.rms.data.entity.man.user.UserRoleId; import com.eactive.eai.rms.data.entity.man.user.UserRoleId;
@@ -16,8 +15,8 @@ import com.eactive.eai.rms.data.entity.man.user.UserRoleService;
import com.eactive.eai.rms.data.entity.man.user.UserServiceTypeService; import com.eactive.eai.rms.data.entity.man.user.UserServiceTypeService;
import com.eactive.eai.rms.onl.apim.masking.MaskingUtils; import com.eactive.eai.rms.onl.apim.masking.MaskingUtils;
import com.eactive.ext.kjb.common.KjbProperty; import com.eactive.ext.kjb.common.KjbProperty;
import com.eactive.ext.kjb.common.KjbPropertyHolder;
import com.eactive.ext.kjb.sso.KjbSsoModule; import com.eactive.ext.kjb.sso.KjbSsoModule;
import com.eactive.ext.kjb.util.KjbPropertyInjector;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
@@ -34,25 +33,18 @@ import java.util.Properties;
@Slf4j @Slf4j
@Controller @Controller
public class SsoController implements InterceptorSkipController { public class SsoController implements InterceptorSkipController {
public static final String PROP_GORUP_ID = "Monitoring";
private final KjbSsoModule ssoModule;
private final KjbProperty prop;
private final UserInfoService userInfoService; private final UserInfoService userInfoService;
private final UserRoleService userRoleService; private final UserRoleService userRoleService;
private final UserServiceTypeService userServiceTypeService; private final UserServiceTypeService userServiceTypeService;
private final MonitoringContext monitoringContext; private final MonitoringContext monitoringContext;
private final MainController mainController; private final MainController mainController;
public SsoController(MonitoringPropertyService monitoringPropertyService, public SsoController(UserInfoService userInfoService,
UserInfoService userInfoService,
UserRoleService userRoleService, UserRoleService userRoleService,
UserServiceTypeService userServiceTypeService, UserServiceTypeService userServiceTypeService,
MonitoringContext monitoringContext, MonitoringContext monitoringContext,
MainController mainController) { MainController mainController) {
KjbPropertyInjector kjbPropertyInjector = new KjbPropertyInjector(monitoringPropertyService, PROP_GORUP_ID);
prop = kjbPropertyInjector.inject(new KjbProperty());
this.ssoModule = KjbSsoModule.getInstance(prop);
this.userInfoService = userInfoService; this.userInfoService = userInfoService;
this.userRoleService = userRoleService; this.userRoleService = userRoleService;
this.userServiceTypeService = userServiceTypeService; this.userServiceTypeService = userServiceTypeService;
@@ -60,11 +52,19 @@ public class SsoController implements InterceptorSkipController {
this.mainController = mainController; this.mainController = mainController;
} }
private KjbSsoModule getSsoModule() {
return KjbSsoModule.getInstance();
}
private KjbProperty getProperty() {
return KjbPropertyHolder.get();
}
@RequestMapping(value = "/sso/login.do") @RequestMapping(value = "/sso/login.do")
public String login(HttpServletRequest req, HttpServletResponse res, public String login(HttpServletRequest req, HttpServletResponse res,
@RequestParam(name = "UURL", required = false) String uurl) throws Exception { @RequestParam(name = "UURL", required = false) String uurl) throws Exception {
HttpSession session = req.getSession(); HttpSession session = req.getSession();
String ssoId = ssoModule.getSsoId(req); String ssoId = getSsoModule().getSsoId(req);
log.debug("ssoId : {}", ssoId); log.debug("ssoId : {}", ssoId);
log.debug("uurl : {}", uurl); log.debug("uurl : {}", uurl);
@@ -83,19 +83,19 @@ public class SsoController implements InterceptorSkipController {
} }
if (StringUtils.isEmpty(uurl)) { if (StringUtils.isEmpty(uurl)) {
uurl = ssoModule.getAscpUrl(); uurl = getSsoModule().getAscpUrl();
} }
if (ssoId == null) { if (ssoId == null) {
ssoModule.goLoginPage(res, uurl); getSsoModule().goLoginPage(res, uurl);
return null; return null;
} else { } else {
// EAM 세션 검증 // EAM 세션 검증
String retCode = ssoModule.getEamSessionCheck(req, res); String retCode = getSsoModule().getEamSessionCheck(req, res);
log.debug("retCode : {}", retCode); log.debug("retCode : {}", retCode);
log.debug("Property - sso.ignore_validation = {}", prop.isSsoIgnoreValidation()); log.debug("Property - sso.ignore_validation = {}", getProperty().isSsoIgnoreValidation());
if (!prop.isSsoIgnoreValidation() && !"0".equalsIgnoreCase(retCode)) { if (!getProperty().isSsoIgnoreValidation() && !"0".equalsIgnoreCase(retCode)) {
// SSO 검증 실패 시 에러 메시지 설정 및 로그인 페이지로 이동 // SSO 검증 실패 시 에러 메시지 설정 및 로그인 페이지로 이동
session.setAttribute("resultMsg", "SSO 로그인을 실패 하였습니다. 다른 로그인 방식을 시도 하세요"); session.setAttribute("resultMsg", "SSO 로그인을 실패 하였습니다. 다른 로그인 방식을 시도 하세요");
return "redirect:/loginForm.do"; return "redirect:/loginForm.do";
@@ -106,11 +106,11 @@ public class SsoController implements InterceptorSkipController {
return "redirect:/loginForm.do"; return "redirect:/loginForm.do";
} }
if (prop.isSsoSyncInfo()) { if (getProperty().isSsoSyncInfo()) {
// 기본 정보 // 기본 정보
Properties info = ssoModule.getUserInfos(ssoId); Properties info = getSsoModule().getUserInfos(ssoId);
// 확장 정보 // 확장 정보
Properties extendedInfo = ssoModule.getUserExFields(ssoId); Properties extendedInfo = getSsoModule().getUserExFields(ssoId);
// 필수 필드 검증 // 필수 필드 검증
try { try {
@@ -1,12 +1,8 @@
package com.eactive.eai.rms.onl.apim.portalorg; package com.eactive.eai.rms.onl.apim.portalorg;
import com.eactive.eai.rms.data.entity.man.monitoringProperty.service.MonitoringPropertyService;
import com.eactive.ext.kjb.common.KjbObpException; import com.eactive.ext.kjb.common.KjbObpException;
import com.eactive.ext.kjb.common.KjbProperty;
import com.eactive.ext.kjb.obp.KjbObpModule; import com.eactive.ext.kjb.obp.KjbObpModule;
import com.eactive.ext.kjb.util.KjbPropertyInjector;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.HashMap; import java.util.HashMap;
@@ -18,13 +14,9 @@ import java.util.Map;
@Slf4j @Slf4j
@Service @Service
public class ObpPartnerCodeService { public class ObpPartnerCodeService {
private static final String PROP_GROUP_ID = "Monitoring";
private final KjbPropertyInjector kjbPropertyInjector; private KjbObpModule getObpModule() {
return KjbObpModule.getInstance();
@Autowired
public ObpPartnerCodeService(MonitoringPropertyService monitoringPropertyService) {
this.kjbPropertyInjector = new KjbPropertyInjector(monitoringPropertyService, PROP_GROUP_ID);
} }
/** /**
@@ -36,10 +28,7 @@ public class ObpPartnerCodeService {
Map<String, Object> result = new HashMap<>(); Map<String, Object> result = new HashMap<>();
try { try {
KjbProperty prop = kjbPropertyInjector.inject(new KjbProperty()); String partnerCode = getObpModule().queryPartnerCode(compRegNo);
KjbObpModule obpModule = KjbObpModule.getInstance(prop);
String partnerCode = obpModule.queryPartnerCode(compRegNo);
result.put("success", true); result.put("success", true);
result.put("partnerCode", partnerCode); result.put("partnerCode", partnerCode);
@@ -2,11 +2,8 @@ package com.eactive.eai.rms.onl.common.service.ums;
import com.eactive.apim.portal.template.entity.MessageRequest; import com.eactive.apim.portal.template.entity.MessageRequest;
import com.eactive.apim.portal.template.repository.MessageRequestRepository; import com.eactive.apim.portal.template.repository.MessageRequestRepository;
import com.eactive.eai.rms.data.entity.man.monitoringProperty.service.MonitoringPropertyService;
import com.eactive.ext.kjb.common.KjbProperty;
import com.eactive.ext.kjb.ums.KjbEmailSendModule; import com.eactive.ext.kjb.ums.KjbEmailSendModule;
import com.eactive.ext.kjb.ums.KjbUmsService; import com.eactive.ext.kjb.ums.KjbUmsService;
import com.eactive.ext.kjb.util.KjbPropertyInjector;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
@@ -30,7 +27,6 @@ import javax.annotation.PostConstruct;
@Service @Service
@Transactional(transactionManager = "transactionManagerForEMS") @Transactional(transactionManager = "transactionManagerForEMS")
public class UmsDispatchService { public class UmsDispatchService {
public static final String PROP_GORUP_ID = "Monitoring";
private final ThreadPoolExecutor executorService; private final ThreadPoolExecutor executorService;
private final TransactionTemplate transactionTemplate; private final TransactionTemplate transactionTemplate;
@@ -44,19 +40,12 @@ public class UmsDispatchService {
private EntityManager entityManager; private EntityManager entityManager;
private final MessageRequestRepository messageRequestRepository; private final MessageRequestRepository messageRequestRepository;
private KjbUmsService kjbUmsService;
private KjbEmailSendModule kjbEmailSendModule;
private final KjbPropertyInjector kjbPropertyInjector;
private KjbProperty kjbProperty;
@Autowired @Autowired
public UmsDispatchService( public UmsDispatchService(
@Qualifier("transactionManagerForEMS") PlatformTransactionManager transactionManager, @Qualifier("transactionManagerForEMS") PlatformTransactionManager transactionManager,
MonitoringPropertyService monitoringPropertyService,
MessageRequestRepository messageRequestRepository MessageRequestRepository messageRequestRepository
) { ) {
this.kjbPropertyInjector = new KjbPropertyInjector(monitoringPropertyService, PROP_GORUP_ID);
this.messageRequestRepository = messageRequestRepository; this.messageRequestRepository = messageRequestRepository;
this.executorService = new ThreadPoolExecutor( this.executorService = new ThreadPoolExecutor(
@@ -74,36 +63,17 @@ public class UmsDispatchService {
@PostConstruct @PostConstruct
public void init() { public void init() {
this.kjbProperty = kjbPropertyInjector.inject(new KjbProperty()); // KjbEmailSendModule에 repository 설정
KjbEmailSendModule.getInstance().setRepository(messageRequestRepository);
try { log.info("UmsDispatchService initialized - KjbUmsService and KjbEmailSendModule ready via singleton");
this.kjbUmsService = new KjbUmsService(kjbProperty);
this.kjbEmailSendModule = KjbEmailSendModule.getInstance(kjbProperty, messageRequestRepository);
log.info("KjbUmsService and KjbEmailSendModule initialized successfully");
} catch (Exception e) {
log.error("Failed to initialize KjbUmsService or KjbEmailSendModule: {}", e.getMessage(), e);
}
} }
/** private KjbUmsService getUmsService() {
* URL 설정값이 비어있을 경우 DB에서 재로딩 후 서비스 재초기화 return KjbUmsService.getInstance();
*/ }
private void reloadPropertyIfNeeded() {
if (!kjbPropertyInjector.isUrlConfigured(kjbProperty)) {
log.info("KjbProperty URL 값이 비어있어 재로딩 시도");
KjbProperty reloaded = kjbPropertyInjector.reloadIfUrlEmpty(kjbProperty);
if (reloaded != kjbProperty && kjbPropertyInjector.isUrlConfigured(reloaded)) { private KjbEmailSendModule getEmailModule() {
this.kjbProperty = reloaded; return KjbEmailSendModule.getInstance();
try {
this.kjbUmsService = new KjbUmsService(kjbProperty);
this.kjbEmailSendModule = KjbEmailSendModule.getInstance(kjbProperty, messageRequestRepository);
log.info("KjbProperty 재로딩 후 서비스 재초기화 완료");
} catch (Exception e) {
log.error("KjbProperty 재로딩 후 서비스 재초기화 실패: {}", e.getMessage(), e);
}
}
}
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@@ -111,9 +81,6 @@ public class UmsDispatchService {
public void processMessages() { public void processMessages() {
log.info("Starting to process pending messages"); log.info("Starting to process pending messages");
// URL 설정값이 비어있을 경우 DB에서 재로딩 시도
reloadPropertyIfNeeded();
// 상태가 PENDING인 메시지들을 조회하고 바로 PROCESSING으로 변경 // 상태가 PENDING인 메시지들을 조회하고 바로 PROCESSING으로 변경
List<MessageRequest> pendingMessages = entityManager.createQuery( List<MessageRequest> pendingMessages = entityManager.createQuery(
"SELECT m FROM MessageRequest m WHERE m.requestStatus = :status " + "SELECT m FROM MessageRequest m WHERE m.requestStatus = :status " +
@@ -168,11 +135,11 @@ public class UmsDispatchService {
// processMessage(message); // processMessage(message);
if (KjbUmsService.isAllowChannel(message)) { if (KjbUmsService.isAllowChannel(message)) {
kjbUmsService.send(message); getUmsService().send(message);
} }
if (KjbEmailSendModule.isAllowChannel(message)) { if (KjbEmailSendModule.isAllowChannel(message)) {
kjbEmailSendModule.sendEmail(message); getEmailModule().sendEmail(message);
} }
return null; return null;