SSO 인증처리 및 구글 2단계 인증처리 기능확대
This commit is contained in:
parent
224ed4bd63
commit
4953c90feb
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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); // 사용자 정보 추출
|
||||
|
||||
|
||||
@ -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";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
169
src/main/java/com/urpsys/basefront/controller/SsoController.java
Normal file
169
src/main/java/com/urpsys/basefront/controller/SsoController.java
Normal 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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -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 >>>>>>>>>> ");
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package com.urpsys.basefront.config;
|
||||
package com.urpsys.basefront.interceptor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -1,4 +1,4 @@
|
||||
package com.urpsys.basefront.config;
|
||||
package com.urpsys.basefront.interceptor;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
@ -53,7 +53,8 @@ import lombok.extern.slf4j.Slf4j;
|
||||
*/
|
||||
@Slf4j
|
||||
@Controller
|
||||
public class PortletManageController {
|
||||
public class PortletManageController
|
||||
{
|
||||
|
||||
@Resource(name="RestApiCallUtil")
|
||||
private RestApiCallUtil restApiCallUtil;
|
||||
|
||||
@ -53,5 +53,7 @@
|
||||
client : kintex
|
||||
# 2026.03.12 URL 권한 체크 여부
|
||||
urlAuthCheck : Y
|
||||
# 2026.04.02 GoogleOTP 사용여부 체크
|
||||
GoogleOtpUseYn : N
|
||||
|
||||
|
||||
109
src/main/webapp/WEB-INF/jsp/main/Login_otp_check.jsp
Normal file
109
src/main/webapp/WEB-INF/jsp/main/Login_otp_check.jsp
Normal 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>
|
||||
106
src/main/webapp/WEB-INF/jsp/main/Login_otp_create.jsp
Normal file
106
src/main/webapp/WEB-INF/jsp/main/Login_otp_create.jsp
Normal 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>
|
||||
Loading…
Reference in New Issue
Block a user