SSO 인증처리 및 구글 2단계 인증처리 기능확대

This commit is contained in:
urp 2026-04-06 13:41:50 +09:00
parent 224ed4bd63
commit 4953c90feb
19 changed files with 859 additions and 23 deletions

View File

@ -105,7 +105,14 @@ dependencies
implementation 'org.apache.poi:poi-ooxml:4.1.2'
implementation 'org.apache.poi:poi-scratchpad:4.1.2'
implementation 'org.apache.poi:poi-examples:4.1.2'
// 2026.04.02 OTP
implementation 'com.warrenstrange:googleauth:1.5.0'
// 2026.04.02 OTP
implementation 'com.google.zxing:core:3.5.1'
implementation 'com.google.zxing:javase:3.5.1'
implementation 'de.taimos:totp:1.0'
}
test

View File

@ -0,0 +1,92 @@
package com.urpsys.basefront.common.service;
import java.io.ByteArrayOutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Base64;
import org.apache.commons.codec.binary.Base32;
import org.apache.commons.codec.binary.Hex;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.warrenstrange.googleauth.GoogleAuthenticator;
import com.warrenstrange.googleauth.GoogleAuthenticatorKey;
import de.taimos.totp.TOTP;
public class GoogleOtpService
{
/**
* 비밀키 생성.
*
* @return 32자리의 비밀키
*/
public static String generateSecretKey()
{
SecureRandom random = new SecureRandom();
byte[] bytes = new byte[20];
random.nextBytes(bytes);
Base32 base32 = new Base32();
return base32.encodeToString(bytes);
}
/**
* QR코드 URL 생성.
*
* @param displayName 표시할 이름
* @param secret 비밀키
* @return QR코드 URL
*/
public static String getQrCodeUrl(String displayName, String secret) throws Exception
{
String format = "otpauth://totp/" + URLEncoder.encode(displayName, StandardCharsets.UTF_8)
.replace("+", "%20")
+ "?secret=" + secret;
return generateQRCodeImage(format);
}
/**
* QR코드 이미지 생성.
*
* @param barcodeText 바코드 텍스트
* @return QR코드 이미지
*/
public static String generateQRCodeImage(String barcodeText) throws Exception
{
QRCodeWriter qrCodeWriter = new QRCodeWriter();
BitMatrix bitMatrix = qrCodeWriter.encode(barcodeText, BarcodeFormat.QR_CODE, 200, 200);
ByteArrayOutputStream pngOutputStream = new ByteArrayOutputStream();
MatrixToImageWriter.writeToStream(bitMatrix, "PNG", pngOutputStream);
return Base64.getEncoder().encodeToString(pngOutputStream.toByteArray());
}
/**
* OTP 체크.
*
* @param secretKey 비밀키 (32자리)
* @param otp OTP(6자리)
* @return true: 일치, false: 불일치
*/
public static boolean checkOtp(String secretKey, String otp)
{
return otp.equals(getOtpCode(secretKey));
}
public static String getOtpCode(String secretKey)
{
Base32 base32 = new Base32();
byte[] bytes = base32.decode(secretKey);
String hexKey = Hex.encodeHexString(bytes);
return TOTP.getOTP(hexKey);
}
}

View File

@ -59,8 +59,9 @@ public class LoginService implements UserDetailsService
log.info("srLoing 0 [{}]", srLoing[0]);
log.info("srLoing 1 [{}]", srLoing[1]);
log.info("srLoing 2 [{}]", srLoing[2]); // sso 처리여부 추가
if(srLoing.length != 2)
if(srLoing.length != 3)
{
return null;
}
@ -71,11 +72,12 @@ public class LoginService implements UserDetailsService
String strAuthClientSecret = "secretKey";
HttpResponse<String> resAuth = Unirest.post(strAuthServerUrl)
.basicAuth(strAuthClientId , strAuthClientSecret)
.queryString("grant_type" , "password")
.queryString("username" , srLoing[0])
.queryString("password" , srLoing[1])
.asString();
.basicAuth(strAuthClientId , strAuthClientSecret)
.queryString("grant_type" , "password")
.queryString("username" , srLoing[0])
.queryString("password" , srLoing[1])
.queryString("ssoYn" , srLoing[2])
.asString();
String returnAuth = resAuth.getBody();

View File

@ -447,7 +447,7 @@ public class CommonUtil
/**
Map 데이터를 JSONObject로 변환.
@version 1.0
@author Yoon Su-wan
@author 나혁제
@since JDK1.0
@param v_msg Map
@return JSONObject

View File

@ -68,8 +68,10 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
.antMatchers("/egov/images/**" ).permitAll()
.antMatchers("/EgovModal.do" ).permitAll()
.antMatchers("/main/noAuth*.do" ).permitAll()
.antMatchers("//validator*" ).permitAll()
.antMatchers("/validator*" ).permitAll()
.antMatchers("/main/ssoDirectlogin.do" ).permitAll() // 2026.04.02 나혁제 SSO 로그인처리
// .antMatchers("//validator*" ).permitAll()
// .antMatchers("/main/searchIdFind.do" ).permitAll()
// .antMatchers("/main/UserPasswordResetExt.do" ).permitAll()

View File

@ -7,6 +7,10 @@ import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.urpsys.basefront.interceptor.GoogleOtpCheckFrontInterceptor;
import com.urpsys.basefront.interceptor.ItsmMenuAuthCheckFrontInterceptor;
import com.urpsys.basefront.interceptor.UrpPortalFrontInterceptor;
/**
* 기본 웹서버 설정 클리스
*
@ -37,6 +41,9 @@ public class WebConfig implements WebMvcConfigurer
@Autowired
ItsmMenuAuthCheckFrontInterceptor itsmMenuAuthCheckFrontInterceptor;
// 2026.04.03 나혁제 구글 OTP 2단계 처리 interceptor
@Autowired
GoogleOtpCheckFrontInterceptor googleOtpCheckFrontInterceptor;
/*
@Value("${custom.path.phy_sitemap}")
@ -52,6 +59,8 @@ public class WebConfig implements WebMvcConfigurer
@Value("${custom.path.loc_svy}")
private String resourcesUriPathSvy; // 파일 URL 주소
@Value("${custom.GoogleOtpUseYn}")
private String strGoogleOtpUseYn; // 2026.04.03 나혁제 OTP 사용여부
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
@ -73,6 +82,18 @@ public class WebConfig implements WebMvcConfigurer
public void addInterceptors(InterceptorRegistry registry)
{
registry.addInterceptor(urpPortalFrontInterceptor).addPathPatterns("/**/*.do");
// 2026.04.03 나혁제 OTP 사용여부
if(strGoogleOtpUseYn.equals("Y"))
{
registry.addInterceptor(googleOtpCheckFrontInterceptor).addPathPatterns("/main/*.do");
registry.addInterceptor(googleOtpCheckFrontInterceptor).addPathPatterns("/srm/*.do");
registry.addInterceptor(googleOtpCheckFrontInterceptor).addPathPatterns("/bbs/*.do");
registry.addInterceptor(googleOtpCheckFrontInterceptor).addPathPatterns("/rem/*.do");
registry.addInterceptor(googleOtpCheckFrontInterceptor).addPathPatterns("/sys/*.do");
}
// 2026.03.02 나혁제 url 보안영부
registry.addInterceptor(itsmMenuAuthCheckFrontInterceptor).addPathPatterns("/srm/*.do");
registry.addInterceptor(itsmMenuAuthCheckFrontInterceptor).addPathPatterns("/bbs/*.do");
registry.addInterceptor(itsmMenuAuthCheckFrontInterceptor).addPathPatterns("/rem/*.do");

View File

@ -6,9 +6,11 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.context.annotation.Bean;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;

View File

@ -11,6 +11,7 @@ import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import com.urpsys.basefront.common.exception.CustomException;
import com.urpsys.basefront.common.util.CommonUtil;
@ -42,6 +43,7 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
public class CustomSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler
{
@Resource(name="RestApiCallUtil")

View File

@ -54,7 +54,8 @@ public class CustomAuthenticationProvider implements AuthenticationProvider
String username = (String) authentication.getPrincipal();
String password = (String) authentication.getCredentials();
String strLoginInfo = username+","+password;
// 2026.04.02 나혁제 SSO 처리여부 추가
String strLoginInfo = username+","+password+","+"N";
UserDetails info = loginService.loadUserByUsername(strLoginInfo); // 사용자 정보 추출

View File

@ -0,0 +1,175 @@
package com.urpsys.basefront.controller;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.codec.binary.Base32;
import org.egovframe.rte.fdl.property.EgovPropertyService;
import org.egovframe.rte.ptl.mvc.tags.ui.pagination.PaginationInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.urpsys.basefront.common.egov.util.EgovStringUtil;
import com.urpsys.basefront.common.service.CommonService;
import com.urpsys.basefront.common.service.GoogleOtpService;
import com.urpsys.basefront.common.service.LoginService;
import com.urpsys.basefront.common.util.CommonUtil;
import com.urpsys.basefront.common.util.ConvertUtils;
import com.urpsys.basefront.common.util.RestApiCallUtil;
import com.urpsys.basefront.config.handler.CustomSuccessHandler;
import com.urpsys.basefront.domain.MemberVO;
import com.urpsys.basefront.domain.TokenInfo;
import com.urpsys.itmsfront.cmm.domain.ComDefaultCodeVO;
import com.urpsys.itmsfront.sys.domain.PrsnlPortletCreateVO;
import com.urpsys.itmsfront.sys.domain.PrsnlPortletManageVO;
import com.urpsys.itmsfront.sys.domain.UserDefaultVO;
import com.urpsys.itmsfront.sys.domain.UserManageVO;
import kong.unirest.HttpResponse;
import kong.unirest.JsonNode;
import kong.unirest.Unirest;
import kong.unirest.json.JSONObject;
import lombok.extern.slf4j.Slf4j;
/**
* SSO 처리 Controller
* @author urp 인프라본부 나혁제
* @since 2026.04.02
* @version 1.0
* @see
*
* <pre>
* << 개정이력(Modification Information) >>
*
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.04.02 나혁제 최초 생성
*
* </pre>
*
*/
@Slf4j
@Controller
public class GoogleOtpController
{
@Resource(name="RestApiCallUtil")
private RestApiCallUtil restApiCallUtil;
/**
* otp key 발급처리
* @author urp 인프라본부 나혁제
* 작성일 : 2026.04.03
*/
@RequestMapping(value = "/otp/OrRegist.do")
public String OrRegist ( HttpServletRequest request
, HttpServletResponse response
, Model model) throws Exception
{
log.info(">>>>>>>>>>>>>>> /otpOrRegist.do <<<<<<<<<<<<<<<");
Map<String, Object> bodyMap = new LinkedHashMap();
Map<String, Object> returnMap = new LinkedHashMap();
String secretKey = GoogleOtpService.generateSecretKey();
log.info("secretKey [{}] ", secretKey);
TokenInfo tokenInfo = CommonUtil.getTokenInfo();
// Body Setting Start ---------------------------------
bodyMap.put("uniqId", tokenInfo.getUniqId() );
bodyMap.put("otpKey", secretKey );
// Body Setting End ---------------------------------
// RestApi Call Start ---------------------------------
returnMap = restApiCallUtil.RestApiCall(bodyMap, "/sys/updateOtpKey.do");
// RestApi Call End ---------------------------------
model.addAttribute("key", secretKey);
model.addAttribute("qr", GoogleOtpService.getQrCodeUrl("urITSM", secretKey));
log.info("<<<<<<<<<<<<<<< /otpOrRegist.do >>>>>>>>>>>>>>>");
return "/main/Login_otp_create";
}
/**
* otp 인증화면으로 이동처리
* @author urp 인프라본부 나혁제
* 작성일 : 2026.04.03
*/
@RequestMapping(value = "/otp/checkView.do")
public String checkView ( HttpServletRequest request
, HttpServletResponse response
, Model model) throws Exception
{
log.info(">>>>>>>>>>>>>>> /otp/checkView.do <<<<<<<<<<<<<<<");
Map<String, Object> bodyMap = new LinkedHashMap();
Map<String, Object> returnMap = new LinkedHashMap();
log.info("<<<<<<<<<<<<<<< /otp/checkView.do >>>>>>>>>>>>>>>");
return "/main/Login_otp_check";
}
@RequestMapping(value = "/otp/checkOtp.do")
public String check( HttpServletRequest request
, HttpServletResponse response
, Model model)
{
String strOtpSecretKey = CommonUtil.nvl(request.getSession().getAttribute("otpSecretKey")); // 인증키
String strUserOtp = CommonUtil.nvl(request.getParameter("userOtp")); // otp
boolean strOtpCheckYn = GoogleOtpService.checkOtp(strOtpSecretKey, strUserOtp);
// 세션에 인증성공 처리
if(strOtpCheckYn)
{
request.getSession().setAttribute("otpCheckYn", "Y");
}
return "forward:/main/mainPage.do";
}
}

View File

@ -15,7 +15,12 @@ import org.egovframe.rte.fdl.property.EgovPropertyService;
import org.egovframe.rte.ptl.mvc.tags.ui.pagination.PaginationInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
@ -31,9 +36,11 @@ import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.urpsys.basefront.common.egov.util.EgovStringUtil;
import com.urpsys.basefront.common.service.CommonService;
import com.urpsys.basefront.common.service.LoginService;
import com.urpsys.basefront.common.util.CommonUtil;
import com.urpsys.basefront.common.util.ConvertUtils;
import com.urpsys.basefront.common.util.RestApiCallUtil;
import com.urpsys.basefront.config.handler.CustomSuccessHandler;
import com.urpsys.basefront.domain.MemberVO;
import com.urpsys.basefront.domain.TokenInfo;
import com.urpsys.itmsfront.cmm.domain.ComDefaultCodeVO;
@ -86,6 +93,12 @@ public class MainController
@Autowired
private CommonService commonService;
@Autowired
private LoginService loginService;
@Autowired
private CustomSuccessHandler customSuccessHandler;
// @Autowired
// HtmlEmailUtil htmlEmailUtil;
@ -150,6 +163,10 @@ public class MainController
{
strErrorMsg = "사용자 정보는 존재하나 권한정보가 존재하지 않습니다. 시스템 관리자에게 연락해 주세요.";
}
else if(srError[0].equals("-5"))
{
strErrorMsg = "SSO 인증처리 실패하였습니다. 시스템 관리자에게 연락해 주세요.";
}
else
{
strErrorMsg = "";
@ -174,6 +191,8 @@ public class MainController
{
// 메인 페이지 이동
strReturnJsp = "forward:/main/mainPage.do";
// strReturnJsp = "forward:/otpOrRegist.do";
}
log.info("<<<<<<<<<<<<<<< /main/actionMain.do >>>>>>>>>>>>>>>");
@ -203,11 +222,11 @@ public class MainController
String strAuthUrl = strAuthServer+"/oauth/token";
HttpResponse<String> resAuth = Unirest.post(strAuthUrl)
.basicAuth("clientId", "secretKey")
.queryString("grant_type", "password")
.queryString("username", strId)
.queryString("password", strPassWord)
.asString();
.basicAuth("clientId", "secretKey")
.queryString("grant_type", "password")
.queryString("username", strId)
.queryString("password", strPassWord)
.asString();
String returnAuth = resAuth.getBody();
@ -684,11 +703,6 @@ public class MainController

View File

@ -0,0 +1,169 @@
package com.urpsys.basefront.controller;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.egovframe.rte.fdl.property.EgovPropertyService;
import org.egovframe.rte.ptl.mvc.tags.ui.pagination.PaginationInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.urpsys.basefront.common.egov.util.EgovStringUtil;
import com.urpsys.basefront.common.service.CommonService;
import com.urpsys.basefront.common.service.LoginService;
import com.urpsys.basefront.common.util.CommonUtil;
import com.urpsys.basefront.common.util.ConvertUtils;
import com.urpsys.basefront.common.util.RestApiCallUtil;
import com.urpsys.basefront.config.handler.CustomSuccessHandler;
import com.urpsys.basefront.domain.MemberVO;
import com.urpsys.basefront.domain.TokenInfo;
import com.urpsys.itmsfront.cmm.domain.ComDefaultCodeVO;
import com.urpsys.itmsfront.sys.domain.PrsnlPortletCreateVO;
import com.urpsys.itmsfront.sys.domain.PrsnlPortletManageVO;
import com.urpsys.itmsfront.sys.domain.UserDefaultVO;
import com.urpsys.itmsfront.sys.domain.UserManageVO;
import kong.unirest.HttpResponse;
import kong.unirest.JsonNode;
import kong.unirest.Unirest;
import kong.unirest.json.JSONObject;
import lombok.extern.slf4j.Slf4j;
/**
* SSO 처리 Controller
* @author urp 인프라본부 나혁제
* @since 2026.04.02
* @version 1.0
* @see
*
* <pre>
* << 개정이력(Modification Information) >>
*
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.04.02 나혁제 최초 생성
*
* </pre>
*
*/
@Slf4j
@Controller
public class SsoController
{
@Autowired
private LoginService loginService;
@Autowired
private CustomSuccessHandler customSuccessHandler;
/**
* SSO 연동 로그인 처리
* @author urp 인프라본부 나혁제
* 작성일 : 2026.04.02
*/
@RequestMapping(value = "/main/ssoDirectlogin.do")
public String ssoDirectlogin( HttpServletRequest request
, HttpServletResponse response
, ModelMap model) throws Exception
{
log.info(">>>>>>>>>>>>>>> /main/ssoDirectlogin.do <<<<<<<<<<<<<<<");
// --- SSO 연동 Start ---------------------------------
// boolean bSsoAuthYn = false;
boolean bSsoAuthYn = true;
// sso 인증처리
if(!bSsoAuthYn)
{
return "forward:/main/actionMain.do?error=-5:ssoFail";
}
// --- SSO 연동 End ---------------------------------
// 인증 Start ----
// String username = (String)request.getParameter("userId");
// String password = "";
String username = (String)request.getParameter("userId");
String password = "sso";
log.info("username => {}" , username );
log.info("password => {}" , password );
String strLoginInfo = username+","+password+","+"Y";
UserDetails info = loginService.loadUserByUsername(strLoginInfo); // 사용자 정보 추출
MemberVO member = (MemberVO) info;
if(info == null)
{
return "forward:/main/actionMain.do?error=-5:ssoFail";
}
// 시스템 토큰 등록
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(info, null, info.getAuthorities());
// 시스템 보안 등록
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(auth);
SecurityContextHolder.setContext(context);
//
request.getSession(true).setAttribute("SPRING_SECURITY_CONTEXT", context);
// 로그인 성공처리
customSuccessHandler.onAuthenticationSuccess(request, response, auth);
log.info("<<<<<<<<<<<<<<< /main/ssoDirectlogin.do >>>>>>>>>>>>>>>");
return null;
}
}

View File

@ -0,0 +1,131 @@
package com.urpsys.basefront.interceptor;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import com.urpsys.basefront.common.util.CommonUtil;
import com.urpsys.basefront.common.util.RestApiCallUtil;
import com.urpsys.basefront.domain.TokenInfo;
import lombok.extern.slf4j.Slf4j;
/**
* Controller Google OTP Check interceptor 클리스
*
* @author urp 인프라본부 나혁제
* @since 2026.04.03
* @version 1.0.0
* @see
*
* <pre>
*
* << 개정이력(Modification information) >>
*
* 수정일 수정자 수정내용
* ----------- --------- ------------------------
* 2026.04.03 나혁제 최초작성
*
* </pre>
*/
@Slf4j
@Component
public class GoogleOtpCheckFrontInterceptor implements HandlerInterceptor
{
@Resource(name="RestApiCallUtil")
private RestApiCallUtil restApiCallUtil;
// 컨트롤러가 호출되기
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
{
log.info(" >>>>>>>>>> preHandle <<<<<<<<<< ");
Map<String, Object> bodyMap = new LinkedHashMap();
Map<String, Object> returnMap = new LinkedHashMap();
// 로그아웃인경우 return;
if(request.getSession().getAttribute("tokenInfo") == null)
{
return true;
}
// 인증키 체크
String strOtpSecretKey = CommonUtil.nvl(request.getSession().getAttribute("otpSecretKey"));
// 키가 없는경우 개인키 추출
if(strOtpSecretKey.equals(""))
{
TokenInfo tokenInfo = CommonUtil.getTokenInfo();
// Body Setting Start ---------------------------------
bodyMap.put("uniqId" , tokenInfo.getUniqId());
// Body Setting End ---------------------------------
// RestApi Call Start ---------------------------------
returnMap = restApiCallUtil.RestApiCall(bodyMap, "/sys/selectOtpKey.do");
// RestApi Call End ---------------------------------
strOtpSecretKey = CommonUtil.nvl(returnMap.get("strOtpKey"));
// 발급이 안된경우 발급처리
if(strOtpSecretKey.equals(""))
{
response.sendRedirect("/otp/OrRegist.do");
return true;
}
// 세션 등록 처리
request.getSession().setAttribute("otpSecretKey", strOtpSecretKey);
}
// otp 인증여부 확인
String strOtpCheckYn = CommonUtil.nvl(request.getSession().getAttribute("otpCheckYn"));
if(!strOtpCheckYn.equals("Y"))
{
// otp 인증 페이지로 이동처리
response.sendRedirect("/otp/checkView.do");
return true;
}
log.info(" <<<<<<<<<< preHandle >>>>>>>>>> ");
return true;
}
// controller의 handler가 끝나면 처리됨
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception
{
/*
log.info(" >>>>>>>>>> postHandle <<<<<<<<<< ");
log.info(" <<<<<<<<<< postHandle >>>>>>>>>> ");
*/
}
// 컨트롤러가 처리 완료후
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception
{
/*
log.info(" >>>>>>>>>> afterCompletion <<<<<<<<<< ");
log.info(" <<<<<<<<<< afterCompletion >>>>>>>>>> ");
*/
}
}

View File

@ -1,4 +1,4 @@
package com.urpsys.basefront.config;
package com.urpsys.basefront.interceptor;
import java.util.List;
import java.util.Map;

View File

@ -1,4 +1,4 @@
package com.urpsys.basefront.config;
package com.urpsys.basefront.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

View File

@ -53,7 +53,8 @@ import lombok.extern.slf4j.Slf4j;
*/
@Slf4j
@Controller
public class PortletManageController {
public class PortletManageController
{
@Resource(name="RestApiCallUtil")
private RestApiCallUtil restApiCallUtil;

View File

@ -53,5 +53,7 @@
client : kintex
# 2026.03.12 URL 권한 체크 여부
urlAuthCheck : Y
# 2026.04.02 GoogleOTP 사용여부 체크
GoogleOtpUseYn : N

View File

@ -0,0 +1,109 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%--
Class Name : LoginUsr.jsp
Description : 로그인화면
Modification Information
수정일 수정자 수정내용
------- -------- ---------------------------
2025.01.03 나혁제 최초생성
author : 인프라본부 나혁제
since : 2025.01.02
--%>
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<c:set var="ctx" value="${pageContext.request.contextPath}"/>
<title> UR-ITSM </title>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<html>
<head>
<title>urITSM</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<meta name="keywords" content="uWorks Portal">
<meta name="description" content="uWorks Portal에 오신것을 환영합니다.">
<link rel="stylesheet" type="text/css" href="${ctx}/css/import.css">
<script type="text/javascript" src="${ctx}/js/jquery-2.2.4.min.js"></script>
<script type="text/javascript" src="${ctx}/js/common_script.js"></script>
<link rel="stylesheet" href="${ctx}/js/js-calendar/calendar-jquery-ui.css">
<script type="text/javascript" src="${ctx}/js/js-calendar/jquery.js"></script>
<script type="text/javascript" src="${ctx}/js/js-calendar/jquery-ui.js"></script>
<script type="text/javascript" src="${ctx}/js/js-calendar/calendar.js"></script>
</head>
<script type="text/javascript">
//----------------------------------------
// 화면 초기화
// ----------------------------------------
$(document).ready(function()
{
});
//----------------------------------------
// otp 인증 체크
// ----------------------------------------
function oto_auth_check()
{
document.otpAuthForm.action="/otp/checkOtp.do";
document.otpAuthForm.submit();
}
</script>
<body>
<div style="display: flex;align-items: flex-start;">
<!-- login :s -->
<div class="logback">
</div>
<!-- login :s -->
<div class="logback">
<h1><img src="${ctx}/images/main/logo.png" alt="uWorks Portal logo"></h1>
<h3>urITSM 에 오신것을 환영합니다.</h3>
<!-- login_box :s -->
<div class="login_box">
<!-- loginWrap :s -->
<div class="loginWrap">
<div class="input_wrap">
<form:form id="otpAuthForm" name="otpAuthForm" method="post">
<br> OTP 을 입력해주세요.
<br>
<label for="member_id" class="hidden">OTP 입력</label>
<input type="text" name="userOtp" id="userOtp" class="txt_id" placeholder="OTP">
<button type="button" class="btn_login" onclick="javascript:oto_auth_check()" >인증</button>
</form:form>
</div>
</div>
<!--//loginWrap :e -->
</div>
<!--//login_box :e -->
</div>
<!--//login :e -->
</div>
</body>
</html>

View File

@ -0,0 +1,106 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%--
Class Name : LoginUsr.jsp
Description : 로그인화면
Modification Information
수정일 수정자 수정내용
------- -------- ---------------------------
2025.01.03 나혁제 최초생성
author : 인프라본부 나혁제
since : 2025.01.02
--%>
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<c:set var="ctx" value="${pageContext.request.contextPath}"/>
<title> UR-ITSM </title>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<html>
<head>
<title>urITSM</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<meta name="keywords" content="uWorks Portal">
<meta name="description" content="uWorks Portal에 오신것을 환영합니다.">
<link rel="stylesheet" type="text/css" href="${ctx}/css/import.css">
<script type="text/javascript" src="${ctx}/js/jquery-2.2.4.min.js"></script>
<script type="text/javascript" src="${ctx}/js/common_script.js"></script>
<link rel="stylesheet" href="${ctx}/js/js-calendar/calendar-jquery-ui.css">
<script type="text/javascript" src="${ctx}/js/js-calendar/jquery.js"></script>
<script type="text/javascript" src="${ctx}/js/js-calendar/jquery-ui.js"></script>
<script type="text/javascript" src="${ctx}/js/js-calendar/calendar.js"></script>
</head>
<script type="text/javascript">
//----------------------------------------
// 화면 초기화
// ----------------------------------------
$(document).ready(function()
{
});
//----------------------------------------
// 메인 접속 처리
// ----------------------------------------
function oto_re_connect()
{
document.otpAuthForm.action="/main/actionMain.do";
document.otpAuthForm.submit();
}
</script>
<body>
<div style="display: flex;align-items: flex-start;">
<!-- login :s -->
<div class="logback">
</div>
<!-- login :s -->
<div class="logback">
<h1><img src="${ctx}/images/main/logo.png" alt="uWorks Portal logo"></h1>
<h3>urITSM 에 오신것을 환영합니다.</h3>
<!-- login_box :s -->
<div class="login_box">
<!-- loginWrap :s -->
<div class="loginWrap">
<div class="input_wrap">
<form:form id="otpAuthForm" name="otpAuthForm" method="post">
비밀키 <input type="text" name="secretKey" id="secretKey" value="${key}" readonly class="txt_id">
<br> 아래 OR 코드을 스캔하여
<br> Authenticator 에 등록해 주시길 바랍니다.
<img src="data:image/jpeg;base64, ${qr}" alt="QR Code">
<button type="button" class="btn_login" onclick="javascript:oto_re_connect()" >접속하기</button>
</form:form>
</div>
</div>
<!--//loginWrap :e -->
</div>
<!--//login_box :e -->
</div>
<!--//login :e -->
</div>
</body>
</html>