diff --git a/src/main/java/egovframework/com/ext/oauth/service/OAuthConfig.java b/src/main/java/egovframework/com/ext/oauth/service/OAuthConfig.java
index 79e66b36..9195236e 100644
--- a/src/main/java/egovframework/com/ext/oauth/service/OAuthConfig.java
+++ b/src/main/java/egovframework/com/ext/oauth/service/OAuthConfig.java
@@ -4,18 +4,18 @@ import nlib.cmm.service.NlibProperty;
public interface OAuthConfig {
- static final String GOOGLE_SERVICE_NAME = NlibProperty.getString("oauth2.sns.name.google");
- static final String GOOGLE_PROFILE_URL = NlibProperty.getString("oauth2.client.provider.google.user-info-uri"); // "https://www.googleapis.com/plus/v1/people/me";
+ static final String GOOGLE_SERVICE_NAME = NlibProperty.getString("oauth2.client.provider.google.name");
+ static final String GOOGLE_PROFILE_URL = NlibProperty.getString("oauth2.client.provider.google.user-info-uri"); // "https://www.googleapis.com/plus/v1/people/me";
- static final String NAVER_SERVICE_NAME = NlibProperty.getString("oauth2.sns.name.naver");
- static final String NAVER_PROFILE_URL = NlibProperty.getString("oauth2.client.provider.naver.user-info-uri"); // "https://openapi.naver.com/v1/nid/me";
- static final String NAVER_ACCESS_TOKEN = NlibProperty.getString("oauth2.client.provider.naver.token-uri"); // "https://nid.naver.com/oauth2.0/token?grant_type=authorization_code";
- static final String NAVER_AUTH = NlibProperty.getString("oauth2.client.provider.naver.authorization-uri"); // "https://nid.naver.com/oauth2.0/authorize";
+ static final String NAVER_SERVICE_NAME = NlibProperty.getString("oauth2.client.provider.naver.name");
+ static final String NAVER_PROFILE_URL = NlibProperty.getString("oauth2.client.provider.naver.user-info-uri"); // "https://openapi.naver.com/v1/nid/me";
+ static final String NAVER_ACCESS_TOKEN = NlibProperty.getString("oauth2.client.provider.naver.token-uri"); // "https://nid.naver.com/oauth2.0/token?grant_type=authorization_code";
+ static final String NAVER_AUTH = NlibProperty.getString("oauth2.client.provider.naver.authorization-uri"); // "https://nid.naver.com/oauth2.0/authorize";
- static final String KAKAO_SERVICE_NAME = NlibProperty.getString("oauth2.sns.name.kakao");
- static final String KAKAO_PROFILE_URL = NlibProperty.getString("oauth2.client.provider.kakao.user-info-uri"); // "https://kapi.kakao.com/v2/user/me";
- static final String KAKAO_ACCESS_TOKEN = NlibProperty.getString("oauth2.client.provider.kakao.token-uri"); // "https://kauth.kakao.com/oauth/token?client_id=";
- static final String KAKAO_AUTH = NlibProperty.getString("oauth2.client.provider.kakao.authorization-uri"); // "https://kauth.kakao.com/oauth/authorize";
+ static final String KAKAO_SERVICE_NAME = NlibProperty.getString("oauth2.client.provider.kakao.name");
+ static final String KAKAO_PROFILE_URL = NlibProperty.getString("oauth2.client.provider.kakao.user-info-uri"); // "https://kapi.kakao.com/v2/user/me";
+ static final String KAKAO_ACCESS_TOKEN = NlibProperty.getString("oauth2.client.provider.kakao.token-uri"); // "https://kauth.kakao.com/oauth/token?client_id=";
+ static final String KAKAO_AUTH = NlibProperty.getString("oauth2.client.provider.kakao.authorization-uri"); // "https://kauth.kakao.com/oauth/authorize";
}
diff --git a/src/main/java/egovframework/com/ext/oauth/service/OAuthLogin.java b/src/main/java/egovframework/com/ext/oauth/service/OAuthLogin.java
index c05c1c9d..68c3207c 100644
--- a/src/main/java/egovframework/com/ext/oauth/service/OAuthLogin.java
+++ b/src/main/java/egovframework/com/ext/oauth/service/OAuthLogin.java
@@ -13,6 +13,7 @@ import com.github.scribejava.core.model.Response;
import com.github.scribejava.core.model.Verb;
import com.github.scribejava.core.oauth.OAuth20Service;
+import nlib.user.service.LoginService;
import nlib.util.StringUtil;
@@ -25,15 +26,15 @@ public class OAuthLogin {
this(oauthVO, null);
}
- public OAuthLogin(OAuthVO oauthVO, String deptJsessionId) {
+ public OAuthLogin(OAuthVO oauthVO, String councilJsessionId) {
// CallBack 호출될 경우, 지방문화원 원세션ID를 다시 전달받을 수 있도록 포함 처리 (2021.08.23 KKN)
this.oauthService = new ServiceBuilder(oauthVO.getClientId())
.apiSecret(oauthVO.getClientSecret())
.callback(
- StringUtil.isEmpty(deptJsessionId) ?
+ StringUtil.isEmpty(councilJsessionId) ?
oauthVO.getRedirectUrl() :
- appendUrlParam(oauthVO.getRedirectUrl(), "deptJsessionId", deptJsessionId)
+ appendUrlParam(oauthVO.getRedirectUrl(), LoginService.COUNCIL_JSESSION_ID_NAME, councilJsessionId)
)
.scope("profile")
.build(oauthVO.getApi20Instance());
diff --git a/src/main/java/nlib/cmm/crypto/NlibPasswordEncoder.java b/src/main/java/nlib/cmm/crypto/NlibPasswordEncoder.java
index d7e7ba5c..37e88b45 100644
--- a/src/main/java/nlib/cmm/crypto/NlibPasswordEncoder.java
+++ b/src/main/java/nlib/cmm/crypto/NlibPasswordEncoder.java
@@ -48,7 +48,7 @@ public class NlibPasswordEncoder implements PasswordEncoder { /* extends EgovPa
*/
@Override
public String encode(CharSequence rawPassword) {
-
+ log.debug("encode > rawPassword = " + rawPassword);
return egovPasswordEncoder.encryptPassword(rawPassword.toString());
}
@@ -61,7 +61,8 @@ public class NlibPasswordEncoder implements PasswordEncoder { /* extends EgovPa
*/
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
-
+ log.debug("matches > rawPassword = " + rawPassword);
+ log.debug("matches > encodedPassword = " + encodedPassword);
return egovPasswordEncoder.checkPassword(rawPassword.toString(), encodedPassword);
}
diff --git a/src/main/java/nlib/cmm/session/SessionConfig.java b/src/main/java/nlib/cmm/session/SessionConfig.java
index a52e396a..10a7c5b3 100644
--- a/src/main/java/nlib/cmm/session/SessionConfig.java
+++ b/src/main/java/nlib/cmm/session/SessionConfig.java
@@ -16,57 +16,101 @@ import org.springframework.security.core.context.SecurityContextHolder;
import nlib.user.service.impl.LoginServiceImpl;
import nlib.util.StringUtil;
+/**
+ *
+ * @Class Name : SessionConfig.java
+ *
+ * @Description : 멀티도메인에서의 로그인 처리를 위해 지방문화원에서 해당 세션에서 로그인처리 가능한 정보를 관리하는 클래스
+ *
+ *
+ * @프로젝트명: 지방문화원 통합자료관리시스템 구축사업 (2021)
+ *
+ *
+ *
+ * @ ------------ -------- ---------------------------
+ * @ 수정일 수정자 수정내용
+ * @ ------------ -------- ---------------------------
+ * @ 2021. 8. 25. KNKIM 최초 생성
+ *
+ *
+ * @author 이씨플라자 * DIGITALSHIP KNKIM
+ * @since 2021. 8. 25.
+ * @version 1.0
+ *
+ */
@WebListener
public class SessionConfig implements HttpSessionListener {
- private static final Logger log = LoggerFactory.getLogger(LoginServiceImpl.class);
-
+ private static final Logger log = LoggerFactory.getLogger(SessionConfig.class);
+
+ /* 사용자 정보를 구분하는 구분자 */
+ private static final String DELIMITER = "\t";
+
+ /* 세션별 로그인 처리 가능한 정보 목록 */
private static final Map sessions = new ConcurrentHashMap<>();
-//
-// //중복로그인 지우기
-// public synchronized static String getSessionidCheck(String type, String compareId){
-// String result = "";
-// for( String key : sessions.keySet() ){
-// HttpSession hs = sessions.get(key);
-// if(hs != null && hs.getAttribute(type) != null && hs.getAttribute(type).toString().equals(compareId) ){
-// result = key.toString();
-// }
-// }
-// removeSessionForDoubleLogin(result);
-// return result;
-// }
-//
-// private static void removeSessionForDoubleLogin(String userId){
-// System.out.println("remove userId : " + userId);
-// if(userId != null && userId.length() > 0){
-// sessions.get(userId).invalidate();
-// sessions.remove(userId);
-// }
-// }
-//
@Override
public void sessionCreated(HttpSessionEvent se) {
- System.out.println("Session sessionCreated (O) : " + se.getSession().getId());
+ log.debug("Session sessionCreated (O) : " + se.getSession().getId());
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
log.debug("Session sessionDestroyed (X) : " + se.getSession().getId());
if(sessions.get(se.getSession().getId()) != null){
- //sessions.get(se.getSession().getId()).invalidate();
- sessions.remove(se.getSession().getId());
+ removeLoginInfo(se.getSession().getId());
+ }
+ }
+
+
+ /**
+ * 지방문화원 해당 세션에 로그인 처리 가능한 SNS 유형 및 SNS ID 정보를 리턴한다.
+ * 리턴유형 문자열 배열
+ * [0] : snsType
+ * [1] : snsId
+ *
+ * @param sessionId
+ * @return
+ */
+ public static String[] getLoginInfo(String sessionId) {
+
+ if(StringUtil.isEmpty(sessionId)) return null;
+
+ String loginInfo = sessions.get(sessionId);
+ if(StringUtil.isEmpty(loginInfo)) return null;
+
+ String ret[] = loginInfo.split(DELIMITER);
+
+ log.debug("setLoginInfo : " + sessionId + ", " + ret[0] + ", " + ret[1]);
+
+ return ret;
+ }
+
+ /**
+ * 지방문화원 해당 세션에 로그인 처리 가능한 SNS유형 및 SNSID 정보를 저장한다.
+ *
+ * @param sessionId
+ * @param snsType
+ * @param snsId
+ */
+ public static void setLoginInfo(String sessionId, String snsType, String snsId) {
+ log.debug("setLoginInfo : " + sessionId + ", " + snsType + ", " + snsId);
+ sessions.put(sessionId, StringUtil.getString(snsType, "NONE") + DELIMITER + StringUtil.getString(snsId, "NONE"));
+ }
+
+ /**
+ * 지방문화원 해당 세션에 로그인 처리 가능한 정보를 삭제한다.
+ * (로그인 처리 완료 후 또는 세션 종료 시, 삭제)
+ *
+ * @param sessionId
+ * @param snsType
+ * @param snsId
+ */
+ public static void removeLoginInfo(String sessionId) {
+ if(StringUtil.isNotEmpty(sessionId)){
+ sessions.remove(sessionId);
+ log.debug("removeLoginInfo > sessionId = " + sessionId);
}
}
- public static String getLoginInfo(String sessionId) {
- if(StringUtil.isEmpty(sessionId)) return null;
-
- return sessions.get(sessionId);
- }
-
- public static void setLoginInfo(String sessionId, String snsUserId) {
- sessions.put(sessionId, snsUserId);
- }
-
}
\ No newline at end of file
diff --git a/src/main/java/nlib/user/service/LoginService.java b/src/main/java/nlib/user/service/LoginService.java
index f0a3ba9a..d2fa6219 100644
--- a/src/main/java/nlib/user/service/LoginService.java
+++ b/src/main/java/nlib/user/service/LoginService.java
@@ -5,27 +5,33 @@ import java.util.HashMap;
import javax.servlet.http.HttpServletRequest;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.DisabledException;
+import org.springframework.security.authentication.LockedException;
+
import nlib.restful.service.DataApiReqVO;
import nlib.restful.service.DataApiResVO;
public interface LoginService
{
+ public final String COUNCIL_JSESSION_ID_NAME = "councilJsessionId"; /* 지방문화원 회원 세션ID 저장변수명 */
//-------------------------------
// 로그인 오류코드
//-------------------------------
- public final String ERR_CODE_REQ_MEMBERSHIP = "ERR_REQ_MEMBERSHIP"; /* 오류코드 : 회원가입 필요 */
- public final String ERR_CODE_REQ_SNSLOGIN = "ERR_REQ_SNSLOGIN"; /* 오류코드 : SNS 로그인 필요 */
-
+ public final String ERR_CODE_REQ_MEMBERSHIP = "ERR_REQ_MEMBERSHIP"; /* 오류코드 : 회원가입 필요 */
+ public final String ERR_CODE_REQ_SNSLOGIN = "ERR_REQ_SNSLOGIN"; /* 오류코드 : SNS 로그인 필요 */
+ public final String ERR_CODE_LOCKED = "ERR_CODE_LOGIN_LOCKED"; /* 오류코드 : 계정잠김 */
+ public final String ERR_CODE_DISABLE = "ERR_CODE_LOGIN_DISABLE"; /* 오류코드 : 계정유효하지 않음 */
+ public final String ERR_CODE_MISMATCH = "ERR_CODE_LOGIN_MISMATCH"; /* 오류코드 : ID,PW일치하지 않음 */
+ public final String ERR_CODE_EXC = "ERR_CODE_LOGIN_EXC"; /* 오류코드 : 기타오류 */
public String login(HttpServletRequest req, String username, String password);
- public String loginOauth(HttpServletRequest req, String snsUserId, String councilJsessionId);
+ public String loginOauth(HttpServletRequest req, String oauthServiceName, String snsId, String councilJsessionId);
- public String loginCouncilSSO(HttpServletRequest req, String snsUserId);
+ public String loginCouncilSSO(HttpServletRequest req, String councilJsessionId);
- public String loginCouncil(HttpServletRequest req, String snsUserId);
-
public DataApiResVO logout(DataApiReqVO reqVO);
public HashMap selectCouncilInfoByDnsHost(String councilDnsHost);
diff --git a/src/main/java/nlib/user/service/NlibLoginVO.java b/src/main/java/nlib/user/service/NlibLoginVO.java
index cb3adf03..0bf522ab 100644
--- a/src/main/java/nlib/user/service/NlibLoginVO.java
+++ b/src/main/java/nlib/user/service/NlibLoginVO.java
@@ -68,6 +68,9 @@ public class NlibLoginVO implements Serializable {
private String ip; /* 접속IP */
private String authorityList; /* 권한ROLE 리스트 */
private String authKey; /* RESTful API : 접속인증코드 */
+
+ private String error = null; /* 처리 오류코드 */
+ private String message = null; /* 처리 관련 메시지 */
/**
* 로그인인증키값을 리턴한다. 만일 존재하지 않을 경우, userId를 기반으로 인증키를 생성하여 리턴한다.
@@ -323,4 +326,20 @@ public class NlibLoginVO implements Serializable {
this.authKey = authKey;
}
+ public String getError() {
+ return error;
+ }
+
+ public void setError(String error) {
+ this.error = error;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
}
diff --git a/src/main/java/nlib/user/service/impl/LoginDAO.java b/src/main/java/nlib/user/service/impl/LoginDAO.java
index 3ac8eace..2f55bd3d 100644
--- a/src/main/java/nlib/user/service/impl/LoginDAO.java
+++ b/src/main/java/nlib/user/service/impl/LoginDAO.java
@@ -21,8 +21,8 @@ public class LoginDAO extends EgovComAbstractDAO {
return null;
}
- public NlibLoginVO selectLoginUserInfo(String snsUserId) {
- return (NlibLoginVO) selectOne("LoginDAO.selectLoginUserInfo", snsUserId);
+ public NlibLoginVO selectLoginUserInfo(NlibLoginVO nLoginParamVO) {
+ return (NlibLoginVO) selectOne("LoginDAO.selectLoginUserInfo", nLoginParamVO);
}
public void insertLoginLog(NlibLoginVO userVO) {
diff --git a/src/main/java/nlib/user/service/impl/LoginServiceImpl.java b/src/main/java/nlib/user/service/impl/LoginServiceImpl.java
index 0d8d5e48..0465abc9 100644
--- a/src/main/java/nlib/user/service/impl/LoginServiceImpl.java
+++ b/src/main/java/nlib/user/service/impl/LoginServiceImpl.java
@@ -40,43 +40,171 @@ public class LoginServiceImpl implements LoginService
/*
* 입력된 로그인정보를 받아 로그인 처리한다.
+ * 정상인 경우 null을 리턴하고, 오류발생시 오류코드를 리턴한다
*
* (non-Javadoc)
* @see nlib.user.service.LoginService#login(javax.servlet.http.HttpServletRequest, java.lang.String, java.lang.String)
*/
public String login(HttpServletRequest req, String username, String password) {
-
// 아이디와 패스워드로, Security 가 알아 볼 수 있는 token 객체로 변경한다.
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
- try {
- // AuthenticationManager 에 token 을 넘기면 UserDetailsService 가 받아 처리하도록 한다.
- Authentication authentication = authenticationManager.authenticate(token);
-
- // AuthKey 등록
- SecUserVO userVO = (SecUserVO)authentication.getPrincipal();
- userVO.setAuthKey(req.getSession().getId()); // TODO : 임시로 세션ID 넣음, 실제 Authkey 생성 규칙에 따른 해당 키 설정 처리 필요
-
- // 실제 SecurityContext 에 authentication 정보를 등록한다.
- SecurityContextHolder.getContext().setAuthentication(authentication);
-
- // TODO : 로그인 로그 정보를 통합자료시스템에 전송한다.
-
- } catch (DisabledException e) {
- return "/login/loginForm.do?error=locked";
- } catch (LockedException e) {
- return "/login/loginForm.do?error=disable";
- } catch (BadCredentialsException e) {
- return "/login/loginForm.do?error=invalid-password";
- } catch (Exception e) {
- return "/login/loginForm.do?error=other." + e.toString();
+ NlibLoginVO userVO = loginProcSecurity(req, token);
+
+ if(StringUtil.isNotEmpty(userVO.getError())) {
+ return userVO.getError();
}
+ // 로그인 로그 정보 저장
+ loginDAO.insertLoginLog(userVO);
+
return null;
}
+
+ /*
+ * SNS 연동 SSO를 처리를 위한 확인작업을 수행한다.
+ * (nlib에서 호출됨)
+ *
+ * return값이 null인 경우, 정상적인 로그인 가능 상태
+ * null이 아닌 경우, 오류 코드
+ * - ERR_CODE_REQ_SNSLOGIN : SNS 정상적인 로그인이 필요
+ * - ERR_CODE_REQ_MEMBERSHIP : 신규회원가입/회원통합 필요
+ */
+ public String loginOauth(HttpServletRequest req, String oauthServiceName, String snsId, String councilJsessionId) {
+
+ if(StringUtil.isEmpty(snsId)) return ERR_CODE_REQ_SNSLOGIN;
+
+ // SNS 연동 사용자UID로 사용자정보 조회
+ NlibLoginVO nLoginParamVO = new NlibLoginVO();
+ nLoginParamVO.setSnsId(snsId);
+ nLoginParamVO.setSnsType(oauthServiceName);
+ NlibLoginVO nLoginVO = loginDAO.selectLoginUserInfo(nLoginParamVO);
+
+ if(nLoginVO == null) {
+ return ERR_CODE_REQ_MEMBERSHIP;
+ }
+
+ // 세션목록관리에 로그인 정상 가능 정보 추가
+ SessionConfig.setLoginInfo(councilJsessionId, oauthServiceName, snsId);
+
+ // 회원가입정보 존재하는 SNS 아이디임을 확인 완료
+ return null;
+ }
+
+ /*
+ * 지방문화원 해당 세션에 대한 최종 로그인 세션을 처리한다.
+ * 정상인 경우 null을 리턴하고, 오류발생시 오류코드를 리턴한다.
+ *
+ * (non-Javadoc)
+ * @see nlib.user.service.LoginService#loginCouncilSSO(javax.servlet.http.HttpServletRequest, java.lang.String)
+ */
+ public String loginCouncilSSO(HttpServletRequest req, String councilJsessionId) {
+
+ // 세션에서 로그인 처리 가능 정보 가져오기
+ String loginInfo[] = SessionConfig.getLoginInfo(councilJsessionId);
+ if(loginInfo == null || loginInfo.length < 2) return ERR_CODE_REQ_SNSLOGIN;
+
+ String oauthServiceName = loginInfo[0];
+ String snsId = loginInfo[1];
+
+ if(StringUtil.isEmpty(oauthServiceName))
+ return ERR_CODE_REQ_SNSLOGIN;
+ if(StringUtil.isEmpty(snsId))
+ return ERR_CODE_REQ_SNSLOGIN;
+
+ // 회원DB에서 사용자 정보 조회
+ NlibLoginVO nLoginParamVO = new NlibLoginVO();
+ nLoginParamVO.setSnsId(snsId);
+ nLoginParamVO.setSnsType(oauthServiceName);
+
+ NlibLoginVO nLoginVO = loginDAO.selectLoginUserInfo(nLoginParamVO);
+
+ if(nLoginVO == null) {
+ return ERR_CODE_REQ_MEMBERSHIP;
+ }
+
+ // Spring Security 로그인처리위한 token 생성하여 로그인 처리
+ UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(nLoginVO.getLoginUserId(), nLoginVO.getPassword());
+ NlibLoginVO nlibLoginedVO = loginProcSecurity(req, token);
+
+ if(StringUtil.isNotEmpty(nlibLoginedVO.getError())) {
+ return nlibLoginedVO.getError();
+ }
+
+ // 로그인 로그 정보 저장
+ loginDAO.insertLoginLog(nlibLoginedVO);
+
+ return null;
+
+ }
+
+ /**
+ * 스프링 시큐리티 로그인 처리를 수행한다.
+ *
+ * @param req
+ * @param token
+ * @return
+ */
+ public NlibLoginVO loginProcSecurity(HttpServletRequest req, UsernamePasswordAuthenticationToken token) {
+
+ NlibLoginVO nlibLoginVO = null;
+
+ try {
+ // AuthenticationManager 에 token 을 넘기면 UserDetailsService 가 받아 처리하도록 한다.
+ Authentication authentication = authenticationManager.authenticate(token);
+
+ // AuthKey 생성 및 기타 정보 저장
+ String sessionId = req.getSession().getId();
+ nlibLoginVO = (NlibLoginVO)authentication.getPrincipal();
+ nlibLoginVO.getOrCreateAuthKey(sessionId); // SET AUTHKEY
+ nlibLoginVO.setIp(req.getRemoteAddr());
+
+ // 실제 SecurityContext 에 authentication 정보를 등록한다.
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+ SessionConfig.removeLoginInfo(sessionId);
+
+ } catch (DisabledException e) {
+ log.error("loginProcSecurity > " + ERR_CODE_LOCKED);
+ if(nlibLoginVO == null) nlibLoginVO = new NlibLoginVO();
+ nlibLoginVO.setError(ERR_CODE_LOCKED);
+ } catch (LockedException e) {
+ log.error("loginProcSecurity > " + ERR_CODE_DISABLE);
+ if(nlibLoginVO == null) nlibLoginVO = new NlibLoginVO();
+ nlibLoginVO.setError(ERR_CODE_DISABLE);
+ } catch (BadCredentialsException e) {
+ log.error("loginProcSecurity > " + ERR_CODE_MISMATCH);
+ if(nlibLoginVO == null) nlibLoginVO = new NlibLoginVO();
+ nlibLoginVO.setError(ERR_CODE_MISMATCH);
+ } catch (Exception e) {
+ log.error("loginProcSecurity > " + ERR_CODE_EXC + " : " + e.toString());
+ if(nlibLoginVO == null) nlibLoginVO = new NlibLoginVO();
+ nlibLoginVO.setError(ERR_CODE_EXC + " : " + e.toString());
+ }
+
+ return nlibLoginVO;
+ }
+
+ public DataApiResVO logout(DataApiReqVO reqVO) {
+ return null;
+ }
+
+ /*
+ * DNS 호스트명으로 지방문화원 정보를 조회한다. (지방문화원코드, 명칭)
+ *
+ * (non-Javadoc)
+ * @see nlib.user.service.LoginService#selectCouncilInfoByDnsHost(java.lang.String)
+ */
+ public HashMap selectCouncilInfoByDnsHost(String councilDnsHost) {
+ return loginDAO.selectCouncilInfoByDnsHost(councilDnsHost);
+ }
+
+
+
+
+
/* SNS 연동 SSO를 통한 로그인 처리한다.
*
* return값이 null인 경우, 정상적인 로그인 처리 완료
@@ -131,134 +259,40 @@ public class LoginServiceImpl implements LoginService
// }
//
- /* SNS 연동 SSO를 통한 로그인 처리한다.
- *
- * return값이 null인 경우, 정상적인 로그인 처리 완료
- * null이 아닌 경우, 오류 코드
- * - REQ-SNS-SSO : SNS 정상적인 로그인이 필요
- * - NEW-MEMBERSHIP : 신규회원가입/회원통합 필요
- */
- public String loginOauth(HttpServletRequest req, String snsId, String councilJsessionId) {
-
- // SNS 연동 사용자UID로 사용자정보 조회
- if(StringUtil.isEmpty(snsId)) return ERR_CODE_REQ_SNSLOGIN;
-
- NlibLoginVO nLoginVO = loginDAO.selectLoginUserInfo(snsId);
-
- if(nLoginVO == null) {
- return ERR_CODE_REQ_MEMBERSHIP;
- }
-
- // 회원가입정보 존재하는 SNS 아이디임을 확인 완료
- return null;
- }
-
- public String loginCouncilSSO(HttpServletRequest req, String snsUserId) {
-
- // SNS 연동 사용자UID로 사용자정보 조회
- if(StringUtil.isEmpty(snsUserId)) return "/login/loginForm.do?error=req-sns-sso";
-
- NlibLoginVO nLoginVO = loginDAO.selectLoginUserInfo(snsUserId);
-
- if(nLoginVO == null) {
- return "/login/loginForm.do?error=new-membership";
- }
-
- // 아이디와 패스워드로, Security 가 알아 볼 수 있는 token 객체로 변경한다.
- UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(nLoginVO.getLoginUserId(), nLoginVO.getPassword());
-
- try {
- // AuthenticationManager 에 token 을 넘기면 UserDetailsService 가 받아 처리하도록 한다.
- Authentication authentication = authenticationManager.authenticate(token);
-
- // AuthKey 등록
- SecUserVO userVO = (SecUserVO)authentication.getPrincipal();
- userVO.getOrCreateAuthKey(req.getSession().getId()); // SET AUTHKEY
-
- // 실제 SecurityContext 에 authentication 정보를 등록한다.
- SecurityContextHolder.getContext().setAuthentication(authentication);
-
- // TODO : 로그인 로그 정보를 통합자료시스템에 전송한다.
- userVO.setIp(req.getRemoteAddr());
- loginDAO.insertLoginLog(userVO);
-
- } catch (DisabledException e) {
- e.printStackTrace();
- return "/login/loginForm.do?error=locked";
- } catch (LockedException e) {
- e.printStackTrace();
- return "/login/loginForm.do?error=disable";
- } catch (BadCredentialsException e) {
- e.printStackTrace();
- return "/login/loginForm.do?error=invalid-password";
- } catch (Exception e) {
- e.printStackTrace();
- return "/login/loginForm.do?error=other&message=" + e.toString();
- }
-
- return null;
- }
-
- /* SNS 연동 SSO를 통한 로그인 처리한다.
+ /*
+ * SNS 연동 SSO를 통한 로그인 처리한다.
*
* return값이 null인 경우, 정상적인 로그인 처리 완료
* null이 아닌 경우, REDIRECT되어야 하는 URL(오류 코드 포함)
*
*/
- public String loginCouncil(HttpServletRequest req, String snsUserId) {
-
- // SNS 연동 사용자UID로 사용자정보 조회
- if(StringUtil.isEmpty(snsUserId)) return "/login/loginForm.do?error=req-sns-sso";
-
- NlibLoginVO nLoginVO = loginDAO.selectLoginUserInfo(snsUserId);
-
- if(nLoginVO == null) {
- return "/login/loginForm.do?error=new-membership";
- }
-
- // 아이디와 패스워드로, Security 가 알아 볼 수 있는 token 객체로 변경한다.
- UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(nLoginVO.getLoginUserId(), nLoginVO.getPassword());
-
- try {
- // AuthenticationManager 에 token 을 넘기면 UserDetailsService 가 받아 처리하도록 한다.
- Authentication authentication = authenticationManager.authenticate(token);
-
- // AuthKey 등록
- SecUserVO userVO = (SecUserVO)authentication.getPrincipal();
- userVO.getOrCreateAuthKey(req.getSession().getId()); // SET AUTHKEY
-
- // 실제 SecurityContext 에 authentication 정보를 등록한다.
- SecurityContextHolder.getContext().setAuthentication(authentication);
-
- // TODO : 로그인 로그 정보를 통합자료시스템에 전송한다.
- userVO.setIp(req.getRemoteAddr());
- loginDAO.insertLoginLog(userVO);
-
- } catch (DisabledException e) {
- e.printStackTrace();
- return "/login/loginForm.do?error=locked";
- } catch (LockedException e) {
- e.printStackTrace();
- return "/login/loginForm.do?error=disable";
- } catch (BadCredentialsException e) {
- e.printStackTrace();
- return "/login/loginForm.do?error=invalid-password";
- } catch (Exception e) {
- e.printStackTrace();
- return "/login/loginForm.do?error=other&message=" + e.toString();
- }
-
- return null;
- }
+// public String loginCouncil(HttpServletRequest req, String snsUserId) {
+//
+// // SNS 연동 사용자UID로 사용자정보 조회
+// if(StringUtil.isEmpty(snsUserId)) return "/login/loginForm.do?error=req-sns-sso";
+//
+//
+// NlibLoginVO nLoginParamVO = new NlibLoginVO();
+//
+// nLoginParamVO.setSnsId(snsId);
+// nLoginParamVO.setSnsType(oauthServiceName);
+//
+// NlibLoginVO nLoginVO = loginDAO.selectLoginUserInfo(nLoginParamVO);
+//
+// if(nLoginVO == null) {
+// return "/login/loginForm.do?error=new-membership";
+// }
+//
+// // 아이디와 패스워드로, Security 가 알아 볼 수 있는 token 객체로 변경한다.
+// UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(nLoginVO.getLoginUserId(), nLoginVO.getPassword());
+// NlibLoginVO nlibLoginedVO = loginProcSecurity(req, token);
+//
+// // 로그인 로그 저장
+// loginDAO.insertLoginLog(NlibLoginedVO);
+//
+// return null;
+// }
- public DataApiResVO logout(DataApiReqVO reqVO) {
- return null;
- }
-
- public HashMap selectCouncilInfoByDnsHost(String councilDnsHost) {
- return loginDAO.selectCouncilInfoByDnsHost(councilDnsHost);
- }
-
}
\ No newline at end of file
diff --git a/src/main/java/nlib/user/web/LoginController.java b/src/main/java/nlib/user/web/LoginController.java
index e1640961..9e9291a2 100644
--- a/src/main/java/nlib/user/web/LoginController.java
+++ b/src/main/java/nlib/user/web/LoginController.java
@@ -46,13 +46,12 @@ import nlib.util.StringUtil;
*
*
* @ ------------ -------- ---------------------------
- * @ 수정일 수정자 수정내용
+ * @ 수정일 수정자 수정내용
* @ ------------ -------- ---------------------------
- * @ 2021. 7. 9. KNKIM 최초 생성
- * @ 2021. 8.18. RESTful -> DB접속 방식으로 변경 처리
- * @ 2021. 8.19. 지방문화원별 다른 도메인 호스트 접속 처리를 위한 Spring Security 변경 작업, 네이버 전자정보 프레임워크 기반으로 변경
- * @ 2021. 8.24. 테이블 변경에 따른 항목명 변경
- *
+ * @ 2021. 7. 9. KNKIM 최초 생성
+ * @ 2021. 8.18. KNKIM RESTful -> DB접속 방식으로 변경 처리
+ * @ 2021. 8.19. KNKIM 지방문화원별 다른 도메인 호스트 접속 처리를 위한 Spring Security 변경 작업, 네이버 전자정보 프레임워크 기반으로 변경
+ * @ 2021. 8.24. KNKIM 테이블 변경에 따른 항목명 변경 및 코드 정리작업
*
*
* @author 이씨플라자 * DIGITALSHIP KNKIM
@@ -109,14 +108,14 @@ public class LoginController {
String councilDnsHost = StringUtil.getHostName(url);
// returnUrl 정보 설정
- if(StringUtil.isEmpty(returnUrl)) {
- RequestCache cache = new HttpSessionRequestCache();
- SavedRequest savedRequest = cache.getRequest(req, res);
- if (savedRequest != null) {
- councilReturnUrl = savedRequest.getRedirectUrl();
- log.debug("loginCouncil > HttpSessionRequestCache에서 획득 councilReturnUrl = " + councilReturnUrl);
- }
- }
+// if(StringUtil.isEmpty(returnUrl)) {
+// RequestCache cache = new HttpSessionRequestCache();
+// SavedRequest savedRequest = cache.getRequest(req, res);
+// if (savedRequest != null) {
+// councilReturnUrl = savedRequest.getRedirectUrl();
+// log.debug("loginCouncil > HttpSessionRequestCache에서 획득 councilReturnUrl = " + councilReturnUrl);
+// }
+// }
// 지방문화원 코드 확인
HashMap councilInfo = loginService.selectCouncilInfoByDnsHost(councilDnsHost);
@@ -169,7 +168,7 @@ public class LoginController {
//----------------------------------------------
// 지방문화원 사이트에서 접속한 경우, 지방문화원 정보 확인
//----------------------------------------------
- String councilJsessionId = paramMap.get("councilJsessionId");
+ String councilJsessionId = paramMap.get(LoginService.COUNCIL_JSESSION_ID_NAME);
String councilDnsHost = paramMap.get("councilDnsHost");
String councilReturnUrl = StringUtil.decodeBase64(paramMap.get("councilReturnUrl"));
String councilHomeUrl = StringUtil.decodeBase64(paramMap.get("councilHomeUrl"));
@@ -183,7 +182,7 @@ public class LoginController {
log.debug("loginForm > councilCd = " + councilCd);
log.debug("loginForm > councilNm = " + councilNm);
- session.setAttribute("councilJsessionId", councilJsessionId);
+ session.setAttribute(LoginService.COUNCIL_JSESSION_ID_NAME, councilJsessionId);
session.setAttribute("councilDnsHost", councilDnsHost);
session.setAttribute("councilReturnUrl", councilReturnUrl);
session.setAttribute("councilHomeUrl" , councilHomeUrl);
@@ -211,9 +210,177 @@ public class LoginController {
return "nlib/login/loginForm";
}
+
+ /**
+ * SNS 로그인 후, 콜백으로 호출되며 회원가입된 사용자가 존재하는 지 확인하여 로그인 처리가 진행될 수 있도록 한다.
+ * 정상 회원가입자인 경우, 해당 지방문화원 도메인(호스트)주소로 loginCouncilSSO(..)가 재호출되어 실제 세션 로그인 처리 마무리가 이루어진다.
+ *
+ * @param req
+ * @param oauthServiceName
+ * @param paramMap
+ * @param model
+ * @param session
+ * @return
+ * @throws Exception
+ */
+ @RequestMapping(value = {"/login/{oauthServiceName}Callback.do", "/login/{oauthServiceName}/callback.do"})
+ public String loginOauthCallback(
+ HttpServletRequest req,
+ @PathVariable String oauthServiceName,
+ @RequestParam Map paramMap,
+ Model model,
+ HttpSession session) throws Exception {
+
+ String redirectUrl = NlibProperty.getString("home.uri"); // "/index.do";
+ String code = paramMap.get("code");
+ String councilJsessionId = paramMap.get(LoginService.COUNCIL_JSESSION_ID_NAME);
+
+ log.debug("oauthLoginCallback: code = ", code);
+
+ // SNS 클라이언트 설정 정보 할당
+ OAuthVO oauthVO = null;
+ if (StringUtil.equalsIgnoreCase(OAuthConfig.GOOGLE_SERVICE_NAME, oauthServiceName))
+ oauthVO = googleAuthVO;
+ else if (StringUtil.equalsIgnoreCase(OAuthConfig.NAVER_SERVICE_NAME, oauthServiceName))
+ oauthVO = naverAuthVO;
+ else if (StringUtil.equalsIgnoreCase(OAuthConfig.KAKAO_SERVICE_NAME, oauthServiceName))
+ oauthVO = kakaoAuthVO;
+ else {
+ throw new Exception("SNS 연동 콜백정보가 올바르지 않습니다.");
+ }
+
+ //-----------------------------------------------
+ // 1. code를 이용해서 Access Token 받기
+ // 2. Access Token을 이용해서 사용자 제공정보 가져오기
+ //-----------------------------------------------
+ OAuthLogin oauthLogin = new OAuthLogin(oauthVO, councilJsessionId);
+ OAuthUniversalUser oauthUser = oauthLogin.getUserProfile(code); // 1,2번 동시
+ log.debug("oauthLoginCallback > Profile ===>>" + oauthUser.getSnsId() + ", " + oauthUser.getName());
+
+ // oAuth를 통한 SNS연동이 실패한 경우
+ if(oauthUser == null || !oauthUser.isValid()) {
+ model.addAttribute("message", "먼저 회원가입하신 후, 이용하여 주시기 바랍니다.");
+ redirectUrl = NlibProperty.getString("member.new.url"); // "/member/insertMemberInfoForm.do"
+ return "redirect:" + redirectUrl;
+ }
+
+ //-----------------------------------------------
+ // 등록된 사용자 여부 확인
+ //-----------------------------------------------
+ String loginResult = loginService.loginOauth(req, oauthServiceName, oauthUser.getSnsId(), councilJsessionId);
+
+ // 정상 SNS 연동 로그인
+ if(loginResult == null) {
+ // 세션에 설정된 redirect 주소 확인
+ String councilHomeUrl = (String)session.getAttribute("councilHomeUrl");
+ redirectUrl = councilHomeUrl + "/login/loginCouncilSSO.do";
+ return "redirect:" + redirectUrl;
+ }
+
+ // SNS 로그인은 정상적이나, NLIB 시스템에 등록되지 않은 사용자 : 회원가입으로 전환
+ model.addAttribute("error", loginResult);
+ if(StringUtil.equalsIgnoreCase(loginResult, loginService.ERR_CODE_REQ_MEMBERSHIP)) {
+ model.addAttribute("message", "회원가입하신 후, 이용 가능합니다.");
+ redirectUrl = NlibProperty.getString("member.new.url");
+ } else if(StringUtil.equalsIgnoreCase(loginResult, loginService.ERR_CODE_REQ_SNSLOGIN)) {
+ model.addAttribute("message", "먼저 로그인할 SNS를 선택하여 로그인 바랍니다.");
+ redirectUrl = NlibProperty.getString("login.back.url");
+ }
+
+ return "redirect:" + redirectUrl;
+ }
+
+ /**
+ * ID, PW를 입력받아 로그인을 처리한다.
+ * (스프링 시큐리티 로그인 수행)
+ *
+ * @param req
+ * @param res
+ * @param username
+ * @param password
+ * @param model
+ * @return
+ */
+ @RequestMapping("/login/login.do")
+ public String login(HttpServletRequest req, HttpServletResponse res,
+ @RequestParam(required = false) String username, @RequestParam(required = false) String password,
+ ModelMap model) {
+
+ String loginResult = loginService.login(req, username, password);
+ String redirectUrl = NlibProperty.getString("home.uri");
+
+ // 정상적으로 로그인된 경우
+ if (StringUtil.isEmpty(loginResult)) {
+ RequestCache cache = new HttpSessionRequestCache();
+ SavedRequest savedRequest = cache.getRequest(req, res);
+ if (savedRequest != null) {
+ redirectUrl = savedRequest.getRedirectUrl();
+ }
+ }
+ // 로그인 실패
+ else {
+ redirectUrl = NlibProperty.getString("login.back.url") + "?error=" + loginResult;
+ }
+
+ log.debug("login.do > loginResult = " + loginResult);
+ log.debug("login.do > redirectUrl = " + redirectUrl);
+
+ return "redirect:" + redirectUrl;
+ }
+ /**
+ * 정상적인 사용자의 로그인 요청이 검증된 후, 실제 해당 지방문화원 사이트 URL로 호출되는 함수로써 로그인 최종 처리를 진행한다.
+ *
+ * @param req
+ * @param res
+ * @param session
+ * @param model
+ * @return
+ * @throws Exception
+ */
+ @RequestMapping("/login/loginCouncilSSO.do")
+ public String loginCouncilSSO(
+ HttpServletRequest req,
+ HttpServletResponse res,
+ HttpSession session,
+ ModelMap model) throws Exception {
+
+ String redirectUrl = "/index.do";
+
+ String jsessionId = session.getId();
+ String loginResult = loginService.loginCouncilSSO(req, session.getId());
+
+ // oAuth를 통한 SNS연동 성공인 경우
+ if(loginResult == null) {
+ // 세션에 설정된 redirect 주소 확인
+ String councilReturnUrl = (String)session.getAttribute("councilReturnUrl");
+
+ log.debug("loginCouncilSSO > councilReturnUrl = " + councilReturnUrl);
+ if(StringUtil.isNotEmpty(councilReturnUrl)) {
+ redirectUrl = councilReturnUrl;
+ }
+ }
+ // SNS 로그인은 정상적이나, NLIB 시스템에 등록되지 않았거나 정보가 서로 맞지 않는 경우
+ else {
+ model.addAttribute("error", loginResult);
+ redirectUrl = NlibProperty.getString("login.back.url");
+ }
+
+ return "redirect:" + redirectUrl;
+ }
+
+ /**
+ * 로그아웃 후의 처리를 담당한다. 스프링 시큐리티에서 로그아웃이 수행된 후, 호출된다.
+ *
+ * @param req
+ * @return
+ */
+ @RequestMapping("/login/logout.do")
+ public String logout(HttpServletRequest req) {
+ return null;
+ }
-
+
// // 구글 로그인 콜백
// @RequestMapping(value = "/login/googleCallback.do")
// public String googleCallback(@RequestParam(value = "code") String authCode, HttpSession session, Model model,
@@ -297,146 +464,4 @@ public class LoginController {
// }// end kakaoLogin()
//
- @RequestMapping(value = {"/login/{oauthServiceName}Callback.do", "/login/{oauthServiceName}/callback.do"})
- public String oauthLoginCallback(
- HttpServletRequest req,
- @PathVariable String oauthServiceName,
- @RequestParam Map paramMap,
- Model model,
- HttpSession session) throws Exception {
-
- String redirectUrl = NlibProperty.getString("home.uri"); // "/index.do";
- String code = paramMap.get("code");
- String councilJsessionId = paramMap.get("councilJsessionId");
-
- log.debug("oauthLoginCallback: code = ", code);
-
- // SNS 클라이언트 설정 정보 할당
- OAuthVO oauthVO = null;
- if (StringUtil.equals(OAuthConfig.GOOGLE_SERVICE_NAME, oauthServiceName))
- oauthVO = googleAuthVO;
- else if (StringUtil.equals(OAuthConfig.NAVER_SERVICE_NAME, oauthServiceName))
- oauthVO = naverAuthVO;
- else if (StringUtil.equals(OAuthConfig.KAKAO_SERVICE_NAME, oauthServiceName))
- oauthVO = kakaoAuthVO;
- else {
- throw new Exception("SNS 연동 콜백정보가 올바르지 않습니다.");
- }
-
- //-----------------------------------------------
- // 1. code를 이용해서 Access Token 받기
- // 2. Access Token을 이용해서 사용자 제공정보 가져오기
- //-----------------------------------------------
- OAuthLogin oauthLogin = new OAuthLogin(oauthVO, councilJsessionId);
-
- OAuthUniversalUser oauthUser = oauthLogin.getUserProfile(code); // 1,2번 동시
- log.debug("oauthLoginCallback > Profile ===>>" + oauthUser.toString());
-
- // oAuth를 통한 SNS연동이 실패한 경우
- if(oauthUser == null || !oauthUser.isValid()) {
- model.addAttribute("message", "먼저 회원가입하신 후, 이용하여 주시기 바랍니다.");
- redirectUrl = NlibProperty.getString("member.new.url"); // "/member/insertMemberInfoForm.do"
- return "redirect:" + redirectUrl;
- }
-
- // 등록된 사용자 여부 확인
- String loginResult = loginService.loginOauth(req, oauthUser.getSnsId(), councilJsessionId);
-
- // 정상 SNS 연동 로그인
- if(loginResult == null) {
- // 세션에 설정된 redirect 주소 확인
- model.addAttribute("message", "정상적으로 로그인되었습니다.");
- String councilHomeUrl = (String)session.getAttribute("councilHomeUrl");
- redirectUrl = councilHomeUrl + "/login/loginCouncilSSO.do";
- return "redirect:" + redirectUrl;
- }
-
- // SNS 로그인은 정상적이나, NLIB 시스템에 등록되지 않은 사용자 : 회원가입으로 전환
- model.addAttribute("error", loginResult);
- if(StringUtil.equals(loginResult, loginService.ERR_CODE_REQ_MEMBERSHIP)) {
- model.addAttribute("message", "먼저 회원가입하신 후, 이용 가능합니다. 해당 SNS 계정 정보로 회원가입을 진행하시겠습니까?");
- redirectUrl = NlibProperty.getString("member.new.url");
- } else if(StringUtil.equals(loginResult, loginService.ERR_CODE_REQ_SNSLOGIN)) {
- model.addAttribute("message", "먼저 SNS 로그인 바랍니다.");
- redirectUrl = NlibProperty.getString("login.back.url");
- }
-
- return "redirect:" + redirectUrl;
- }
-
- /**
- * 로그인을 처리한다. (스프링 시큐리티 로그인 수행)
- *
- * @param req
- * @param res
- * @param username
- * @param password
- * @param model
- * @return
- */
- @RequestMapping("/login/login.do")
- public String login(HttpServletRequest req, HttpServletResponse res,
- @RequestParam(required = false) String username, @RequestParam(required = false) String password,
- ModelMap model) {
-
- String redirectUrl = loginService.login(req, username, password);
-
- // 정상적으로 로그인된 경우
- if (StringUtil.isEmpty(redirectUrl)) {
- RequestCache cache = new HttpSessionRequestCache();
- SavedRequest savedRequest = cache.getRequest(req, res);
- if (savedRequest == null)
- redirectUrl = "/";
- else
- redirectUrl = savedRequest.getRedirectUrl();
- }
-
- log.debug("login.do > RequestCache : url=" + redirectUrl);
-
- return "redirect:" + redirectUrl;
- }
-
- @RequestMapping("/login/loginCouncilSSO.do")
- public String loginCouncilSSO(
- HttpServletRequest req,
- HttpServletResponse res,
- HttpSession session,
- ModelMap model) throws Exception {
-
- String redirectUrl = "/index.do";
-
- String snsUserId = SessionConfig.getLoginInfo(session.getId());
- String loginResult = loginService.loginCouncilSSO(req, snsUserId);
-
- // oAuth를 통한 SNS연동이 실패한 경우
- if(loginResult == null) {
- // 세션에 설정된 redirect 주소 확인
- String councilReturnUrl = (String)session.getAttribute("councilReturnUrl");
-
- log.debug("loginCouncilSSO > councilReturnUrl = " + councilReturnUrl);
- if(StringUtil.isNotEmpty(councilReturnUrl)) {
- redirectUrl = councilReturnUrl;
- }
- }
- // SNS 로그인은 정상적이나, NLIB 시스템에 등록되지 않은 사용자 : 회원가입으로 전환
- else {
- model.addAttribute("code", "GONEW");
- model.addAttribute("message", "먼저 회원가입하신 후, 이용 가능합니다. 해당 SNS 계정 정보로 회원가입을 진행하시겠습니까?");
- redirectUrl = loginResult;
- }
-
- return "redirect:" + redirectUrl;
-
- }
-
- /**
- * 로그아웃 후의 처리를 담당한다. 스프링 시큐리티에서 로그아웃이 수행된 후, 호출된다.
- *
- * @param req
- * @return
- */
- @RequestMapping("/login/logout.do")
- public String logout(HttpServletRequest req) {
- return null;
- }
}
\ No newline at end of file
diff --git a/src/main/resources/egovframework/mapper/nlib/user/LoginDAO_SQL.xml b/src/main/resources/egovframework/mapper/nlib/user/LoginDAO_SQL.xml
index c0cae348..3d936d49 100644
--- a/src/main/resources/egovframework/mapper/nlib/user/LoginDAO_SQL.xml
+++ b/src/main/resources/egovframework/mapper/nlib/user/LoginDAO_SQL.xml
@@ -3,7 +3,7 @@
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-