Procházet zdrojové kódy

구글 리캡쳐 기능 추가

jsshin před 5 roky
rodič
revize
c4fb1c914d

+ 8 - 1
src/main/java/com/style24/front/biz/dao/TsfLoginDao.java

@@ -103,6 +103,13 @@ public interface TsfLoginDao {
 	 * @author gagamel
 	 * @since 2020. 6. 17
 	 */
-	void deletePersistentToken(String remembermeToken);
 
+	void deletePersistentToken(String remembermeToken);
+	/**
+	 * 로그인실패 건수가 10회 이상이고 3분 지났으면 실패횟수 초기화
+	 * @param login - 고객ID(=로그인ID)
+	 * @author jsshin
+	 * @since 2021. 03. 31
+	 */
+	void updateLoginFailInfo(Login login);
 }

+ 14 - 0
src/main/java/com/style24/front/biz/service/TsfLoginService.java

@@ -116,6 +116,20 @@ public class TsfLoginService {
 		return loginDao.getLoginFailInfo(login);
 	}
 
+	/**
+	 * 로그인실패 건수가 10회 이상이고 3분 지났으면 실패횟수 초기화
+	 * @param custId - 고객ID(=로그인ID)
+	 * @author jsshin
+	 * @since 2021. 03. 31
+	 */
+	public void updateLoginFailInfo(String custId) {
+		Login login = new Login();
+		login.setCustId(custId);
+		login.setIpAddr(TsfSession.getIpAddress());
+		login.setSiteCd(TscConstants.Site.STYLE24.value());
+		loginDao.updateLoginFailInfo(login);
+	}
+
 	/**
 	 * 로그인유지토큰 생성
 	 * @param custNo - 고객번호

+ 78 - 0
src/main/java/com/style24/front/biz/thirdparty/GoogleReCaptcha.java

@@ -0,0 +1,78 @@
+package com.style24.front.biz.thirdparty;
+
+import com.gagaframework.web.parameter.GagaMap;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.env.Environment;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.RestTemplate;
+
+import javax.annotation.PostConstruct;
+import java.net.URI;
+
+@Component
+@Slf4j
+public class GoogleReCaptcha {
+	@Autowired
+	private Environment env;
+
+	@Autowired
+	private RestTemplate restTemplate;
+
+	private String siteKey;
+	private String secretKey;
+	private String verifyUrl;
+
+	@PostConstruct
+	public void init() {
+		siteKey = env.getProperty("google.siteKey");
+		secretKey = env.getProperty("google.secretKey");
+		verifyUrl = env.getProperty("google.recaptchaVerifyUrl");
+
+		log.info("\n\n---- Google initialization started ----");
+		log.info("siteKey: [{}]", siteKey);
+		log.info("secretKey: [{}]", secretKey);
+		log.info("verifyUrl: [{}]", verifyUrl);
+		log.info("\n--- Google initialization completed ----\n");
+	}
+
+	public GagaMap getVerify(String token) {
+		GagaMap resultMap = new GagaMap();
+		try {
+			MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
+			params.add("secret", secretKey);
+			params.add("response", token);
+
+			// Header
+			HttpHeaders headers = new HttpHeaders();
+			headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+
+			HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(params, headers);
+			URI url = URI.create(verifyUrl);
+
+			// POST방식으로 호출
+			ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, request, String.class);
+			log.info("responseEntity.getStatusCode(): {} ", responseEntity.getStatusCode());
+
+			String jsonResult = responseEntity.getBody();
+			log.info("responseEntity.getBody(): {} ", jsonResult);
+
+			Gson gson = new GsonBuilder().create();
+			resultMap = gson.fromJson(jsonResult, GagaMap.class);
+		} catch (Exception e) {
+			log.error(e.getMessage());
+		}
+		return  resultMap;
+	}
+
+
+
+}

+ 7 - 7
src/main/java/com/style24/front/biz/thirdparty/Yes24Login.java

@@ -69,13 +69,13 @@ public class Yes24Login {
 		unlinkUrl = env.getProperty("yes24.unlinkUrl");
 
 		log.debug("\n\n---- YES24 initialization started ----");
-		log.info("id: [{}]", id);
-		log.info("type: [{}]", type);
-		log.info("requestUrl: [{}]", requestUrl);
-		log.info("callBackUrl: [{}]", callBackUrl);
-		log.info("userInfoUrl: [{}]", userInfoUrl);
-		log.info("linkUrl: [{}]", linkUrl);
-		log.info("unlinkUrl: [{}]", unlinkUrl);
+		log.debug("id: [{}]", id);
+		log.debug("type: [{}]", type);
+		log.debug("requestUrl: [{}]", requestUrl);
+		log.debug("callBackUrl: [{}]", callBackUrl);
+		log.debug("userInfoUrl: [{}]", userInfoUrl);
+		log.debug("linkUrl: [{}]", linkUrl);
+		log.debug("unlinkUrl: [{}]", unlinkUrl);
 		log.debug("\n--- YES24 initialization completed ----\n");
 	}
 

+ 42 - 6
src/main/java/com/style24/front/biz/web/TsfIndexController.java

@@ -8,13 +8,16 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 
+import com.style24.front.biz.thirdparty.GoogleReCaptcha;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.core.env.Environment;
 import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
 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.fasterxml.jackson.databind.ObjectMapper;
@@ -61,6 +64,9 @@ public class TsfIndexController extends TsfBaseController {
 	@Autowired
 	private Yes24Login yes24Login;
 
+	@Autowired
+	private GoogleReCaptcha googleReCaptcha;
+
 	@Autowired
 	private ObjectMapper objectMapper;
 
@@ -139,9 +145,10 @@ public class TsfIndexController extends TsfBaseController {
 
 	/**
 	 * 로그인 페이지
-	 * @param error - 로그인 오류 정보
+	 *
+	 * @param isError - 오류여부
 	 * @param session - HttpSession
-	 * @return
+	 * @return ModelAndView
 	 * @author gagamel
 	 * @throws IOException
 	 * @since 2020. 2. 4
@@ -163,6 +170,7 @@ public class TsfIndexController extends TsfBaseController {
 		}
 
 		mav.addObject("front", env.getProperty("domain.front"));
+		mav.addObject("sitekey", env.getProperty("google.sitekey"));
 
 		// 로그인 페이지로
 		mav.setViewName(super.getDeviceViewName("SigninForm"));
@@ -228,7 +236,8 @@ public class TsfIndexController extends TsfBaseController {
 	 * @since 2021. 02. 23
 	 */
 	@GetMapping("/signin/yes24login")
-	public String signinYes24Login(@RequestParam(value = "rememberMe", required = false) String rememberMe, @RequestParam(value = "requestGb", required = false) String requestGb) {
+	public String signinYes24Login(@RequestParam(value = "rememberMe", required = false) String rememberMe
+			, @RequestParam(value = "requestGb", required = false) String requestGb) {
 
 		if (StringUtils.isNotBlank(rememberMe)) {
 			// RememberMe 세션 저장
@@ -246,7 +255,7 @@ public class TsfIndexController extends TsfBaseController {
 	}
 
 	/**
-	 * SNS 로그인
+	 * SNS 로그인 콜백
 	 *
 	 * @param session - HttpSession
 	 * @param snsType - SNS유형(NV:네이버, KK:카카오)
@@ -255,7 +264,9 @@ public class TsfIndexController extends TsfBaseController {
 	 * @since 2021. 02. 23
 	 */
 	@RequestMapping("/signin/snsLoginCallback")
-	public ModelAndView signinSnsLoginCallback(@RequestParam(value = "snsType", required = false) String snsType, HttpSession session, @RequestParam(value = "code", required = false) String code, @RequestParam(value = "state", required = false) String state, @RequestParam(value = "error", required = false) boolean isError, HttpServletRequest request) {
+	public ModelAndView signinSnsLoginCallback(@RequestParam(value = "snsType", required = false) String snsType, HttpSession session
+			, @RequestParam(value = "code", required = false) String code, @RequestParam(value = "state", required = false) String state
+			, @RequestParam(value = "error", required = false) boolean isError, HttpServletRequest request) {
 
 		ModelAndView mav = new ModelAndView();
 
@@ -331,8 +342,17 @@ public class TsfIndexController extends TsfBaseController {
 		return mav;
 	}
 
+	/**
+	 * SNS - YES24 로그인 콜백URL
+	 *
+	 * @param ipin - Yes24넘겨 주는 암호화된 값:날짜|CI
+	 * @param session - Yes24넘겨받은 정보 세션저장
+	 * @return ModelAndView
+	 * @author jsshin
+	 * @since 2021. 02. 23
+	 */
 	@RequestMapping("/signin/yes24LoginCallback")
-	public ModelAndView yes24LoginCallback(@RequestParam("ipin") String ipin, HttpSession session, HttpServletRequest request) {
+	public ModelAndView yes24LoginCallback(@RequestParam("ipin") String ipin, HttpSession session) {
 		ModelAndView mav = new ModelAndView();
 		boolean isSnsLoing = false;
 		GagaMap userInfo = new GagaMap();
@@ -375,4 +395,20 @@ public class TsfIndexController extends TsfBaseController {
 		return mav;
 	}
 
+	/**
+	 * 구글 리캡챠 확인
+	 *
+	 * @param request - HttpServletRequest
+	 * @return GagaMap
+	 * @author jsshin
+	 * @since 2021. 03. 31
+	 */
+	@PostMapping("/verify/recaptcha")
+	@ResponseBody
+	public GagaMap verfyRecaptcha(HttpServletRequest request) {
+		String token = request.getParameter("token");
+		log.info("token {}", token);
+		return googleReCaptcha.getVerify(token);
+	}
+
 }

+ 3 - 3
src/main/java/com/style24/front/support/security/TsfAuthenticationProvider.java

@@ -86,9 +86,9 @@ public class TsfAuthenticationProvider implements AuthenticationProvider {
 		}
 
 		// 로그인 실패누적건수가 5회 이상이면
-		if (loginInfo.getLoginFailCnt() >= LOGIN_FAIL_COUNT) {
-			throw new TsfLockedAccountException(message.getMessage("LOGN_0005"));
-		}
+//		if (loginInfo.getLoginFailCnt() >= LOGIN_FAIL_COUNT) {
+//			throw new TsfLockedAccountException(message.getMessage("LOGN_0005"));
+//		}
 
 		/// SNS로그인이 아닌 일반로그인 이면
 		if (!loginId.startsWith(TsfConstants.SNSLOGIN_PREFIX)) {

+ 9 - 1
src/main/java/com/style24/front/support/security/handler/TsfLoginFailureHandler.java

@@ -7,6 +7,7 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import com.style24.front.support.exception.TsfNonCertificationAccountException;
+import com.style24.persistence.domain.Login;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.web.authentication.AuthenticationFailureHandler;
@@ -64,8 +65,15 @@ public class TsfLoginFailureHandler implements AuthenticationFailureHandler {
 			result.setString("status", "ETC_ERROR");
 		}
 
+		Login loginFailInfo = loginService.getLoginFailInfo(request.getParameter("loginId"));
+
+		// 실패 횟수 10회 이상이면서 3분 지난경우 초기화 해준다.
+		if (loginFailInfo.getLoginFailCnt() >= 10 && loginFailInfo.getBlockSecs() <= 0) {
+			loginService.updateLoginFailInfo(request.getParameter("loginId"));
+		}
 		// 로그인실패정보 조회
-		result.set("loginFailInfo", loginService.getLoginFailInfo(request.getParameter("loginId")));
+		result.set("loginFailInfo", loginFailInfo);
+
 
 		GagaStringUtil.write(response, result);
 	}

+ 26 - 5
src/main/java/com/style24/persistence/mybatis/shop/TsfLogin.xml

@@ -18,7 +18,9 @@
 		     , EMAIL                                                     /*이메일*/
 		     , #{snsType}                              AS SNS_TYPE       /*SNS유형*/
 		     , #{snsId}                                AS SNS_ID         /*SNS가입ID*/
-		     , IFNULL((SELECT LOGIN_FAIL_CNT
+		     , IFNULL((SELECT CASE WHEN LOGIN_FAIL_CNT > 10 THEN 10
+		                           ELSE LOGIN_FAIL_CNT
+		                      END
 		               FROM   TB_LOGIN_FAIL
 		               WHERE  CUST_ID = #{custId}
 		               AND    IP_ADDR = #{ipAddr}
@@ -66,19 +68,26 @@
 		)
 		ON DUPLICATE KEY UPDATE
 		       LOGIN_FAIL_CNT = CASE WHEN #{loginFailYn} = 'Y' THEN
-		                                 CASE WHEN LOGIN_FAIL_CNT = 10 THEN LOGIN_FAIL_CNT
+		                                 CASE WHEN LOGIN_FAIL_CNT = 11 THEN LOGIN_FAIL_CNT
 		                                      ELSE LOGIN_FAIL_CNT + 1
 		                                 END
 		                             ELSE
 		                                 0
 		                        END
-		     , UPD_DT = NOW()
+		     , UPD_DT = CASE WHEN #{loginFailYn} = 'Y' THEN
+		                                 CASE WHEN LOGIN_FAIL_CNT = 11 THEN UPD_DT
+		                                      ELSE NOW()
+		                                 END
+		                     ELSE NOW()
+		                END
 	</insert>
 
 	<!-- 로그인 실패건수 조회 -->
 	<select id="getLoginFailCount" parameterType="Login" resultType="int">
 		/* TsfLogin.getLoginFailCount */
-		SELECT IFNULL((SELECT LOGIN_FAIL_CNT
+		SELECT IFNULL((SELECT CASE WHEN LOGIN_FAIL_CNT > 10 THEN 10
+		                           ELSE LOGIN_FAIL_CNT
+		                      END
 		               FROM   TB_LOGIN_FAIL
 		               WHERE  CUST_ID = #{custId}
 		               AND    IP_ADDR = #{ipAddr}
@@ -90,7 +99,10 @@
 	<!-- 로그인실패정보 조회 -->
 	<select id="getLoginFailInfo" parameterType="Login" resultType="Login">
 		/* TsfLogin.getLoginFailInfo */
-		SELECT LOGIN_FAIL_CNT
+		SELECT CASE WHEN LOGIN_FAIL_CNT > 10 THEN 10
+		            ELSE LOGIN_FAIL_CNT
+
+		        END           AS LOGIN_FAIL_CNT
 		     , CASE WHEN LOGIN_FAIL_CNT <![CDATA[>=]]> 10 THEN
 		                180 - TIMESTAMPDIFF(SECOND,UPD_DT,NOW())
 		            ELSE
@@ -209,4 +221,13 @@
 		WHERE  REMEMBERME_TOKEN = #{remembermeToken}
 	</delete>
 
+	<!-- 로그인 실패횟수 초기화-->
+	<update id="updateLoginFailInfo" parameterType="Login">
+		UPDATE TB_LOGIN_FAIL
+		SET    LOGIN_FAIL_CNT = 0
+		WHERE  CUST_ID = #{custId}
+		AND    IP_ADDR = #{ipAddr}
+		AND    SITE_CD = #{siteCd}
+	</update>
+
 </mapper>

+ 7 - 0
src/main/resources/config/application.yml

@@ -108,6 +108,13 @@ yes24 :
     linkUrl : https://wsyes24.yes24.com/Yes_Mem.asmx/Yes_Partner_Insert
     unlinkUrl : https://wsyes24.yes24.com/Yes_Mem.asmx/Yes_Partner_Remove
 
+google :
+    # 개발정보
+    sitekey : 6LcY_JUaAAAAAIy1nHPE_lTkpIa51ihEd-waJCeO
+    secretKey : 6LcY_JUaAAAAAFux56mVih0a5NUwP3ARq0HUHR9p
+    # 개발정보
+    recaptchaVerifyUrl : https://www.google.com/recaptcha/api/siteverify
+
 
 # SPEEDY Image Upload
 speedy:

+ 122 - 143
src/main/webapp/WEB-INF/views/web/SigninFormWeb.html

@@ -17,7 +17,6 @@
  *******************************************************************************
  -->
 <body>
-
 <th:block layout:fragment="content">
 <div id="container" class="container mb">
 	<div class="wrap">
@@ -52,21 +51,9 @@
 					<div class="help_block">
 						<!-- //회원 아이디 또는 비밀번호가 일치하지 않을때 -->
 						<!-- 보안문자 입력시 -->
-						<div class="captcha mt40" style="display: none;"> <!-- 캡차영역 -->
-							<ul>
-								<li class="captcha_box"> <!-- 캡차이미지 -->
-									<img src="" id="imgCaptcha"/>
-								</li>
-								<li class="captcha_btn_dual">
-									<button type="button" id="play_audio" onclick="fnReloadCaptchaImage();">새로고침</button>
-									<button type="button" id="swap_captcha">음성듣기</button>
-								</li>
-								<li class="captcha_area">
-									<label for="captcha" id="label_captcha_area">보안문자 입력</label>
-									<input type="text" id="captcha" name="captcha" title="문자입력"/>
-								</li>
-							</ul>
-						</div>
+
+						<div id="recaptcha" class="g-recaptcha" style="display: none;" data-callback="fnRecaptchaCallback" th:data-sitekey="${sitekey}"></div>
+
 						<!-- //보안문자 입력시 -->
 						<!-- 회원 아이디 또는 비밀번호가 일치하지 않을때 -->
 						<p class="t_err t_err_login_fail" style="display: none;">
@@ -121,112 +108,31 @@
 		</div>
 	</div>
 </div>
-
 <script th:inline="javascript">
 /*<![CDATA[*/
 	let ckLoginId = "ckLoginId";
+	let rechaptchCheck = true;
 
-	// 로그인블락시간(초) 설정
-	let fnSetLoginBlockTime = function() {
-		let blockSecs = Number($("#blockSecs2").html()) - 1;
-		$("#blockSecs2").html(blockSecs);
-		if (blockSecs == 0) {
-			clearTimeout(fnSetLoginBlockTime);
-			$('#btnLogin').attr('disabled',false);
-		} else {
-			setTimeout(fnSetLoginBlockTime, 1000);
-		}
-	}
-	
 	// 로그인
 	$('#btnLogin').on('click', function() {
 		if (!gagajf.validation($('#loginForm'))) {
 			return;
 		}
+		if (!rechaptchCheck) {
+			mcxDialog.alert("자동 방지 봇을 확인 한뒤 진행 해 주세요.");
+			return;
+		}
 
-		let params = new Object();
+		let params = {};
 		params.loginId = $('#loginForm input[name=loginId]').val();
 		params.passwd = $('#loginForm input[name=passwd]').val();
-		
+
 		$.post($('#loginForm').prop('action')
 			, $.param(params)
-			, function(result) {
-				if (result.status != 'OK') {
-
-					if (result.status == 'PWD_5WRONG') {
-						// Do nothing
-					} else if (result.status == 'DORMANT_CUST') {
-						// 휴면회원
-						mcxDialog.alertC("휴면 고객님께서는 휴면을<br>해지하신 후 사용하실 수 있습니다.", {
-							sureBtnText: "확인",
-							sureBtnClick: function() {
-								cfnGoToPage(_PAGE_CUSTOMER_DORMANT);
-							}
-						});
-						return;
-					} else if (result.status == 'SECEDE_CUST') {
-						// 탈퇴회원
-					} else if (result.status == 'SESSION_EXPIRED') {
-						// 세션만료
-					} else if (result.status == 'CI_EMPTY') {
-						// 본인이증 필요한 회원
-						mcxDialog.alertC("본인인증 후 다시 로그인 하시기 바랍니다.", {
-							sureBtnText: "확인",
-							sureBtnClick: function() {
-								cfnGoToPage(_PAGE_CUSTOMER_CERTIFICATION);
-							}
-						});
-						return;
-					}
-
-					if (!gagajf.isNull(result.message)) {
-						let loginFailCnt = Number(result.loginFailInfo.loginFailCnt);
-						
-						$("#err_msg").html(result.message);
-						$("#login_fail_cnt").html(loginFailCnt);
-						$("#login_remain_cnt").html(10 - loginFailCnt);
-						$(".t_err_login_fail").show();
-						
-						if (loginFailCnt >= 5 && loginFailCnt < 10) {
-							// 비밀번호 5회 이상 틀린 경우 캡챠 노출
-							$('.captcha').show();
-						} else if (loginFailCnt >= 10) {
-							$('.captcha').hide();
-							
-							// 비밀번호 10회 이상 틀린 경우 로그인 블락
-							let blockSecs = Number(result.loginFailInfo.blockSecs);
-							if (blockSecs > 0) {
-								$("#blockSecs2").html(blockSecs);
-								fnSetLoginBlockTime();
-								$("#blockSecs1").show();
-								$('#btnLogin').attr('disabled',true);
-							}
-						}
-					}
-
-					return; // 정상적으로 로그인 되지 않았으므로 return
-				}
-				
-				document.location.href = result.returnUrl;
-				
-// 				if (gagajf.isNull(result.returnUrl)) {
-// 					cfnGoToPage(_PAGE_MAIN);
-// 				} else {
-// 					if (result.returnUrl.indexOf(_PAGE_DIRECT_BUY) > -1) {
-// 						// 바로주문
-// 						jfOrderByMember();
-// 					} else if (result.returnUrl.indexOf(_PAGE_CUSTOMER_JOIN) > -1 ||
-// 						result.returnUrl.indexOf(_PAGE_CUSTOMER_ID_FIND) > -1 ||
-// 						result.returnUrl.indexOf(_PAGE_CUSTOMER_PW_FIND) > -1 ||
-// 						result.returnUrl.indexOf(_PAGE_CUSTOMER_JOIN_COMPLETE) > -1) {
-// 						cfnGoToPage(_PAGE_MAIN);
-// 					} else {
-// 						document.location.href = result.returnUrl;
-// 					}
-// 				}
-			}
+			, fnReloadAfterLogin
 			, 'json');
 	});
+
 	
 	// Save ID
 	$('#chkSaveId').on('click', function() {
@@ -240,10 +146,106 @@
 			}
 		}
 	});
-	
-	// 캡챠 이미지 로딩
-	let fnReloadCaptchaImage = function() {
-		$('#imgCaptcha').attr('src', '/common/captcha.do?dummy=' + new Date());
+
+		//엔터키 로그인
+	$('#loginForm input[name=passwd]').keypress(function (event) {
+		if (event.which === 13) {
+			event.preventDefault();
+			fnValidConfirm();
+		}
+	});
+
+	// 로그인 후 처리
+	var fnReloadAfterLogin = function(result) {
+		if (result.status === 'OK') {
+			document.location.href = result.returnUrl;
+		} else {
+			fnFailLoginProcess(result);
+		}
+	};
+
+	// 로그인 실패 후 과정
+	var fnFailLoginProcess = function (result) {
+		if (result.status === 'SECEDE_CUST') {
+			// 탈퇴회원
+		} else if (result.status === 'SESSION_EXPIRED') {
+			// 세션만료
+		} else if (result.status === 'DORMANT_CUST') {
+			// 휴면회원
+			mcxDialog.alertC("휴면 고객님께서는 휴면을<br>해지하신 후 사용하실 수 있습니다.", {
+				sureBtnText: "확인",
+				sureBtnClick: function() {
+					cfnGoToPage(_PAGE_CUSTOMER_DORMANT);
+				}
+			});
+			return;
+		} else if (result.status === 'CI_EMPTY') {
+			// 본인이증 필요한 회원
+			mcxDialog.alertC("본인인증 후 다시 로그인 하시기 바랍니다.", {
+				sureBtnText: "확인",
+				sureBtnClick: function() {
+					cfnGoToPage(_PAGE_CUSTOMER_CERTIFICATION);
+				}
+			});
+			return;
+		}
+		if (!gagajf.isNull(result.message)) {
+			grecaptcha.reset(); // 리캡챠 리셋
+			let loginFailCnt = Number(result.loginFailInfo.loginFailCnt);
+			$("#err_msg").html(result.message);
+			$("#login_fail_cnt").html(loginFailCnt);
+			$("#login_remain_cnt").html(10 - loginFailCnt);
+			$(".t_err_login_fail").show();
+			if (loginFailCnt >= 5 && loginFailCnt < 10) {
+				// 비밀번호 5회 이상 틀린 경우 캡챠 노출
+				$('#recaptcha').show();
+				rechaptchCheck = false;
+			} else if (loginFailCnt >= 10) {
+				$('#recaptcha').hide();
+				rechaptchCheck = true;
+				// 비밀번호 10회 이상 틀린 경우 로그인 블락
+				let blockSecs = Number(result.loginFailInfo.blockSecs);
+
+				if (blockSecs > 0) {
+					$("#blockSecs2").html(blockSecs);
+					fnSetLoginBlockTime();
+					$("#blockSecs1").show();
+					$('#btnLogin').attr('disabled',true);
+				}
+			}
+		}
+
+		return; // 정상적으로 로그인 되지 않았으므로 return
+	}
+
+
+	// 구글 캡챠 콜백함수
+	var fnRecaptchaCallback = function () {
+		let token = $("#g-recaptcha-response").val();
+		let params = {};
+		params.token = token;
+		gagajf.ajaxSubmit('/verify/recaptcha','', fnVerifyReCaptchaCallback, params);
+	};
+
+	// Token 인증
+	var fnVerifyReCaptchaCallback = function (result) {
+		if (result.success) {
+			rechaptchCheck = true;
+		} else {
+			rechaptchCheck = false;
+		}
+	};
+
+	// 로그인블락시간(초) 설정
+	let fnSetLoginBlockTime = function() {
+		let blockSecs = Number($("#blockSecs2").html()) - 1;
+		$("#blockSecs2").html(blockSecs);
+		if (blockSecs <= 0) {
+			clearTimeout(fnSetLoginBlockTime);
+			$('#btnLogin').attr('disabled',false);
+		} else {
+			setTimeout(fnSetLoginBlockTime, 1000);
+		}
 	}
 
 	// SNS 로그인 콜백함수
@@ -266,60 +268,38 @@
 			return;
 		}
 		if (userInfo.custStat === 'DORMANT_CUST') {
-			cfnGoToPage(_PAGE_CUSTOMER_DORMANT);
+			mcxDialog.alertC("휴면 고객님께서는 휴면을<br>해지하신 후 사용하실 수 있습니다.", {
+				sureBtnText: "확인",
+				sureBtnClick: function() {
+					cfnGoToPage(_PAGE_CUSTOMER_DORMANT);
+				}
+			});
 		}
 		if (userInfo.custStat === 'FAIL_CUST') {
 			mcxDialog.alert("회원가입에 실패 했습니다.<br> 고객센터에 문의 하시기 바랍니다.");
 			return;
 		}
-		if (userInfo.custStat === 'NEW_CUST') {
+		if (userInfo.custStat === 'NEW_CUST') { // 회원가입
 			cfnGoToPage(_PAGE_CUSTOMER_JOIN_COMPLETE);
 		}
 
-		if (userInfo.custStat === 'NEED_AGREE_CUST'){
+		if (userInfo.custStat === 'NEED_AGREE_CUST') { // YES24 로그인 시 동의화면 필요
 			cfnConsentUseInfo(userInfo.custNm);
 		}
 
-
 		if (userInfo.custStat === 'SUCC_CUST') {
 			let params = {};
 			params.snsType = userInfo.snsType;
 			params.snsId = [[${snsLoginPrefix}]] + userInfo.snsId;
 			$.post(_frontUrl + '/login'
 				, $.param(params)
-				, function(result) {
-					fnReloadAfterLogin(result);
-				}
+				,fnReloadAfterLogin
 				, "json");
 		}
-
-
 	};
 
-	var fnReloadAfterLogin = function(result) {
-		if (result.status === 'OK') {
-			document.location.href = result.returnUrl;
-		} else if (result.status === 'EMAIL_DUP') {
-
-		} else if(result.status === 'DORMANT_CUST') {
-			cfnGoToPage(_PAGE_CUSTOMER_DORMANT);
-		} else if(result.status === 'SECEDE_CUST') {
-
-		} else {
-			//cfnGoToPage(_PAGE_CUSTOMER_JOIN_CERTIFY_SNS);
-		}
-	};
-
-	//엔터키 확인
-	$('#loginForm input[name=passwd]').keypress(function (event) {
-		if (event.which === 13) {
-			event.preventDefault();
-			fnValidConfirm();
-		}
-	});
-
-
 	$(document).ready(function() {
+
 		$('#loginForm input[name=loginId]').val(gagajf.getCookie(ckLoginId));
 		if (gagajf.isNull($('#loginForm input[name=loginId]').val())) {
 			$('#loginForm input[name=loginId]').focus();
@@ -331,7 +311,6 @@
 	});
 /*]]>*/
 </script>
-
 </th:block>
 
 </body>

+ 1 - 0
src/main/webapp/WEB-INF/views/web/common/fragments/HeadWeb.html

@@ -71,6 +71,7 @@
 	<script src="/ux/plugins/mcxdialog/mcxdialog_ui.js"></script>
 	
 	<script type="text/javascript" src="https://ssl.daumcdn.net/dmaps/map_js_init/postcode.v2.js"></script>
+	<script src="https://www.google.com/recaptcha/api.js" async defer></script>
 
 	<!-- Global site tag (gtag.js) - Google Analytics -->
 <!-- 	<script async src="https://www.googletagmanager.com/gtag/js?id=UA-168660512-1"></script> -->