하석형 하석형 05-27
250527 하석형 이메일 2차 인증 설정 여부에 따른 관리자 로그인 방식 변경
@d23abfe13b9a326e31f12efdf25b88955c4f6ec1
client/resources/api/loginPolicy.js
--- client/resources/api/loginPolicy.js
+++ client/resources/api/loginPolicy.js
@@ -14,4 +14,12 @@
 
 export const saveByLoginMode = loginMode => {
     return apiClient.post(`/admin/loginPolicy/saveLoginMode.json`, loginMode);
+}
+
+export const findAllBy2ndAuth = () => {
+    return apiClient.post(`/admin/loginPolicy/findByEmail2ndAuth.json`);
+}
+
+export const saveBy2ndAuth = email2ndAuth => {
+    return apiClient.post(`/admin/loginPolicy/saveEmail2ndAuth.json`, email2ndAuth);
 }
(파일 끝에 줄바꿈 문자 없음)
client/views/pages/adm/system/LoginPolicy/LoginPolicy.vue
--- client/views/pages/adm/system/LoginPolicy/LoginPolicy.vue
+++ client/views/pages/adm/system/LoginPolicy/LoginPolicy.vue
@@ -53,11 +53,31 @@
                   <div class="form-check">
                     <input type="text" id="cntxtPth" class="form-control sm" v-model="cntxtPth" ref="cntxtPth" />
                   </div>
-                  <button class="btn sm main" @click="fnSave">저장</button>
+                  <button class="btn sm main" @click="saveByContextPath">저장</button>
                 </div>
                 <span class="ml10 gray">
                   <strong>/</strong> 또는 <strong>/경로</strong> 형식으로 입력하세요.
                 </span>
+              </div>
+            </div>
+            <div class="layout">
+            </div>
+            <div class="layout">
+              <label class="form-title">이메일 2차 인증 설정</label>
+              <div class="form-group">
+                <div class="check-area">
+                  <div class="form-check">
+                    <input type="radio" id="use2ndAuthY" class="mr5" value="Y" v-model="use2ndAuth"
+                      @change="saveBy2ndAuth" />
+                    <label for="use2ndAuthY">사용</label>
+                  </div>
+                  <div class="form-check">
+                    <input type="radio" id="use2ndAuthN" class="mr5" value="N" v-model="use2ndAuth"
+                      @change="saveBy2ndAuth" />
+                    <label for="use2ndAuthN">미사용</label>
+                  </div>
+                </div>
+                <!--    <p>{{ allowMultipleLogin ? '중복 로그인을 허용하고 있습니다.' : '중복 로그인을 허용하지 않습니다.' }}</p>-->
               </div>
             </div>
           </div>
@@ -68,7 +88,7 @@
 </template>
 
 <script>
-import { findAllByLoginPolicy, saveByLoginPolicy, findAllByLoginMode, saveByLoginMode } from '../../../../../resources/api/loginPolicy.js';
+import { findAllByLoginPolicy, saveByLoginPolicy, findAllByLoginMode, saveByLoginMode, findAllBy2ndAuth, saveBy2ndAuth } from '../../../../../resources/api/loginPolicy.js';
 import { getCntxtPth, saveCntxtPth } from '../../../../../resources/api/cntxtPth';
 import { cacheReSet } from "../../../../../resources/api/cacheReSet";
 import store from "../../../../../views/pages/AppStore";
@@ -82,6 +102,9 @@
 
       cntxtPth: '/', // context path 초기값
       defaultCntxtPth: null, // 현재 설정된 Context Path
+
+      use2ndAuth: null, // 2차 인증 사용 여부
+      previousUse2ndAuth: null, // 이전 2차 인증 사용 여부 저장
     }
   },
   created() {
@@ -104,6 +127,9 @@
         const res2 = await findAllByLoginMode();
         this.lgnMode = res2.data.data;
         this.previousLgnMode = this.lgnMode; // 초기 상태를 저장
+        const res3 = await findAllBy2ndAuth();
+        this.use2ndAuth = res3.data.data === true ? 'Y' : 'N';
+        this.previousUse2ndAuth = this.use2ndAuth; // 초기 상태를 저장
       } catch (err) {
         alert('설정 정보를 불러오는 데 실패했습니다.');
       }
@@ -170,7 +196,7 @@
     },
 
     // Context Path 저장
-    async fnSave() {
+    async saveByContextPath() {
       if (!this.validation()) {
         return;
       }
@@ -235,6 +261,33 @@
       }
       return true;
     },
+
+    // 2차 인증 설정 저장
+    async saveBy2ndAuth() {
+      const confirmed = confirm(
+        '2차 인증 설정을 변경하면 전체 사용자가 로그아웃됩니다.\n계속하시겠습니까?'
+      );
+      if (!confirmed) {
+        this.use2ndAuth = this.previousUse2ndAuth;
+        return;
+      }
+
+      try {
+        const email2ndAuth = {};
+        email2ndAuth.useYn = this.use2ndAuth;
+        await saveBy2ndAuth(email2ndAuth);
+        alert('이메일 2차 인증 설정이 저장되었습니다.');
+        store.commit("setStoreReset");
+        window.location = this.$filters.ctxPath('/cmslogin.page');
+      } catch (error) {
+        const errorData = error.response.data;
+          if (errorData.message != null && errorData.message != "") {
+              alert(error.response.data.message);
+          } else {
+              alert("에러가 발생했습니다.\n관리자에게 문의해주세요.");
+          }
+      }
+    },
   }
 }
 </script>
(파일 끝에 줄바꿈 문자 없음)
client/views/pages/login/AdminLogin.vue
--- client/views/pages/login/AdminLogin.vue
+++ client/views/pages/login/AdminLogin.vue
@@ -85,6 +85,7 @@
 
             // 인증 절차
             loginStep1: false, // 1차 인증
+            loginStep2: false, // 2차 인증
         };
     },
     methods: {
@@ -100,6 +101,7 @@
                 code: '',
             };
             this.loginStep1 = false;
+            this.loginStep2 = false;
         },
         checkAdminPage() {
             if (
@@ -116,7 +118,14 @@
             try {
                 const res = await loginProc(this.member);
                 if (res.status == 200) {
-                    this.memberInfo = res.data; // 인증코드 전송을 위한 이메일 정보 저장
+                    // 2차 인증에 필요한 이메일 정보가 있는지 확인
+                    if(res.data.email) {
+                        this.memberInfo = res.data; // 인증코드 전송을 위한 이메일 정보 저장
+                    // 없을 경우 2차 인증 패스
+                    } else {
+                        this.loginStep2 = true; // 2차 인증 패스
+                        this.loginSuccess(res); // 로그인 성공 처리
+                    }
                     this.loginStep1 = true; // 1차 인증 성공
                 }
             } catch (error) {
@@ -158,63 +167,64 @@
             try {
                 const res = await check2ndAuthProc(this.memberInfo);
                 if (res.status == 200) {
-                    const loginType = res.headers['login-type']; // 세션/토큰 로그인 구분
-                    if (loginType === 'J') {
-                        // JWT 방식
-                        store.commit("setAuthorization", res.headers.authorization);
-                        store.commit("setLoginMode", "J");
-                        localStorage.setItem("loginMode", "J");
-                        const base64String = store.state.authorization.split(".")[1];
-                        const base64 = base64String.replace(/-/g, "+").replace(/_/g, "/");
-                        const jsonPayload = decodeURIComponent(
-                            atob(base64).split("").map((c) => {
-                                return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
-                            }).join("")
-                        );
-                        const mbr = JSON.parse(jsonPayload);
-                        store.commit("setMbrId", mbr.mbrId);
-                        store.commit("setMbrNm", mbr.mbrNm);
-                        store.commit("setRoles", mbr.roles);
-                    } else if (loginType === 'S') {
-                        store.commit("setLoginMode", "S");
-                        localStorage.setItem("loginMode", "S");
-                        const mbr = res.data;
-                        store.commit("setAuthorization", null);
-                        store.commit("setMbrId", mbr.mbrId);
-                        store.commit("setMbrNm", mbr.mbrNm);
-                        const roles = mbr.roles.map(r => ({ authority: r.authrtCd }));
-                        store.commit("setRoles", roles);
-                    } else {
-                        alert("알 수 없는 로그인 방식입니다.");
-                        return;
-                    }
-                    const isAdmin = store.state.roles.some(role => role.authority === "ROLE_ADMIN");
-                    let url = this.restoreRedirect("redirect");
-                    if (url != null && url != "") {
-                        const ctx = store.state.contextPath;
-                        if (ctx !== "") {
-                            // redirect 값에서 Context Path 추가
-                            url = this.$filters.ctxPath(url);
-                        } else {
-                            // redirect 값에서 기존 Context Path 제거
-                            url = url.replace(/^\/[^\/]+/, ""); // 첫 번째 '/' 이후의 경로만 남김
-                        }
-                        const routeExists = this.$router.getRoutes().some(route => route.path === url);
+                    this.loginSuccess(res); // 로그인 성공 처리
+                    // const loginType = res.headers['login-type']; // 세션/토큰 로그인 구분
+                    // if (loginType === 'J') {
+                    //     // JWT 방식
+                    //     store.commit("setAuthorization", res.headers.authorization);
+                    //     store.commit("setLoginMode", "J");
+                    //     localStorage.setItem("loginMode", "J");
+                    //     const base64String = store.state.authorization.split(".")[1];
+                    //     const base64 = base64String.replace(/-/g, "+").replace(/_/g, "/");
+                    //     const jsonPayload = decodeURIComponent(
+                    //         atob(base64).split("").map((c) => {
+                    //             return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
+                    //         }).join("")
+                    //     );
+                    //     const mbr = JSON.parse(jsonPayload);
+                    //     store.commit("setMbrId", mbr.mbrId);
+                    //     store.commit("setMbrNm", mbr.mbrNm);
+                    //     store.commit("setRoles", mbr.roles);
+                    // } else if (loginType === 'S') {
+                    //     store.commit("setLoginMode", "S");
+                    //     localStorage.setItem("loginMode", "S");
+                    //     const mbr = res.data;
+                    //     store.commit("setAuthorization", null);
+                    //     store.commit("setMbrId", mbr.mbrId);
+                    //     store.commit("setMbrNm", mbr.mbrNm);
+                    //     const roles = mbr.roles.map(r => ({ authority: r.authrtCd }));
+                    //     store.commit("setRoles", roles);
+                    // } else {
+                    //     alert("알 수 없는 로그인 방식입니다.");
+                    //     return;
+                    // }
+                    // const isAdmin = store.state.roles.some(role => role.authority === "ROLE_ADMIN");
+                    // let url = this.restoreRedirect("redirect");
+                    // if (url != null && url != "") {
+                    //     const ctx = store.state.contextPath;
+                    //     if (ctx !== "") {
+                    //         // redirect 값에서 Context Path 추가
+                    //         url = this.$filters.ctxPath(url);
+                    //     } else {
+                    //         // redirect 값에서 기존 Context Path 제거
+                    //         url = url.replace(/^\/[^\/]+/, ""); // 첫 번째 '/' 이후의 경로만 남김
+                    //     }
+                    //     const routeExists = this.$router.getRoutes().some(route => route.path === url);
 
-                        if (url == this.$filters.ctxPath("/searchId.page") || url == this.$filters.ctxPath("/resetPswd.page")) {
-                            this.$router.push({ path: this.$filters.ctxPath("/main.page") });
-                        } else if (routeExists) {
-                            this.$router.push({ path: url });
-                        } else {
-                            this.$router.push({
-                                path: isAdmin ? this.$filters.ctxPath("/adm/main.page") : this.$filters.ctxPath("/")
-                            });
-                        }
-                    } else {
-                        this.$router.push({
-                            path: isAdmin ? this.$filters.ctxPath("/adm/main.page") : this.$filters.ctxPath("/")
-                        });
-                    }
+                    //     if (url == this.$filters.ctxPath("/searchId.page") || url == this.$filters.ctxPath("/resetPswd.page")) {
+                    //         this.$router.push({ path: this.$filters.ctxPath("/main.page") });
+                    //     } else if (routeExists) {
+                    //         this.$router.push({ path: url });
+                    //     } else {
+                    //         this.$router.push({
+                    //             path: isAdmin ? this.$filters.ctxPath("/adm/main.page") : this.$filters.ctxPath("/")
+                    //         });
+                    //     }
+                    // } else {
+                    //     this.$router.push({
+                    //         path: isAdmin ? this.$filters.ctxPath("/adm/main.page") : this.$filters.ctxPath("/")
+                    //     });
+                    // }
                 }
             } catch (error) {
                 const errorData = error.response.data;
@@ -224,6 +234,68 @@
                 } else {
                     alert("에러가 발생했습니다.\n관리자에게 문의해주세요.");
                 }
+            }
+        },
+
+        // 로그인 성공 시
+        loginSuccess(res) {
+            const loginType = res.headers['login-type']; // 세션/토큰 로그인 구분
+            if (loginType === 'J') {
+                // JWT 방식
+                store.commit("setAuthorization", res.headers.authorization);
+                store.commit("setLoginMode", "J");
+                localStorage.setItem("loginMode", "J");
+                const base64String = store.state.authorization.split(".")[1];
+                const base64 = base64String.replace(/-/g, "+").replace(/_/g, "/");
+                const jsonPayload = decodeURIComponent(
+                    atob(base64).split("").map((c) => {
+                        return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
+                    }).join("")
+                );
+                const mbr = JSON.parse(jsonPayload);
+                store.commit("setMbrId", mbr.mbrId);
+                store.commit("setMbrNm", mbr.mbrNm);
+                store.commit("setRoles", mbr.roles);
+            } else if (loginType === 'S') {
+                store.commit("setLoginMode", "S");
+                localStorage.setItem("loginMode", "S");
+                const mbr = res.data;
+                store.commit("setAuthorization", null);
+                store.commit("setMbrId", mbr.mbrId);
+                store.commit("setMbrNm", mbr.mbrNm);
+                const roles = mbr.roles.map(r => ({ authority: r.authrtCd }));
+                store.commit("setRoles", roles);
+            } else {
+                alert("알 수 없는 로그인 방식입니다.");
+                return;
+            }
+
+            const isAdmin = store.state.roles.some(role => role.authority === "ROLE_ADMIN");
+            let url = this.restoreRedirect("redirect");
+            if (url != null && url != "") {
+                const ctx = store.state.contextPath;
+                if (ctx !== "") {
+                    // redirect 값에서 Context Path 추가
+                    url = this.$filters.ctxPath(url);
+                } else {
+                    // redirect 값에서 기존 Context Path 제거
+                    url = url.replace(/^\/[^\/]+/, ""); // 첫 번째 '/' 이후의 경로만 남김
+                }
+                const routeExists = this.$router.getRoutes().some(route => route.path === url);
+
+                if (url == this.$filters.ctxPath("/searchId.page") || url == this.$filters.ctxPath("/resetPswd.page")) {
+                    this.$router.push({ path: this.$filters.ctxPath("/main.page") });
+                } else if (routeExists) {
+                    this.$router.push({ path: url });
+                } else {
+                    this.$router.push({
+                        path: isAdmin ? this.$filters.ctxPath("/adm/main.page") : this.$filters.ctxPath("/")
+                    });
+                }
+            } else {
+                this.$router.push({
+                    path: isAdmin ? this.$filters.ctxPath("/adm/main.page") : this.$filters.ctxPath("/")
+                });
             }
         },
 
@@ -247,7 +319,14 @@
             }
         },
     },
-    watch: {},
+    watch: {
+        // loginStep2(newVal) {
+        //     if (newVal) {
+        //         // 2차 인증이 패스되면 로그인 성공 처리
+        //         this.loginSuccess();
+        //     }
+        // },
+    },
     computed: {},
     components: {},
     created() {
Add a comment
List