
--- client/resources/api/loginPolicy.js
+++ client/resources/api/loginPolicy.js
... | ... | @@ -14,4 +14,12 @@ |
14 | 14 |
|
15 | 15 |
export const saveByLoginMode = loginMode => { |
16 | 16 |
return apiClient.post(`/admin/loginPolicy/saveLoginMode.json`, loginMode); |
17 |
+} |
|
18 |
+ |
|
19 |
+export const findAllBy2ndAuth = () => { |
|
20 |
+ return apiClient.post(`/admin/loginPolicy/findByEmail2ndAuth.json`); |
|
21 |
+} |
|
22 |
+ |
|
23 |
+export const saveBy2ndAuth = email2ndAuth => { |
|
24 |
+ return apiClient.post(`/admin/loginPolicy/saveEmail2ndAuth.json`, email2ndAuth); |
|
17 | 25 |
}(파일 끝에 줄바꿈 문자 없음) |
--- client/views/pages/adm/system/LoginPolicy/LoginPolicy.vue
+++ client/views/pages/adm/system/LoginPolicy/LoginPolicy.vue
... | ... | @@ -53,11 +53,31 @@ |
53 | 53 |
<div class="form-check"> |
54 | 54 |
<input type="text" id="cntxtPth" class="form-control sm" v-model="cntxtPth" ref="cntxtPth" /> |
55 | 55 |
</div> |
56 |
- <button class="btn sm main" @click="fnSave">저장</button> |
|
56 |
+ <button class="btn sm main" @click="saveByContextPath">저장</button> |
|
57 | 57 |
</div> |
58 | 58 |
<span class="ml10 gray"> |
59 | 59 |
<strong>/</strong> 또는 <strong>/경로</strong> 형식으로 입력하세요. |
60 | 60 |
</span> |
61 |
+ </div> |
|
62 |
+ </div> |
|
63 |
+ <div class="layout"> |
|
64 |
+ </div> |
|
65 |
+ <div class="layout"> |
|
66 |
+ <label class="form-title">이메일 2차 인증 설정</label> |
|
67 |
+ <div class="form-group"> |
|
68 |
+ <div class="check-area"> |
|
69 |
+ <div class="form-check"> |
|
70 |
+ <input type="radio" id="use2ndAuthY" class="mr5" value="Y" v-model="use2ndAuth" |
|
71 |
+ @change="saveBy2ndAuth" /> |
|
72 |
+ <label for="use2ndAuthY">사용</label> |
|
73 |
+ </div> |
|
74 |
+ <div class="form-check"> |
|
75 |
+ <input type="radio" id="use2ndAuthN" class="mr5" value="N" v-model="use2ndAuth" |
|
76 |
+ @change="saveBy2ndAuth" /> |
|
77 |
+ <label for="use2ndAuthN">미사용</label> |
|
78 |
+ </div> |
|
79 |
+ </div> |
|
80 |
+ <!-- <p>{{ allowMultipleLogin ? '중복 로그인을 허용하고 있습니다.' : '중복 로그인을 허용하지 않습니다.' }}</p>--> |
|
61 | 81 |
</div> |
62 | 82 |
</div> |
63 | 83 |
</div> |
... | ... | @@ -68,7 +88,7 @@ |
68 | 88 |
</template> |
69 | 89 |
|
70 | 90 |
<script> |
71 |
-import { findAllByLoginPolicy, saveByLoginPolicy, findAllByLoginMode, saveByLoginMode } from '../../../../../resources/api/loginPolicy.js'; |
|
91 |
+import { findAllByLoginPolicy, saveByLoginPolicy, findAllByLoginMode, saveByLoginMode, findAllBy2ndAuth, saveBy2ndAuth } from '../../../../../resources/api/loginPolicy.js'; |
|
72 | 92 |
import { getCntxtPth, saveCntxtPth } from '../../../../../resources/api/cntxtPth'; |
73 | 93 |
import { cacheReSet } from "../../../../../resources/api/cacheReSet"; |
74 | 94 |
import store from "../../../../../views/pages/AppStore"; |
... | ... | @@ -82,6 +102,9 @@ |
82 | 102 |
|
83 | 103 |
cntxtPth: '/', // context path 초기값 |
84 | 104 |
defaultCntxtPth: null, // 현재 설정된 Context Path |
105 |
+ |
|
106 |
+ use2ndAuth: null, // 2차 인증 사용 여부 |
|
107 |
+ previousUse2ndAuth: null, // 이전 2차 인증 사용 여부 저장 |
|
85 | 108 |
} |
86 | 109 |
}, |
87 | 110 |
created() { |
... | ... | @@ -104,6 +127,9 @@ |
104 | 127 |
const res2 = await findAllByLoginMode(); |
105 | 128 |
this.lgnMode = res2.data.data; |
106 | 129 |
this.previousLgnMode = this.lgnMode; // 초기 상태를 저장 |
130 |
+ const res3 = await findAllBy2ndAuth(); |
|
131 |
+ this.use2ndAuth = res3.data.data === true ? 'Y' : 'N'; |
|
132 |
+ this.previousUse2ndAuth = this.use2ndAuth; // 초기 상태를 저장 |
|
107 | 133 |
} catch (err) { |
108 | 134 |
alert('설정 정보를 불러오는 데 실패했습니다.'); |
109 | 135 |
} |
... | ... | @@ -170,7 +196,7 @@ |
170 | 196 |
}, |
171 | 197 |
|
172 | 198 |
// Context Path 저장 |
173 |
- async fnSave() { |
|
199 |
+ async saveByContextPath() { |
|
174 | 200 |
if (!this.validation()) { |
175 | 201 |
return; |
176 | 202 |
} |
... | ... | @@ -235,6 +261,33 @@ |
235 | 261 |
} |
236 | 262 |
return true; |
237 | 263 |
}, |
264 |
+ |
|
265 |
+ // 2차 인증 설정 저장 |
|
266 |
+ async saveBy2ndAuth() { |
|
267 |
+ const confirmed = confirm( |
|
268 |
+ '2차 인증 설정을 변경하면 전체 사용자가 로그아웃됩니다.\n계속하시겠습니까?' |
|
269 |
+ ); |
|
270 |
+ if (!confirmed) { |
|
271 |
+ this.use2ndAuth = this.previousUse2ndAuth; |
|
272 |
+ return; |
|
273 |
+ } |
|
274 |
+ |
|
275 |
+ try { |
|
276 |
+ const email2ndAuth = {}; |
|
277 |
+ email2ndAuth.useYn = this.use2ndAuth; |
|
278 |
+ await saveBy2ndAuth(email2ndAuth); |
|
279 |
+ alert('이메일 2차 인증 설정이 저장되었습니다.'); |
|
280 |
+ store.commit("setStoreReset"); |
|
281 |
+ window.location = this.$filters.ctxPath('/cmslogin.page'); |
|
282 |
+ } catch (error) { |
|
283 |
+ const errorData = error.response.data; |
|
284 |
+ if (errorData.message != null && errorData.message != "") { |
|
285 |
+ alert(error.response.data.message); |
|
286 |
+ } else { |
|
287 |
+ alert("에러가 발생했습니다.\n관리자에게 문의해주세요."); |
|
288 |
+ } |
|
289 |
+ } |
|
290 |
+ }, |
|
238 | 291 |
} |
239 | 292 |
} |
240 | 293 |
</script>(파일 끝에 줄바꿈 문자 없음) |
--- client/views/pages/login/AdminLogin.vue
+++ client/views/pages/login/AdminLogin.vue
... | ... | @@ -85,6 +85,7 @@ |
85 | 85 |
|
86 | 86 |
// 인증 절차 |
87 | 87 |
loginStep1: false, // 1차 인증 |
88 |
+ loginStep2: false, // 2차 인증 |
|
88 | 89 |
}; |
89 | 90 |
}, |
90 | 91 |
methods: { |
... | ... | @@ -100,6 +101,7 @@ |
100 | 101 |
code: '', |
101 | 102 |
}; |
102 | 103 |
this.loginStep1 = false; |
104 |
+ this.loginStep2 = false; |
|
103 | 105 |
}, |
104 | 106 |
checkAdminPage() { |
105 | 107 |
if ( |
... | ... | @@ -116,7 +118,14 @@ |
116 | 118 |
try { |
117 | 119 |
const res = await loginProc(this.member); |
118 | 120 |
if (res.status == 200) { |
119 |
- this.memberInfo = res.data; // 인증코드 전송을 위한 이메일 정보 저장 |
|
121 |
+ // 2차 인증에 필요한 이메일 정보가 있는지 확인 |
|
122 |
+ if(res.data.email) { |
|
123 |
+ this.memberInfo = res.data; // 인증코드 전송을 위한 이메일 정보 저장 |
|
124 |
+ // 없을 경우 2차 인증 패스 |
|
125 |
+ } else { |
|
126 |
+ this.loginStep2 = true; // 2차 인증 패스 |
|
127 |
+ this.loginSuccess(res); // 로그인 성공 처리 |
|
128 |
+ } |
|
120 | 129 |
this.loginStep1 = true; // 1차 인증 성공 |
121 | 130 |
} |
122 | 131 |
} catch (error) { |
... | ... | @@ -158,63 +167,64 @@ |
158 | 167 |
try { |
159 | 168 |
const res = await check2ndAuthProc(this.memberInfo); |
160 | 169 |
if (res.status == 200) { |
161 |
- const loginType = res.headers['login-type']; // 세션/토큰 로그인 구분 |
|
162 |
- if (loginType === 'J') { |
|
163 |
- // JWT 방식 |
|
164 |
- store.commit("setAuthorization", res.headers.authorization); |
|
165 |
- store.commit("setLoginMode", "J"); |
|
166 |
- localStorage.setItem("loginMode", "J"); |
|
167 |
- const base64String = store.state.authorization.split(".")[1]; |
|
168 |
- const base64 = base64String.replace(/-/g, "+").replace(/_/g, "/"); |
|
169 |
- const jsonPayload = decodeURIComponent( |
|
170 |
- atob(base64).split("").map((c) => { |
|
171 |
- return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2); |
|
172 |
- }).join("") |
|
173 |
- ); |
|
174 |
- const mbr = JSON.parse(jsonPayload); |
|
175 |
- store.commit("setMbrId", mbr.mbrId); |
|
176 |
- store.commit("setMbrNm", mbr.mbrNm); |
|
177 |
- store.commit("setRoles", mbr.roles); |
|
178 |
- } else if (loginType === 'S') { |
|
179 |
- store.commit("setLoginMode", "S"); |
|
180 |
- localStorage.setItem("loginMode", "S"); |
|
181 |
- const mbr = res.data; |
|
182 |
- store.commit("setAuthorization", null); |
|
183 |
- store.commit("setMbrId", mbr.mbrId); |
|
184 |
- store.commit("setMbrNm", mbr.mbrNm); |
|
185 |
- const roles = mbr.roles.map(r => ({ authority: r.authrtCd })); |
|
186 |
- store.commit("setRoles", roles); |
|
187 |
- } else { |
|
188 |
- alert("알 수 없는 로그인 방식입니다."); |
|
189 |
- return; |
|
190 |
- } |
|
191 |
- const isAdmin = store.state.roles.some(role => role.authority === "ROLE_ADMIN"); |
|
192 |
- let url = this.restoreRedirect("redirect"); |
|
193 |
- if (url != null && url != "") { |
|
194 |
- const ctx = store.state.contextPath; |
|
195 |
- if (ctx !== "") { |
|
196 |
- // redirect 값에서 Context Path 추가 |
|
197 |
- url = this.$filters.ctxPath(url); |
|
198 |
- } else { |
|
199 |
- // redirect 값에서 기존 Context Path 제거 |
|
200 |
- url = url.replace(/^\/[^\/]+/, ""); // 첫 번째 '/' 이후의 경로만 남김 |
|
201 |
- } |
|
202 |
- const routeExists = this.$router.getRoutes().some(route => route.path === url); |
|
170 |
+ this.loginSuccess(res); // 로그인 성공 처리 |
|
171 |
+ // const loginType = res.headers['login-type']; // 세션/토큰 로그인 구분 |
|
172 |
+ // if (loginType === 'J') { |
|
173 |
+ // // JWT 방식 |
|
174 |
+ // store.commit("setAuthorization", res.headers.authorization); |
|
175 |
+ // store.commit("setLoginMode", "J"); |
|
176 |
+ // localStorage.setItem("loginMode", "J"); |
|
177 |
+ // const base64String = store.state.authorization.split(".")[1]; |
|
178 |
+ // const base64 = base64String.replace(/-/g, "+").replace(/_/g, "/"); |
|
179 |
+ // const jsonPayload = decodeURIComponent( |
|
180 |
+ // atob(base64).split("").map((c) => { |
|
181 |
+ // return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2); |
|
182 |
+ // }).join("") |
|
183 |
+ // ); |
|
184 |
+ // const mbr = JSON.parse(jsonPayload); |
|
185 |
+ // store.commit("setMbrId", mbr.mbrId); |
|
186 |
+ // store.commit("setMbrNm", mbr.mbrNm); |
|
187 |
+ // store.commit("setRoles", mbr.roles); |
|
188 |
+ // } else if (loginType === 'S') { |
|
189 |
+ // store.commit("setLoginMode", "S"); |
|
190 |
+ // localStorage.setItem("loginMode", "S"); |
|
191 |
+ // const mbr = res.data; |
|
192 |
+ // store.commit("setAuthorization", null); |
|
193 |
+ // store.commit("setMbrId", mbr.mbrId); |
|
194 |
+ // store.commit("setMbrNm", mbr.mbrNm); |
|
195 |
+ // const roles = mbr.roles.map(r => ({ authority: r.authrtCd })); |
|
196 |
+ // store.commit("setRoles", roles); |
|
197 |
+ // } else { |
|
198 |
+ // alert("알 수 없는 로그인 방식입니다."); |
|
199 |
+ // return; |
|
200 |
+ // } |
|
201 |
+ // const isAdmin = store.state.roles.some(role => role.authority === "ROLE_ADMIN"); |
|
202 |
+ // let url = this.restoreRedirect("redirect"); |
|
203 |
+ // if (url != null && url != "") { |
|
204 |
+ // const ctx = store.state.contextPath; |
|
205 |
+ // if (ctx !== "") { |
|
206 |
+ // // redirect 값에서 Context Path 추가 |
|
207 |
+ // url = this.$filters.ctxPath(url); |
|
208 |
+ // } else { |
|
209 |
+ // // redirect 값에서 기존 Context Path 제거 |
|
210 |
+ // url = url.replace(/^\/[^\/]+/, ""); // 첫 번째 '/' 이후의 경로만 남김 |
|
211 |
+ // } |
|
212 |
+ // const routeExists = this.$router.getRoutes().some(route => route.path === url); |
|
203 | 213 |
|
204 |
- if (url == this.$filters.ctxPath("/searchId.page") || url == this.$filters.ctxPath("/resetPswd.page")) { |
|
205 |
- this.$router.push({ path: this.$filters.ctxPath("/main.page") }); |
|
206 |
- } else if (routeExists) { |
|
207 |
- this.$router.push({ path: url }); |
|
208 |
- } else { |
|
209 |
- this.$router.push({ |
|
210 |
- path: isAdmin ? this.$filters.ctxPath("/adm/main.page") : this.$filters.ctxPath("/") |
|
211 |
- }); |
|
212 |
- } |
|
213 |
- } else { |
|
214 |
- this.$router.push({ |
|
215 |
- path: isAdmin ? this.$filters.ctxPath("/adm/main.page") : this.$filters.ctxPath("/") |
|
216 |
- }); |
|
217 |
- } |
|
214 |
+ // if (url == this.$filters.ctxPath("/searchId.page") || url == this.$filters.ctxPath("/resetPswd.page")) { |
|
215 |
+ // this.$router.push({ path: this.$filters.ctxPath("/main.page") }); |
|
216 |
+ // } else if (routeExists) { |
|
217 |
+ // this.$router.push({ path: url }); |
|
218 |
+ // } else { |
|
219 |
+ // this.$router.push({ |
|
220 |
+ // path: isAdmin ? this.$filters.ctxPath("/adm/main.page") : this.$filters.ctxPath("/") |
|
221 |
+ // }); |
|
222 |
+ // } |
|
223 |
+ // } else { |
|
224 |
+ // this.$router.push({ |
|
225 |
+ // path: isAdmin ? this.$filters.ctxPath("/adm/main.page") : this.$filters.ctxPath("/") |
|
226 |
+ // }); |
|
227 |
+ // } |
|
218 | 228 |
} |
219 | 229 |
} catch (error) { |
220 | 230 |
const errorData = error.response.data; |
... | ... | @@ -224,6 +234,68 @@ |
224 | 234 |
} else { |
225 | 235 |
alert("에러가 발생했습니다.\n관리자에게 문의해주세요."); |
226 | 236 |
} |
237 |
+ } |
|
238 |
+ }, |
|
239 |
+ |
|
240 |
+ // 로그인 성공 시 |
|
241 |
+ loginSuccess(res) { |
|
242 |
+ const loginType = res.headers['login-type']; // 세션/토큰 로그인 구분 |
|
243 |
+ if (loginType === 'J') { |
|
244 |
+ // JWT 방식 |
|
245 |
+ store.commit("setAuthorization", res.headers.authorization); |
|
246 |
+ store.commit("setLoginMode", "J"); |
|
247 |
+ localStorage.setItem("loginMode", "J"); |
|
248 |
+ const base64String = store.state.authorization.split(".")[1]; |
|
249 |
+ const base64 = base64String.replace(/-/g, "+").replace(/_/g, "/"); |
|
250 |
+ const jsonPayload = decodeURIComponent( |
|
251 |
+ atob(base64).split("").map((c) => { |
|
252 |
+ return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2); |
|
253 |
+ }).join("") |
|
254 |
+ ); |
|
255 |
+ const mbr = JSON.parse(jsonPayload); |
|
256 |
+ store.commit("setMbrId", mbr.mbrId); |
|
257 |
+ store.commit("setMbrNm", mbr.mbrNm); |
|
258 |
+ store.commit("setRoles", mbr.roles); |
|
259 |
+ } else if (loginType === 'S') { |
|
260 |
+ store.commit("setLoginMode", "S"); |
|
261 |
+ localStorage.setItem("loginMode", "S"); |
|
262 |
+ const mbr = res.data; |
|
263 |
+ store.commit("setAuthorization", null); |
|
264 |
+ store.commit("setMbrId", mbr.mbrId); |
|
265 |
+ store.commit("setMbrNm", mbr.mbrNm); |
|
266 |
+ const roles = mbr.roles.map(r => ({ authority: r.authrtCd })); |
|
267 |
+ store.commit("setRoles", roles); |
|
268 |
+ } else { |
|
269 |
+ alert("알 수 없는 로그인 방식입니다."); |
|
270 |
+ return; |
|
271 |
+ } |
|
272 |
+ |
|
273 |
+ const isAdmin = store.state.roles.some(role => role.authority === "ROLE_ADMIN"); |
|
274 |
+ let url = this.restoreRedirect("redirect"); |
|
275 |
+ if (url != null && url != "") { |
|
276 |
+ const ctx = store.state.contextPath; |
|
277 |
+ if (ctx !== "") { |
|
278 |
+ // redirect 값에서 Context Path 추가 |
|
279 |
+ url = this.$filters.ctxPath(url); |
|
280 |
+ } else { |
|
281 |
+ // redirect 값에서 기존 Context Path 제거 |
|
282 |
+ url = url.replace(/^\/[^\/]+/, ""); // 첫 번째 '/' 이후의 경로만 남김 |
|
283 |
+ } |
|
284 |
+ const routeExists = this.$router.getRoutes().some(route => route.path === url); |
|
285 |
+ |
|
286 |
+ if (url == this.$filters.ctxPath("/searchId.page") || url == this.$filters.ctxPath("/resetPswd.page")) { |
|
287 |
+ this.$router.push({ path: this.$filters.ctxPath("/main.page") }); |
|
288 |
+ } else if (routeExists) { |
|
289 |
+ this.$router.push({ path: url }); |
|
290 |
+ } else { |
|
291 |
+ this.$router.push({ |
|
292 |
+ path: isAdmin ? this.$filters.ctxPath("/adm/main.page") : this.$filters.ctxPath("/") |
|
293 |
+ }); |
|
294 |
+ } |
|
295 |
+ } else { |
|
296 |
+ this.$router.push({ |
|
297 |
+ path: isAdmin ? this.$filters.ctxPath("/adm/main.page") : this.$filters.ctxPath("/") |
|
298 |
+ }); |
|
227 | 299 |
} |
228 | 300 |
}, |
229 | 301 |
|
... | ... | @@ -247,7 +319,14 @@ |
247 | 319 |
} |
248 | 320 |
}, |
249 | 321 |
}, |
250 |
- watch: {}, |
|
322 |
+ watch: { |
|
323 |
+ // loginStep2(newVal) { |
|
324 |
+ // if (newVal) { |
|
325 |
+ // // 2차 인증이 패스되면 로그인 성공 처리 |
|
326 |
+ // this.loginSuccess(); |
|
327 |
+ // } |
|
328 |
+ // }, |
|
329 |
+ }, |
|
251 | 330 |
computed: {}, |
252 | 331 |
components: {}, |
253 | 332 |
created() { |
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?