Forráskód Böngészése

일반로그인, 로그아웃 기능 추가

gagamel 5 éve
szülő
commit
87e2d5c00a
19 módosított fájl, 439 hozzáadás és 155 törlés
  1. 10 10
      src/main/java/com/style24/front/biz/dao/TsfLoginDao.java
  2. 20 17
      src/main/java/com/style24/front/biz/service/TsfLoginService.java
  3. 3 3
      src/main/java/com/style24/front/biz/web/TsfIndexController.java
  4. 1 1
      src/main/java/com/style24/front/support/config/TsfWebMvcConfig.java
  5. 22 0
      src/main/java/com/style24/front/support/exception/TsfUsernameNotFoundException.java
  6. 21 8
      src/main/java/com/style24/front/support/security/TsfAuthenticationProvider.java
  7. 1 1
      src/main/java/com/style24/front/support/security/config/TsfSecurityConfig.java
  8. 2 2
      src/main/java/com/style24/front/support/security/filter/TsfAuthenticationFilter.java
  9. 8 8
      src/main/java/com/style24/front/support/security/handler/TsfLoginFailureHandler.java
  10. 6 6
      src/main/java/com/style24/front/support/security/handler/TsfLoginSuccessHandler.java
  11. 4 4
      src/main/java/com/style24/front/support/security/handler/TsfRememberMeSuccessHandler.java
  12. 2 1
      src/main/java/com/style24/front/support/startup/TsfApplication.java
  13. 88 86
      src/main/java/com/style24/persistence/mybatis/shop/TsfLogin.xml
  14. 2 2
      src/main/resources/config/application.yml
  15. 234 0
      src/main/webapp/WEB-INF/views/web/SigninFormWeb.html
  16. 2 2
      src/main/webapp/WEB-INF/views/web/common/error/500Web.html
  17. 3 2
      src/main/webapp/WEB-INF/views/web/common/fragments/GnbWeb.html
  18. 1 0
      src/main/webapp/WEB-INF/views/web/common/fragments/HeadWeb.html
  19. 9 2
      src/main/webapp/ux/style24_link.js

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

@@ -15,29 +15,29 @@ public interface TsfLoginDao {
 
 	/**
 	 * 로그인체크정보 조회
-	 * @param customer - 고객정보
-	 * @return 고객정보
+	 * @param login - 로그인 정보
+	 * @return
 	 * @author gagamel
 	 * @date 2020. 2. 3
 	 */
-	Login getLoginCheckInfo(Login customer);
+	Login getLoginCheckInfo(Login login);
 
 	/**
 	 * 로그인실패 남기기
-	 * @param customer - 고객정보
+	 * @param login - 로그인 정보
 	 * @author gagamel
 	 * @date 2020. 2. 3
 	 */
-	void createLoginFail(Login customer);
+	void createLoginFail(Login login);
 
 	/**
 	 * 로그인 실패건수 조회
-	 * @param customer - 고객정보
+	 * @param login - 로그인 정보
 	 * @return 로그인 실패건수
 	 * @author gagamel
 	 * @date 2020. 2. 3
 	 */
-	int getLoginFailCount(Login customer);
+	int getLoginFailCount(Login login);
 
 	/**
 	 * 최종로그인일시 Update
@@ -49,11 +49,11 @@ public interface TsfLoginDao {
 
 	/**
 	 * 로그인이력 남기기
-	 * @param customer - 고객정보
+	 * @param login - 로그인 정보
 	 * @author gagamel
 	 * @date 2020. 2. 3
 	 */
-	void createLoginHistory(Login customer);
+	void createLoginHistory(Login login);
 
 //	/**
 //	 * 로그인 세션정보 조회
@@ -62,7 +62,7 @@ public interface TsfLoginDao {
 //	 * @author gagamel
 //	 * @date 2020. 2. 3
 //	 */
-//	FoLogin getLoginSessionInfo(FoLogin customer);
+//	Login getLoginSessionInfo(FoLogin customer);
 
 	/**
 	 * 로그인유지토큰 생성

+ 20 - 17
src/main/java/com/style24/front/biz/service/TsfLoginService.java

@@ -29,15 +29,15 @@ public class TsfLoginService {
 
 	/**
 	 * 로그인체크정보 조회
-	 * @param customer - 고객정보
-	 * @return 고객정보
+	 * @param login - 로그인 정보
+	 * @return
 	 * @author gagamel
 	 * @since 2020. 2. 3
 	 */
-	public Login getLoginCheckInfo(Login customer) {
-		customer.setIpAddr(TsfSession.getIpAddress());
-		customer.setSiteCd(TscConstants.Site.STYLE24.value());
-		return loginDao.getLoginCheckInfo(customer);
+	public Login getLoginCheckInfo(Login login) {
+		login.setIpAddr(TsfSession.getIpAddress());
+		login.setSiteCd(TscConstants.Site.STYLE24.value());
+		return loginDao.getLoginCheckInfo(login);
 	}
 
 	/**
@@ -70,20 +70,20 @@ public class TsfLoginService {
 
 	/**
 	 * 로그인실패 남기기. 로그인실패여부가 "N:성공"이면 실패건수 0으로 초기화
-	 * @param custId - 고객ID
+	 * @param custNo - 고객번호
 	 * @param loginFailYn - 로그인실패여부(Y:실패, N:성공)
 	 * @author gagamel
 	 * @since 2020. 2. 3
 	 */
 	@Transactional("shopTxnManager")
-	public void createLoginFail(String custId, String loginFailYn) {
-		Login customer = new Login();
-		customer.setCustId(custId);
-		customer.setIpAddr(TsfSession.getIpAddress());
-		customer.setSiteCd(TscConstants.Site.STYLE24.value());
-		customer.setLoginFailYn(loginFailYn);
-
-		loginDao.createLoginFail(customer);
+	public void createLoginFail(Integer custNo, String loginFailYn) {
+		Login login = new Login();
+		login.setCustNo(custNo);
+		login.setIpAddr(TsfSession.getIpAddress());
+		login.setSiteCd(TscConstants.Site.STYLE24.value());
+		login.setLoginFailYn(loginFailYn);
+
+		loginDao.createLoginFail(login);
 	}
 
 	/**
@@ -105,7 +105,7 @@ public class TsfLoginService {
 	/**
 	 * 로그인유지토큰 생성
 	 * @param custNo - 고객번호
-	 * @param password - 비밀번호
+	 * @param remembermeToken - Rememberme토큰
 	 * @param expiry - 만료기간
 	 * @author gagamel
 	 * @since 2020. 6. 17
@@ -134,7 +134,10 @@ public class TsfLoginService {
 
 	/**
 	 * 로그인유지토큰 갱신
-	 * @param token - 토큰 정보
+	 * @param custNo - 고객번호
+	 * @param prevRemembermeToken - 기존 Rememberme토큰
+	 * @param remembermeToken - Rememberme토큰
+	 * @param expiry - 만료기간
 	 * @author gagamel
 	 * @since 2020. 6. 17
 	 */

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

@@ -54,14 +54,14 @@ public class TsfIndexController extends TsfBaseController {
 
 		if (status != null) {
 			Integer statusCode = Integer.valueOf(status.toString());
-			log.debug("statusCode: {}", statusCode);
+			log.info("statusCode: {}", statusCode);
 
 			if (statusCode == GagaResponseStatus.NOT_FOUND.getCode()) {
 				mav.addObject("status", GagaResponseStatus.NOT_FOUND.getCode());
 
 				// 404 오류 시 잘못된 링크 유입 시 몰메인으로 이동 처리
 				String prevRequestUri = (String)request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
-				log.debug("prevRequestUri: {}", prevRequestUri);
+				log.info("prevRequestUri: {}", prevRequestUri);
 				if (prevRequestUri.startsWith("/m")) {
 					response.sendRedirect("/");
 				}
@@ -126,7 +126,7 @@ public class TsfIndexController extends TsfBaseController {
 		mav.addObject("front", env.getProperty("domain.front"));
 
 		// 로그인 페이지로
-		mav.setViewName(super.getDeviceViewName("Signin"));
+		mav.setViewName(super.getDeviceViewName("SigninForm"));
 
 		return mav;
 	}

+ 1 - 1
src/main/java/com/style24/front/support/config/TsfWebMvcConfig.java

@@ -59,7 +59,7 @@ public class TsfWebMvcConfig implements WebMvcConfigurer {
 	public void addInterceptors(InterceptorRegistry registry) {
 		final String[] excludePathPatterns = new String[] {
 			"/", "/index", "/signin/**",
-			"/image/**", "/ux/**",
+			"/images/**", "/ux/**",
 			"/error/**", "/data/**",
 			"/login", "/logout"
 		};

+ 22 - 0
src/main/java/com/style24/front/support/exception/TsfUsernameNotFoundException.java

@@ -0,0 +1,22 @@
+package com.style24.front.support.exception;
+
+import org.springframework.security.core.AuthenticationException;
+
+/**
+ * 로그인 정보가 없을 경우 발생시키는 예외
+ * 
+ * @author gagamel
+ * @since 2021. 2. 15
+ */
+@SuppressWarnings("serial")
+public class TsfUsernameNotFoundException extends AuthenticationException {
+
+	public TsfUsernameNotFoundException(String msg) {
+		super(msg);
+	}
+
+	public TsfUsernameNotFoundException(String msg, Throwable t) {
+		super(msg, t);
+	}
+
+}

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

@@ -11,7 +11,6 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.core.authority.SimpleGrantedAuthority;
-import org.springframework.security.core.userdetails.UsernameNotFoundException;
 import org.springframework.stereotype.Component;
 
 import com.style24.core.support.message.TscMessageByLocale;
@@ -21,6 +20,7 @@ import com.style24.front.support.exception.TsfDormantAccountException;
 import com.style24.front.support.exception.TsfEmailDuplicationException;
 import com.style24.front.support.exception.TsfLockedAccountException;
 import com.style24.front.support.exception.TsfSecedeAccountException;
+import com.style24.front.support.exception.TsfUsernameNotFoundException;
 import com.style24.persistence.domain.Login;
 
 import lombok.extern.slf4j.Slf4j;
@@ -69,42 +69,55 @@ public class TsfAuthenticationProvider implements AuthenticationProvider {
 
 		// 로그인정보 조회
 		Login loginInfo = loginService.getLoginCheckInfo(loginParam);
-		log.debug("loginInfo: {}", loginInfo);
+		log.info("loginInfo: {}", loginInfo);
 
 		if (loginInfo == null) {
-			log.debug(String.format("Customer with ID=%s was not found!", loginId));
-			throw new UsernameNotFoundException(message.getMessage("LOGN_0001"));
+			log.info(String.format("Customer with ID=%s was not found!", loginId));
+			throw new TsfUsernameNotFoundException(message.getMessage("LOGN_0001"));
 		}
 
 		// 로그인 실패누적건수가 5이면
 		if (loginInfo.getLoginFailCnt() == 5) {
+			// 로그인 실패 남기기
+			loginService.createLoginFail(loginInfo.getCustNo(), "Y");
+
 //			throw new BadCredentialsException(message.getMessage("LOGN_0005"));
 			throw new TsfLockedAccountException(message.getMessage("LOGN_0005"));
 		}
 
 		/// SNS로그인이 아닌 일반로그인 이면
 		if (!loginId.startsWith(TsfConstants.SNSLOGIN_PREFIX)) {
-			log.debug("encoded pwd: {}", passwordEncoder.encode(passwd));
+			log.info("encoded password: {}", passwordEncoder.encode(passwd));
 			boolean isMatch = passwordEncoder.matches(passwd, loginInfo.getPasswd());
-			log.debug("isMatch: {}", isMatch);
+			log.info("Password is match?: {}", isMatch);
 
 			if (!isMatch) {
-//				// 로그인 실패 남기기
-//				customerService.createLoginFail(loginInfo.getCustId(), "Y");
+				// 로그인 실패 남기기
+				loginService.createLoginFail(loginInfo.getCustNo(), "Y");
+
 				throw new BadCredentialsException(message.getMessage("LOGN_0002"));
 			}
 		} else {
 			// SNS로그인 시 로그인ID 값은 이메일로 처리했으므로
 			// loginId 값과 회원정보의 이메일 값을 비교해서 동일하면
 			if (StringUtils.isBlank(loginInfo.getSnsType()) && passwd.equals(loginInfo.getEmail())) {
+				// 로그인 실패 남기기
+				loginService.createLoginFail(loginInfo.getCustNo(), "Y");
+
 				throw new TsfEmailDuplicationException(message.getMessage("LOGN_0008"));
 			}
 		}
 
 		if (loginInfo.getCustStat().equals("20")) { // 휴면회원
+			// 로그인 실패 남기기
+			loginService.createLoginFail(loginInfo.getCustNo(), "Y");
+
 //			WfoSession.setDormantMemberNo(request, loginInfo.getCustId());
 			throw new TsfDormantAccountException(message.getMessage("LOGN_0006"));
 		} else if (loginInfo.getCustStat().equals("30")) { // 탈퇴회원
+			// 로그인 실패 남기기
+			loginService.createLoginFail(loginInfo.getCustNo(), "Y");
+
 			throw new TsfSecedeAccountException(message.getMessage("LOGN_0007"));
 		}
 

+ 1 - 1
src/main/java/com/style24/front/support/security/config/TsfSecurityConfig.java

@@ -32,7 +32,7 @@ public class TsfSecurityConfig extends WebSecurityConfigurerAdapter {
 
 	private static final String[] UNAUTHORIZED_RESOURCE_LIST = new String[] {"/*", "/index", "/signin/**"};
 
-	private static final String[] UNSECURED_RESOURCE_LIST = new String[] {"/image/**", "/ux/**", "/data/**"};
+	private static final String[] UNSECURED_RESOURCE_LIST = new String[] {"/images/**", "/ux/**", "/data/**"};
 
 	@Autowired
 	private TsfAuthenticationProvider authenticationProvider;

+ 2 - 2
src/main/java/com/style24/front/support/security/filter/TsfAuthenticationFilter.java

@@ -35,7 +35,7 @@ public class TsfAuthenticationFilter extends UsernamePasswordAuthenticationFilte
 			// 로그인ID: SNS_TYPE-SNS_JOIN_ID
 			// 비밀번호: 이메일
 			loginParams.setString("loginId", loginParams.getString("snsType") + "-" + loginParams.getString("snsJoinId"));
-			loginParams.setString("passwd", loginParams.getString("email")); // SNS로그인 시 불필요해서 이메일체크를 위해 추가
+			loginParams.setString("passwd", loginParams.getString("email")); // SNS로그인 시 이메일체크를 위해 추가
 		} else { // 일반로그인
 			if (StringUtils.isNotBlank(loginParams.getString("loginId"))) {
 				loginParams.setString("loginId", loginParams.getString("loginId").trim());
@@ -46,7 +46,7 @@ public class TsfAuthenticationFilter extends UsernamePasswordAuthenticationFilte
 			}
 		}
 
-		log.debug("loginParams: {}", loginParams);
+		log.info("loginParams: {}", loginParams);
 
 		UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
 			loginParams.getString("loginId"), loginParams.getString("passwd"));

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

@@ -6,16 +6,16 @@ import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.springframework.beans.factory.annotation.Autowired;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.web.authentication.AuthenticationFailureHandler;
 
-import com.style24.front.biz.service.TsfLoginService;
 import com.style24.front.support.exception.TsfDormantAccountException;
 import com.style24.front.support.exception.TsfEmailDuplicationException;
 import com.style24.front.support.exception.TsfLockedAccountException;
 import com.style24.front.support.exception.TsfSecedeAccountException;
 import com.style24.front.support.exception.TsfSessionExpiredException;
+import com.style24.front.support.security.session.TsfSession;
 
 import lombok.extern.slf4j.Slf4j;
 
@@ -34,16 +34,10 @@ import com.gagaframework.web.util.GagaStringUtil;
 @Slf4j
 public class TsfLoginFailureHandler implements AuthenticationFailureHandler {
 
-	@Autowired
-	private TsfLoginService loginService;
-
 	@Override
 	public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
 		AuthenticationException exception) throws IOException, ServletException {
 
-		// 로그인 실패 남기기
-		loginService.createLoginFail(request.getParameter("custId"), "Y");
-
 		GagaMap result = new GagaMap();
 		result.setString("message", exception.getMessage());
 
@@ -61,6 +55,12 @@ public class TsfLoginFailureHandler implements AuthenticationFailureHandler {
 			result.setString("status", "ETC_ERROR");
 		}
 
+		// 로그인실패건수 세션에 저장
+		int loginFailCnt = (StringUtils.isNotBlank(TsfSession.getAttribute("loginFailCnt")) ? Integer.parseInt(TsfSession.getAttribute("loginFailCnt")) : 0) + 1;
+		log.info("loginFailCnt: {}", loginFailCnt);
+		result.setInt("loginFailCnt", loginFailCnt);
+		TsfSession.setAttribute("loginFailCnt", String.valueOf(loginFailCnt));
+
 		GagaStringUtil.write(response, result);
 	}
 

+ 6 - 6
src/main/java/com/style24/front/support/security/handler/TsfLoginSuccessHandler.java

@@ -41,18 +41,15 @@ public class TsfLoginSuccessHandler implements AuthenticationSuccessHandler {
 		"/customer/join/form",				// 회원가입
 		"/customer/sns/join/form",			// SNS 회원가입
 		"/customer/id/find/form",			// 아이디찾기
-		"/customer/id/find/result/form",	// 아이디찾기
+		"/customer/id/find/result/form",	// 아이디찾기완료
 		"/customer/pwd/find/form",			// 비밀번호찾기
-		"/customer/pwd/find/result/form",	// 비밀번호완료
+		"/customer/pwd/find/result/form",	// 비밀번호찾기완료
 		"/customer/join/complete/form"		// 회원가입완료
 	};
 
 	@Autowired
 	private TsfLoginService loginService;
 
-//	@Autowired
-//	private FoCustomerService customerService;
-
 	@Override
 	public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
 		// 로그인 상세 정보
@@ -62,7 +59,7 @@ public class TsfLoginSuccessHandler implements AuthenticationSuccessHandler {
 		Integer custNo = loginDetails.getLoginInfo().getCustNo();
 		int failCnt = loginService.getLoginFailCount(custNo);
 		if (failCnt > 0) {
-			loginService.createLoginFail(loginDetails.getLoginInfo().getCustId(), "N");
+			loginService.createLoginFail(loginDetails.getLoginInfo().getCustNo(), "N");
 		}
 
 		// 최종로그인일시 Update
@@ -125,6 +122,9 @@ public class TsfLoginSuccessHandler implements AuthenticationSuccessHandler {
 		HttpSession session = request.getSession(true);
 		session.setMaxInactiveInterval(1800);
 		session.setAttribute("session", loginDetails);
+
+		// 세션에 저장된 로그인실패건수 제거
+		session.removeAttribute("loginFailCnt");
 	}
 
 }

+ 4 - 4
src/main/java/com/style24/front/support/security/handler/TsfRememberMeSuccessHandler.java

@@ -29,9 +29,6 @@ public class TsfRememberMeSuccessHandler implements AuthenticationSuccessHandler
 	@Autowired
 	private TsfLoginService loginService;
 
-//	@Autowired
-//	private FoCustomerService customerService;
-
 	@Override
 	public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
 		// 로그인 상세 정보
@@ -41,7 +38,7 @@ public class TsfRememberMeSuccessHandler implements AuthenticationSuccessHandler
 		Integer custNo = loginDetails.getLoginInfo().getCustNo();
 		int failCnt = loginService.getLoginFailCount(custNo);
 		if (failCnt > 0) {
-			loginService.createLoginFail(loginDetails.getLoginInfo().getCustId(), "N");
+			loginService.createLoginFail(loginDetails.getLoginInfo().getCustNo(), "N");
 		}
 
 		// 최종로그인일시 Update
@@ -77,6 +74,9 @@ public class TsfRememberMeSuccessHandler implements AuthenticationSuccessHandler
 		HttpSession session = request.getSession(true);
 		session.setMaxInactiveInterval(1800);
 		session.setAttribute("session", loginDetails);
+
+		// 세션에 저장된 로그인실패건수 제거
+		session.removeAttribute("loginFailCnt");
 	}
 
 }

+ 2 - 1
src/main/java/com/style24/front/support/startup/TsfApplication.java

@@ -10,6 +10,7 @@ import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
 import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
 import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
+import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;
 import org.springframework.cache.annotation.EnableCaching;
 import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
@@ -28,7 +29,7 @@ import com.gagaframework.web.core.GagaConstants;
  */
 @EnableCaching
 @Configuration
-@EnableAutoConfiguration(exclude = {DataSourceTransactionManagerAutoConfiguration.class, DataSourceAutoConfiguration.class})
+@EnableAutoConfiguration(exclude = {DataSourceTransactionManagerAutoConfiguration.class, DataSourceAutoConfiguration.class, ErrorMvcAutoConfiguration.class})
 @ComponentScan(basePackages = {GagaConstants.GAGA_PACKAGE, TscConstants.BASE_PACKAGE})
 @Slf4j
 public class TsfApplication {

+ 88 - 86
src/main/java/com/style24/persistence/mybatis/shop/TsfLogin.xml

@@ -5,31 +5,37 @@
 	<!-- 로그인체크 정보 조회 -->
 	<select id="getLoginCheckInfo" parameterType="Login" resultType="Login">
 		/* TsfLogin.getLoginCheckInfo */
-		SELECT CUST_NO                                      --고객번호
-		     , CUST_ID                                      --고객ID
-		     , CUST_NM                                      --고객명
-		     , PASSWD                                       --비밀번호
-		     , CUST_GB                                      --고객구분
-		     , FN_GET_CODE_NM('G100',CUST_GB) AS CUST_GB_NM --고객구분명
-		     , CUST_STAT                                    --회원상태
-		     , EMAIL                                        --이메일
-		     , SNS_TYPE                                     --SNS유형
-		     , SNS_JOIN_ID                                  --SNS가입ID
-		     , NVL((SELECT LOGIN_FAIL_CNT
-		            FROM   TB_LOGIN_FAIL
-		            WHERE  LOGIN_ID = TO_CHAR(A.CUST_NO)
-		            AND    IP_ADDR = #{ipAddr}
-		            AND    SITE_CD = #{siteCd}
-		           ),0)    AS LOGIN_FAIL_CNT                --로그인실패건수
+		SELECT CUST_NO                                          /*고객번호*/
+		     , CUST_ID                                          /*고객ID*/
+		     , CUST_NM                                          /*고객명*/
+		     , PASSWD                                           /*비밀번호*/
+		     , CUST_GB                                          /*고객구분*/
+		     , FN_GET_CODE_NM('G100',CUST_GB) AS CUST_GB_NM     /*고객구분명*/
+		     , CUST_STAT                                        /*회원상태*/
+		     , EMAIL                                            /*이메일*/
+		     , #{snsType}                     AS SNS_TYPE       /*SNS유형*/
+		     , CASE WHEN #{snsType} = 'NV' THEN NV_JOIN_ID
+		            WHEN #{snsType} = 'KK' THEN KK_JOIN_ID
+		            WHEN #{snsType} = 'YS' THEN YS_JOIN_ID
+		            ELSE ''
+		       END                            AS SNS_JOIN_ID    /*SNS가입ID*/
+		     , IFNULL((SELECT LOGIN_FAIL_CNT
+		               FROM   TB_LOGIN_FAIL
+		               WHERE  CUST_NO = A.CUST_NO
+		               AND    IP_ADDR = #{ipAddr}
+		               AND    SITE_CD = #{siteCd}
+		              ),0)                    AS LOGIN_FAIL_CNT /*로그인실패건수*/
 		FROM   TB_CUSTOMER A
-		WHERE  CUST_STAT <![CDATA[<>]]> '40'                --전환대상고객제외
+		WHERE  1 = 1
 		<choose>
-		    <when test="snsType != null and snsType != ''"> <!-- SNS로그인 -->
-		AND    (
-		        SNS_TYPE||'-'||SNS_JOIN_ID = #{custId}
-		        OR
-		        EMAIL = #{email}
-		       )
+		    <when test="snsType == 'NV'"> <!-- SNS로그인:네이버 -->
+		AND    NV_JOIN_ID = #{custId}
+		    </when>
+		    <when test="snsType == 'KK'"> <!-- SNS로그인:카카오 -->
+		AND    KK_JOIN_ID = #{custId}
+		    </when>
+		    <when test="snsType == 'YS'"> <!-- SNS로그인:YES24 -->
+		AND    YS_JOIN_ID = #{custId}
 		    </when>
 		    <otherwise> <!-- 일반로그인 -->
 		AND    CUST_ID = #{custId}
@@ -40,36 +46,41 @@
 	<!-- 로그인실패 남기기 -->
 	<insert id="createLoginFail" parameterType="Login">
 		/* TsfLogin.createLoginFail */
-		MERGE INTO TB_LOGIN_FAIL
-		USING (
-		       SELECT TO_CHAR(CUST_NO) AS CUST_NO
-		       FROM   TB_CUSTOMER
-		       WHERE  CUST_ID = #{custId}
-		      ) B
-		ON    (
-		       LOGIN_ID = B.CUST_NO
-		AND    IP_ADDR = #{ipAddr}
-		AND    SITE_CD = #{siteCd}
-		      )
-		WHEN MATCHED THEN
-		    UPDATE
-		    SET    LOGIN_FAIL_CNT = DECODE(#{loginFailYn},'Y',LOGIN_FAIL_CNT + 1,0)
-		         , UPD_NO = B.CUST_NO
-		         , UPD_DT = NOW()
-		WHEN NOT MATCHED THEN
-		    INSERT (LOGIN_ID, IP_ADDR, SITE_CD, LOGIN_FAIL_CNT, REG_ID, REG_DT, UPD_NO, UPD_DT)
-		    VALUES (B.CUST_NO, #{ipAddr}, #{siteCd}, 1, B.CUST_NO, NOW(), B.CUST_NO, NOW())
+		INSERT INTO TB_LOGIN_FAIL (
+		       CUST_NO
+		     , IP_ADDR
+		     , SITE_CD
+		     , LOGIN_FAIL_CNT
+		     , REG_NO
+		     , REG_DT
+		     , UPD_NO
+		     , UPD_DT
+		)
+		VALUES (
+		       #{custNo}
+		     , #{ipAddr}
+		     , #{siteCd}
+		     , 1
+		     , #{custNo}
+		     , NOW()
+		     , #{custNo}
+		     , NOW()
+		)
+		ON DUPLICATE KEY UPDATE
+		       LOGIN_FAIL_CNT = CASE WHEN #{loginFailYn} = 'Y' THEN LOGIN_FAIL_CNT + 1 ELSE 0 END
+		     , UPD_NO = #{custNo}
+		     , UPD_DT = NOW()
 	</insert>
 
 	<!-- 로그인 실패건수 조회 -->
 	<select id="getLoginFailCount" parameterType="Login" resultType="int">
 		/* TsfLogin.getLoginFailCount */
-		SELECT NVL((SELECT LOGIN_FAIL_CNT
-		            FROM   TB_LOGIN_FAIL
-		            WHERE  LOGIN_ID = #{custNo}
-		            AND    IP_ADDR = #{ipAddr}
-		            AND    SITE_CD = #{siteCd}
-		           ),0)
+		SELECT IFNULL((SELECT LOGIN_FAIL_CNT
+		               FROM   TB_LOGIN_FAIL
+		               WHERE  CUST_NO = #{custNo}
+		               AND    IP_ADDR = #{ipAddr}
+		               AND    SITE_CD = #{siteCd}
+		              ),0) AS CNT
 		FROM   DUAL
 	</select>
 
@@ -88,16 +99,16 @@
 		/* TsfLogin.createLoginHistory */
 		INSERT INTO TB_LOGIN_HST (
 		       LOGIN_HST_SQ
-		     , LOGIN_ID
+		     , CUST_NO
 		     , IP_ADDR
 		     , SITE_CD
 		     , FRONT_GB
 		     , LOGIN_DT
-		     , REG_ID
+		     , REG_NO
 		     , REG_DT
 		)
 		VALUES (
-		       SEQ_LOGIN_HST.NEXTVAL
+		       NULL
 		     , #{custNo}
 		     , #{ipAddr}
 		     , #{siteCd}
@@ -111,51 +122,42 @@
 	<!-- 로그인 세션정보 조회 -->
 	<select id="getLoginSessionInfo" parameterType="Login" resultType="Login">
 		/* TsfLogin.getLoginSessionInfo */
-		SELECT CUST_NO                                                --고객번호
-		     , CUST_ID                                                --고객ID
-		     , CUST_NM                                                --고객명
-		     , PASSWD                                                 --비밀번호
-		     , CELL_PHNNO                                             --휴대전화번호
-		     , EMAIL                                                  --이메일
-		     , CUST_GB                                                --회원구분코드
-		     , FN_GET_CODE_NM('G100',CUST_GB)        AS CUST_GB_NM    --회원구분명
-		     , CUST_GRADE                                             --회원등급코드
-		     , FN_GET_CODE_NM('G110',CUST_GRADE)     AS CUST_GRADE_NM --회원등급명
-		     , SITE_CD                                                --사이트코드
-		     , TO_CHAR(LOGIN_LDT,'YYYYMMDDHH24MISS') AS LOGIN_LDT     --최종로그인일시
-		     , MANAGED_RSN                                            --관리대상지정사유(공통코드G120)
+		SELECT CUST_NO                                                /*고객번호*/
+		     , CUST_ID                                                /*고객ID*/
+		     , CUST_NM                                                /*고객명*/
+		     , PASSWD                                                 /*비밀번호*/
+		     , CELL_PHNNO                                             /*휴대전화번호*/
+		     , EMAIL                                                  /*이메일*/
+		     , CUST_GB                                                /*회원구분코드*/
+		     , FN_GET_CODE_NM('G100',CUST_GB)        AS CUST_GB_NM    /*회원구분명*/
+		     , CUST_GRADE                                             /*회원등급코드*/
+		     , FN_GET_CODE_NM('G110',CUST_GRADE)     AS CUST_GRADE_NM /*회원등급명*/
+		     , SITE_CD                                                /*사이트코드*/
+		     , TO_CHAR(LOGIN_LDT,'YYYYMMDDHH24MISS') AS LOGIN_LDT     /*최종로그인일시*/
+		     , MANAGED_RSN                                            /*관리대상지정사유(공통코드G120)*/
 		     , CASE WHEN ADD_MONTHS(PASSWD_CHG_DT,6) <![CDATA[<]]> NOW() THEN 'Y'
 		            ELSE 'N'
-		       END                                   AS PASSWD_CHG_YN --비밀번호변경도래여부
+		       END                                   AS PASSWD_CHG_YN /*비밀번호변경도래여부*/
 		FROM   TB_CUSTOMER A
 		WHERE  CUST_ID = #{custId}
-		AND    CUST_STAT = '10' --활동회원
+		AND    CUST_STAT = 'G104_10' /*활동회원*/
 	</select>
 
 	<!-- 로그인유지토큰 생성 -->
 	<insert id="createPersistentToken" parameterType="PersistentToken">
 		/* TsfLogin.createPersistentToken */
-		MERGE INTO TB_PERSISTENT_TOKEN
-		USING DUAL
-		ON    (
-		       CUST_NO = #{custNo}
-		       AND
-		       REMEMBERME_TOKEN = #{remembermeToken}
+		INSERT INTO TB_PERSISTENT_TOKEN (
+		       CUST_NO
+		     , REMEMBERME_TOKEN
+		     , LIMIT_DT
+		)
+		VALUES (
+		       #{custNo}
+		     , #{remembermeToken}
+		     , NOW()
 		)
-		WHEN MATCHED THEN
-		    UPDATE
-		    SET    LIMIT_DT = #{limitDt}
-		WHEN NOT MATCHED THEN
-		    INSERT (
-		            CUST_NO
-		          , REMEMBERME_TOKEN
-		          , LIMIT_DT
-		    )
-		    VALUES (
-		           #{custNo}
-		         , #{remembermeToken}
-		         , NOW()
-		    )
+		ON DUPLICATE KEY UPDATE
+		       LIMIT_DT = #{limitDt}
 	</insert>
 
 	<!-- 로그인유지토큰 조회 -->

+ 2 - 2
src/main/resources/config/application.yml

@@ -17,13 +17,13 @@ spring:
             repositories:
                 enabled: true
 
-server:
+#server:
 #    servlet:
 #        session:
 #            cookie:
 #                name: WSESSIONID
 #                secure: true
-    error.whitelabel.enabled: false
+#    error.whitelabel.enabled: false
 
 # 본인인증
 certify:

+ 234 - 0
src/main/webapp/WEB-INF/views/web/SigninFormWeb.html

@@ -0,0 +1,234 @@
+<!DOCTYPE html>
+<html lang="ko"
+	xmlns:th="http://www.thymeleaf.org"
+	xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
+	layout:decorator="web/common/layout/DefaultLayoutWeb">
+<!--
+ *******************************************************************************
+ * @source  : SigninFormWeb.html
+ * @desc    : 로그인 Page
+ *============================================================================
+ * STYLE24
+ * Copyright(C) 2020 TSIT, All rights reserved.
+ *============================================================================
+ * VER  DATE         AUTHOR      DESCRIPTION
+ * ===  ===========  ==========  =============================================
+ * 1.0  2021.02.15   gagamel     최초 작성
+ *******************************************************************************
+ -->
+<body>
+
+<th:block layout:fragment="content">
+<div id="container" class="container mb">
+	<div class="wrap">
+			<div class="content login"> <!-- 페이지특정 클래스 = login -->
+				<div class="cont_head">
+					<h3>style24</h3>
+				</div>
+				<div class="cont_body">
+					<form class="form_wrap form_col_c form_full" role="form" name="loginForm" id="loginForm" th:action="@{/login}" method="post">
+						<div class="form_head">
+							<h4>로그인</h4>
+						</div> 
+						<div class="form_field mt0">
+							<label class="input_label sr-only">아이디</label>
+							<div class="ui_col_12">
+								<div class="input_wrap"> 
+									<input type="text" name="loginId" placeholder="아이디" class="form_control" minlength="4" maxlength="20" required="required" data-valid-name="아이디"/>
+								</div>
+							</div>
+						</div>
+						<div class="form_field">
+							<label class="input_label sr-only">비밀번호</label>
+							<div class="ui_col_12">
+								<div class="input_wrap"> 
+									<input type="password" name="passwd" class="form_control" placeholder="비밀번호 8자 ~ 20자 입력" maxlength="20" required="required" data-valid-name="비밀번호"/>
+								</div>
+							</div>
+						</div>
+						<div class="login_check">
+							<div class="form_field">
+								<input type="checkbox" id="chkSaveId"/><label for="chkSaveId"> <span>아이디 저장</span> </label>
+							</div>
+							<div class="btn_wrap">
+								<ul>
+									<li><a href="javascript:void(0);" onclick="cfnGoToPage(_PAGE_CUSTOMER_ID_FIND);">아이디 찾기</a></li>
+									<li><a href="javascript:void(0);" onclick="cfnGoToPage(_PAGE_CUSTOMER_PWD_FIND);">비밀번호 찾기</a></li>
+								</ul>
+							</div>
+						</div>
+						<!-- case (회원 아이디 또는 비밀번호가 일치하지 않을때,보안문자 입력시) -->
+						<div class="help_block">
+							<!-- 회원 아이디 또는 비밀번호가 일치하지 않을때 -->
+							<p class="t_err t_err_login_fail" style="display: none;">
+								<span id="not_exist">회원 아이디 또는 비밀번호가 일치하지 않습니다.</span><br/>
+								(10회 이상 실패하면 180초 동안 로그인이 불가능 합니다.)<br/>
+								<span id="login_fail_cnt">4</span>회 실패 / 잔여 : <span id="login_remain_cnt">6</span>회 / <span>180</span>초 남음
+							</p>
+							<!-- //회원 아이디 또는 비밀번호가 일치하지 않을때 -->
+							<!-- 보안문자 입력시 -->
+							<div class="captcha mt40" style="display: none;"> <!-- 캡차영역 -->
+								<ul>
+									<li class="captcha_box"> <!-- 캡차이미지 -->
+									</li>
+									<li class="captcha_btn_dual">
+										<button type="button" id="play_audio">새로고침</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>
+							<p class="t_err t_err_captcha" style="display: none;">
+								보안문자 입력을 다시 시도해 주세요.<br/>
+								(10회 이상 실패하면 300초 동안 로그인이 불가능 합니다.)<br/>
+								<span>10</span>회 실패 / 잔여 : <span>0</span>회 / <span>180</span>초 남음
+							</p>
+							<!-- //보안문자 입력시 -->
+						</div>
+						<!-- //case (회원 아이디 또는 비밀번호가 일치하지 않을때,보안문자 입력시) -->
+						<div class="ui_row mt40">
+							<div class="ui_col_12">
+<!-- 								<button class="btn btn_dark btn_block" onclick="fnLogin();"><span>로그인</span></button> -->
+								<a class="btn btn_dark btn_block" onclick="fnLogin();"><span>로그인</span></a>
+							</div>
+						</div>
+						<div class="sns_wrap">
+							<h5 class="sr-only">간편로그인</h5>
+							<ul>
+								<li>
+									<a href="javascript:void(0)">
+										<i class="ico ico_snslogin kakao"></i>
+										<span>카카오로 시작하기</span>
+									</a>
+								</li>
+								<li>
+									<a href="javascript:void(0)">
+										<i class="ico ico_snslogin naver"></i>
+										<span>네이버로 시작하기</span>
+									</a>
+								</li>
+								<li>
+									<a href="javascript:void(0)">
+										<i class="ico ico_snslogin yes24"></i>
+										<span>YES24로 시작하기</span>
+									</a>
+								</li>
+							</ul>
+						</div>
+						<div class="t_c mt30">
+							<button type="button" class="btn_nonMb"><span>비회원 주문조회</span></button>
+						</div>
+					</form>
+				</div>
+			</div>
+		</div>
+</div>
+
+<script th:inline="javascript">
+/*<![CDATA[*/
+	let ckLoginId = "ckLoginId";
+
+	// 로그인
+	var fnLogin = function() {
+		if (!gagajf.validation($('#loginForm'))) {
+			return;
+		}
+
+		let params = new Object();
+		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 (!gagajf.isNull(result.message)) {
+						$("#not_exist").html(result.message);
+						$("#login_fail_cnt").html(result.loginFailCnt);
+						$("#login_remain_cnt").html(10 - Number(result.loginFailCnt));
+						$(".t_err_login_fail").show();
+					}
+					
+					if (result.status == 'PWD_5WRONG') {
+						// 비밀번호 5회 이상 틀린 경우
+					} else if (result.status == 'DORMANT_CUST') {
+						// 휴면회원
+						//cfnGoToPage(_PAGE_DORMANT_GUIDE);
+					} else if (result.status == 'SECEDE_CUST') {
+						// 탈퇴회원
+					} else if (result.status == 'SESSION_EXPIRED') {
+						// 세션만료
+					}
+					
+					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;
+// 					}
+// 				}
+			}
+			, 'json');
+	}
+	
+	// Save ID
+	$('#chkSaveId').on('click', function() {
+		if ($(this).is(":checked")) {
+			if (!gagajf.isNull($('#loginForm input[name=loginId]').val())) {
+				gagajf.setCookie(ckLoginId, $('#loginForm input[name=loginId]').val(), 1);
+			}
+		} else {
+			if (!gagajf.isNull(gagajf.getCookie(ckLoginId)) && (gagajf.getCookie(ckLoginId) === $('#loginForm input[name=loginId]').val())) {
+				gagajf.setCookie(ckLoginId, $('#loginForm input[name=loginId]').val(), -1);
+			}
+		}
+	});
+	
+	// 카카오 로그인
+	var fnLoginKakao = function() {
+		document.location.href = _frontUrl + '/signin/kakologin';
+	};
+	
+	// 네이버 로그인
+	var fnLoginNaver = function() {
+		document.location.href = _frontUrl + '/signin/naverlogin';
+	};
+	
+	// YES24 로그인
+	var fnLoginYes24 = function() {
+		document.location.href = _frontUrl + '/signin/yes24login';
+	};
+
+	$(document).ready(function() {
+		$('#loginForm input[name=loginId]').val(gagajf.getCookie(ckLoginId));
+		if (gagajf.isNull($('#loginForm input[name=loginId]').val())) {
+			$('#loginForm input[name=loginId]').focus();
+			$('#chkSaveId').prop('checked', false);
+		} else {
+			$('#loginForm input[name=passwd]').focus();
+			$('#chkSaveId').prop('checked', true);
+		}
+	});
+/*]]>*/
+</script>
+
+</th:block>
+
+</body>
+</html>

+ 2 - 2
src/main/webapp/WEB-INF/views/web/common/error/500Web.html

@@ -23,13 +23,13 @@
 <body>
 
 <th:block th:fragment="content">
-	<div id="wrapper" layout:fragment="content">
+	<div id="wrapper">
 		<ul class="msgWrap">
 			<li class="title">죄송합니다. 서비스 이용이 원활하지 않습니다.</li>
 			<li class="cont" th:if="${message == null}">요청하신 페이지에 에러가 발생하였습니다. 서비스 이용에 불편을 끼쳐드려 죄송합니다.</li>
 			<li class="cont" th:if="${message != null && message != '' && message != 'null'}" th:text="${message}"></li>
 			<li class="button">
-				<button type="button" class="btn big white" onclick="cfnGoToPage(_PAGE_MAIN);">위비스몰</button>
+				<button type="button" class="btn big white" onclick="cfnGoToPage(_PAGE_MAIN);">스타일24몰</button>
 				<button type="button" class="btn big black marL10"  onclick="history.back(-1); return false;">이전페이지</button>
 			</li>
 		</ul>

+ 3 - 2
src/main/webapp/WEB-INF/views/web/common/fragments/GnbWeb.html

@@ -51,8 +51,9 @@
 				</a>
 			</div>
 			<div class="util_group">
-				<span><a href="mb_login.html" title="로그인 바로가기">로그인</a></span>
-				<span><a href="mb_join_1.html" title="회원가입 바로가기">회원가입</a></span>
+				<span th:if="${sessionInfo == null}"><a href="javascript:void(0);" onclick="cfnGoToPage(_PAGE_LOGIN);" title="로그인 바로가기">로그인</a></span>
+				<span th:if="${sessionInfo != null}"><a href="javascript:void(0);" onclick="cfnGoToPage(_PAGE_LOGOUT);" title="로그아웃">로그아웃</a></span>
+				<span th:if="${sessionInfo == null}"><a href="javascript:void(0);" onclick="cfnGoToPage(_PAGE_CUSTOMER_JOIN);" title="회원가입 바로가기">회원가입</a></span>
 				<span><a href="javascript:void(0);" onclick="cfnGoToPage(_PAGE_MYPAGE);" title="마이페이지 바로가기">마이페이지</a></span>
 			</div>
 		</div>

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

@@ -33,6 +33,7 @@
 	<script src="/ux/pc/js/jquery-ui.js"></script>
 	<script src="/ux/pc/js/jquery.modal.min.js"></script>
 	<script src="/ux/plugins/jquery.serializeObject.min.js"></script>
+	<script src="/ux/plugins/mcxdialog/mcxdialog_ui.js"></script>
 
 	<!-- Global site tag (gtag.js) - Google Analytics -->
 <!-- 	<script async src="https://www.googletagmanager.com/gtag/js?id=UA-168660512-1"></script> -->

+ 9 - 2
src/main/webapp/ux/style24_link.js

@@ -1,13 +1,20 @@
 /*
  * Common URL Definition
  */
-const _PAGE_LOGIN = "/signin";	// GNB > 로그인
-const _PAGE_LOGOUT = "/logout";	// GNB > 로그아웃
+const _PAGE_LOGIN = _frontUrl + "/signin";	// GNB > 로그인
+const _PAGE_LOGOUT = _frontUrl + "/logout";	// GNB > 로그아웃
 
 //== 메인 ==/
 const _PAGE_MAIN = _frontUrl + "/display/mall/main/form";	// 몰메인
 
 //== 고객 ==/
+const _PAGE_CUSTOMER_JOIN = _frontUrl + "/customer/join/form";						// 고객 > 회원가입
+const _PAGE_CUSTOMER_SNS_JOIN = _frontUrl + "/customer/sns/join/form";				// 고객 > SNS가입
+const _PAGE_CUSTOMER_JOIN_COMPLETE = _frontUrl + "/customer/join/complete/form";	// 고객 > 고객가입 > 완료페이지
+const _PAGE_CUSTOMER_ID_FIND = _frontUrl + "/customer/id/find/form";				// 고객 > 아이디 찾기
+const _PAGE_CUSTOMER_ID_FIND_RESULT = _frontUrl +"/customer/id/find/result/form";	// 고객 > 아이디 찾기 > 결과페이지
+const _PAGE_CUSTOMER_PWD_FIND = _frontUrl + "/customer/pwd/find/form";				// 고객 > 비밀번호 찾기
+const _PAGE_CUSTOMER_PWD_FIND_RESULT = _frontUrl +"/customer/pwd/find/result/form";	// 고객 > 비밀번호 찾기 > 결과페이지
 
 //== 상품상세 ==/