

250528 김혜민 oauth2 로그인 수정
@c4a337881995f0cedd243fb56c022e9354cf8cda
--- client/resources/api/login.js
+++ client/resources/api/login.js
... | ... | @@ -1,20 +1,19 @@ |
1 | 1 |
import apiClient from "./index"; |
2 | 2 |
|
3 |
+// 일반 로그인 |
|
3 | 4 |
export const loginProc = mber => { |
4 | 5 |
return apiClient.post(`/mbr/loginProc.json`, mber); |
5 | 6 |
} |
6 | 7 |
|
8 |
+// 사용자 정보 조회 - OAuth2에서도 사용 |
|
9 |
+export const getUserInfo = () => { |
|
10 |
+ return apiClient.post(`/oauth2/getUserInfo.json`); |
|
11 |
+} |
|
12 |
+ |
|
13 |
+// OAuth2 로그인 - 서버로 리다이렉트 |
|
7 | 14 |
export const oauthLogin = (provider) => { |
8 |
- const { API_SERVER_HOST } = require("../../../Global"); |
|
9 |
- const oauthUrl = `//${API_SERVER_HOST}/oauth2/login?provider=${provider}`; |
|
10 |
- window.location.href = oauthUrl; |
|
11 |
-}; |
|
12 |
- |
|
13 |
-export const getOAuthUrl = (provider) => { |
|
14 |
- const { API_SERVER_HOST } = require("../../../Global"); |
|
15 |
- return `//${API_SERVER_HOST}/oauth2/login?provider=${provider}`; |
|
16 |
-}; |
|
17 |
- |
|
18 |
-export const getOAuthUserInfo = () => { |
|
19 |
- return apiClient.get(`/oauth2/user-info`); |
|
15 |
+ // 현재 도메인 유지하면서 OAuth2 엔드포인트로 이동 |
|
16 |
+ const { API_SERVER_HOST } = require("../../../Global"); |
|
17 |
+ const oauthUrl = `//${API_SERVER_HOST}/oauth2/login?provider=${provider}`; |
|
18 |
+ window.location.href = oauthUrl; |
|
20 | 19 |
};(파일 끝에 줄바꿈 문자 없음) |
--- client/views/pages/login/Login.vue
+++ client/views/pages/login/Login.vue
... | ... | @@ -44,7 +44,7 @@ |
44 | 44 |
class="btn md main user-btn" |
45 | 45 |
v-if="!isAdminPage" |
46 | 46 |
@click="fnLogin" |
47 |
- @keydown.enter="fnLogin" |
|
47 |
+ @keydown.enter="fnLogin" |
|
48 | 48 |
> |
49 | 49 |
로그인 |
50 | 50 |
</button> |
... | ... | @@ -64,267 +64,343 @@ |
64 | 64 |
<p class="pl10 pr10 cursor" @click="moveSearchId">아이디찾기</p> |
65 | 65 |
<p class="pl10 pr0 cursor" @click="moveResetPswd">비밀번호 초기화</p> |
66 | 66 |
</div> |
67 |
- <!-- <div class="oauth-login-section" v-if="!isAdminPage"> |
|
68 |
- <div class="oauth-divider"> |
|
67 |
+ |
|
68 |
+ <!-- OAuth2 로그인 버튼들 --> |
|
69 |
+ <div v-if="!isAdminPage"> |
|
70 |
+ <div> |
|
69 | 71 |
<span>또는</span> |
70 | 72 |
</div> |
71 |
- |
|
72 |
- <div class="oauth-buttons"> |
|
73 |
- <button class="oauth-btn kakao-btn" @click="fnOAuthLogin('kakao')"> |
|
74 |
- <img src="/images/kakao-icon.png" alt="카카오" class="oauth-icon" /> |
|
73 |
+ <div> |
|
74 |
+ <button @click="fnOAuthLogin('kakao')" :disabled="isOAuthLoading"> |
|
75 | 75 |
카카오로 로그인 |
76 | 76 |
</button> |
77 | 77 |
|
78 |
- <button class="oauth-btn naver-btn" @click="fnOAuthLogin('naver')"> |
|
79 |
- <img src="/images/naver-icon.png" alt="네이버" class="oauth-icon" /> |
|
78 |
+ <button @click="fnOAuthLogin('naver')" :disabled="isOAuthLoading"> |
|
80 | 79 |
네이버로 로그인 |
81 | 80 |
</button> |
82 | 81 |
|
83 |
- <button class="oauth-btn google-btn" @click="fnOAuthLogin('google')"> |
|
84 |
- <img src="/images/google-icon.png" alt="구글" class="oauth-icon" /> |
|
82 |
+ <button @click="fnOAuthLogin('google')" :disabled="isOAuthLoading"> |
|
85 | 83 |
구글로 로그인 |
86 | 84 |
</button> |
87 | 85 |
</div> |
88 |
- </div> --> |
|
86 |
+ </div> |
|
87 |
+ </div> |
|
89 | 88 |
</div> |
90 | 89 |
</div> |
91 | 90 |
</div> |
92 |
- </div> |
|
93 | 91 |
</template> |
94 | 92 |
|
95 | 93 |
<script> |
96 | 94 |
import { useStore } from "vuex"; |
97 | 95 |
import store from "../AppStore"; |
98 |
-import { loginProc, oauthLogin, getOAuthUserInfo } from "../../../resources/api/login"; |
|
96 |
+import { loginProc, getUserInfo, oauthLogin } from "../../../resources/api/login"; |
|
99 | 97 |
import queryParams from "../../../resources/js/queryParams"; |
100 | 98 |
|
101 | 99 |
export default { |
102 | 100 |
mixins: [queryParams], |
103 |
- data: () => { |
|
101 |
+ |
|
102 |
+ data() { |
|
104 | 103 |
return { |
105 |
- member: { |
|
106 |
- lgnId: null, |
|
107 |
- pswd: null, |
|
108 |
- }, |
|
104 |
+ member: { lgnId: null, pswd: null }, |
|
109 | 105 |
store: useStore(), |
110 | 106 |
isAdminPage: false, |
107 |
+ isOAuthLoading: false, |
|
108 |
+ // oauthProviders: [ |
|
109 |
+ // { name: 'kakao', label: '카카오로 로그인' }, |
|
110 |
+ // { name: 'naver', label: '네이버로 로그인' }, |
|
111 |
+ // { name: 'google', label: '구글로 로그인' } |
|
112 |
+ // ] |
|
111 | 113 |
}; |
112 | 114 |
}, |
113 |
- methods: { |
|
114 |
- checkAdminPage() { |
|
115 |
- if ( |
|
116 |
- this.restoreRedirect("redirect") && |
|
117 |
- this.restoreRedirect("redirect").includes("/adm/") |
|
118 |
- ) { |
|
119 |
- this.isAdminPage = true; |
|
120 |
- } else { |
|
121 |
- this.isAdminPage = false; |
|
122 |
- } |
|
123 |
- }, |
|
124 |
- |
|
125 |
- // OAuth2 로그인 처리 (API 모듈 사용) |
|
126 |
- fnOAuthLogin(provider) { |
|
127 |
- const redirectUrl = this.restoreRedirect("redirect") || this.$route.fullPath; |
|
128 |
- sessionStorage.setItem('oauth_redirect', redirectUrl); |
|
129 |
- |
|
130 |
- oauthLogin(provider); |
|
115 |
+ |
|
116 |
+ async created() { |
|
117 |
+ this.checkAdminPage(); |
|
131 | 118 |
}, |
119 |
+ |
|
120 |
+ async mounted() { |
|
121 |
+ await this.$nextTick(); |
|
122 |
+ if (this.hasOAuthParams()) { |
|
123 |
+ await this.handleOAuthCallback(); |
|
124 |
+ } |
|
125 |
+ }, |
|
126 |
+ |
|
127 |
+ beforeUnmount() { |
|
128 |
+ this.isOAuthLoading = false; |
|
129 |
+ }, |
|
130 |
+ |
|
131 |
+ watch: { |
|
132 |
+ '$route'(to) { |
|
133 |
+ if (to.query.oauth_success || to.query.error) { |
|
134 |
+ this.handleOAuthCallback(); |
|
135 |
+ } |
|
136 |
+ } |
|
137 |
+ }, |
|
138 |
+ |
|
139 |
+ methods: { |
|
140 |
+ // ========== 초기화 및 유틸리티 ========== |
|
141 |
+ checkAdminPage() { |
|
142 |
+ const redirect = this.restoreRedirect("redirect"); |
|
143 |
+ this.isAdminPage = redirect && redirect.includes("/adm/"); |
|
144 |
+ }, |
|
145 |
+ |
|
146 |
+ hasOAuthParams() { |
|
147 |
+ return window.location.search.includes('oauth_success') || |
|
148 |
+ window.location.search.includes('error') || |
|
149 |
+ this.$route.query.oauth_success || |
|
150 |
+ this.$route.query.error; |
|
151 |
+ }, |
|
152 |
+ |
|
153 |
+ // ========== 일반 로그인 ========== |
|
132 | 154 |
async fnLogin() { |
133 | 155 |
try { |
134 | 156 |
const res = await loginProc(this.member); |
135 |
- if (res.status == 200) { |
|
136 |
- const loginType = res.headers['login-type']; // 세션/토큰 로그인 구분 |
|
137 |
- |
|
138 |
- if (loginType === 'J') { |
|
139 |
- // JWT 방식 |
|
140 |
- const token = res.headers.authorization; |
|
141 |
- store.commit("setAuthorization", token); |
|
142 |
- store.commit("setLoginMode", "J"); |
|
143 |
- |
|
144 |
- // localStorage에도 저장 |
|
145 |
- localStorage.setItem("authorization", token); |
|
146 |
- localStorage.setItem("loginMode", "J"); |
|
147 |
- |
|
148 |
- const base64String = token.split(".")[1]; |
|
149 |
- const base64 = base64String.replace(/-/g, "+").replace(/_/g, "/"); |
|
150 |
- const jsonPayload = decodeURIComponent( |
|
151 |
- atob(base64).split("").map((c) => { |
|
152 |
- return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2); |
|
153 |
- }).join("") |
|
154 |
- ); |
|
155 |
- const mbr = JSON.parse(jsonPayload); |
|
156 |
- |
|
157 |
- store.commit("setMbrId", mbr.mbrId); |
|
158 |
- store.commit("setMbrNm", mbr.mbrNm); |
|
159 |
- store.commit("setRoles", mbr.roles); |
|
160 |
- |
|
161 |
- // localStorage에 사용자 정보도 저장 |
|
162 |
- localStorage.setItem("mbrId", mbr.mbrId); |
|
163 |
- localStorage.setItem("mbrNm", mbr.mbrNm); |
|
164 |
- localStorage.setItem("roles", JSON.stringify(mbr.roles)); |
|
165 |
- |
|
166 |
- } else if (loginType === 'S') { |
|
167 |
- store.commit("setLoginMode", "S"); |
|
168 |
- localStorage.setItem("loginMode", "S"); |
|
169 |
- const mbr = res.data; |
|
170 |
- store.commit("setAuthorization", null); |
|
171 |
- store.commit("setMbrId", mbr.mbrId); |
|
172 |
- store.commit("setMbrNm", mbr.mbrNm); |
|
173 |
- const roles = mbr.roles.map(r => ({ authority: r.authrtCd })); |
|
174 |
- store.commit("setRoles", roles); |
|
175 |
- |
|
176 |
- // localStorage에 세션 정보도 저장 |
|
177 |
- localStorage.setItem("mbrId", mbr.mbrId); |
|
178 |
- localStorage.setItem("mbrNm", mbr.mbrNm); |
|
179 |
- localStorage.setItem("roles", JSON.stringify(roles)); |
|
180 |
- |
|
181 |
- } else { |
|
182 |
- alert("알 수 없는 로그인 방식입니다."); |
|
183 |
- return; |
|
184 |
- } |
|
157 |
+ if (res.status !== 200) return; |
|
185 | 158 |
|
186 |
- // 페이지 이동 처리 |
|
187 |
- await this.handleLoginSuccess(); |
|
159 |
+ const loginType = res.headers['login-type']; |
|
160 |
+ |
|
161 |
+ if (loginType === 'J') { |
|
162 |
+ this.handleJWTLogin(res); |
|
163 |
+ } else if (loginType === 'S') { |
|
164 |
+ this.handleSessionLogin(res); |
|
165 |
+ } else { |
|
166 |
+ alert("알 수 없는 로그인 방식입니다."); |
|
167 |
+ return; |
|
188 | 168 |
} |
169 |
+ |
|
170 |
+ await this.handleLoginSuccess(); |
|
171 |
+ |
|
189 | 172 |
} catch (error) { |
190 |
- console.error("로그인 에러:", error); |
|
191 | 173 |
alert(error.response?.data?.message || "로그인에 실패했습니다."); |
192 | 174 |
} |
193 | 175 |
}, |
194 | 176 |
|
195 |
- // 로그인 성공 후 페이지 이동 처리 |
|
196 |
- async handleLoginSuccess() { |
|
197 |
- const isAdmin = store.state.roles.some(role => role.authority === "ROLE_ADMIN"); |
|
198 |
- let redirectUrl = this.restoreRedirect("redirect"); |
|
177 |
+ handleJWTLogin(res) { |
|
178 |
+ const token = res.headers.authorization; |
|
179 |
+ const userInfo = this.parseJWT(token); |
|
199 | 180 |
|
200 |
- if (redirectUrl && redirectUrl !== "") { |
|
201 |
- // 특정 페이지들은 메인으로 리다이렉트 |
|
202 |
- if (redirectUrl.includes("/searchId.page") || redirectUrl.includes("/resetPswd.page") || redirectUrl.includes("/login.page")) { |
|
203 |
- redirectUrl = this.$filters.ctxPath("/"); |
|
204 |
- } |
|
205 |
- |
|
206 |
- // Context Path 처리 |
|
207 |
- if (!redirectUrl.startsWith(store.state.contextPath) && store.state.contextPath) { |
|
208 |
- redirectUrl = this.$filters.ctxPath(redirectUrl); |
|
209 |
- } |
|
210 |
- |
|
211 |
- |
|
212 |
- // 라우터에 해당 경로가 존재하는지 확인 |
|
213 |
- const routeExists = this.$router.getRoutes().some(route => route.path === redirectUrl); |
|
214 |
- |
|
215 |
- if (routeExists) { |
|
216 |
- await this.$nextTick(); |
|
217 |
- this.$router.push({ path: redirectUrl }); |
|
218 |
- } else { |
|
219 |
- const defaultPath = isAdmin ? this.$filters.ctxPath("/adm/main.page") : this.$filters.ctxPath("/"); |
|
220 |
- this.$router.push({ path: defaultPath }); |
|
221 |
- } |
|
181 |
+ this.setAuthInfo("J", token, userInfo); |
|
182 |
+ }, |
|
183 |
+ |
|
184 |
+ handleSessionLogin(res) { |
|
185 |
+ const userInfo = res.data; |
|
186 |
+ const roles = userInfo.roles.map(r => ({ authority: r.authrtCd })); |
|
187 |
+ |
|
188 |
+ this.setAuthInfo("S", null, { ...userInfo, roles }); |
|
189 |
+ }, |
|
190 |
+ |
|
191 |
+ parseJWT(token) { |
|
192 |
+ const base64String = token.split(".")[1]; |
|
193 |
+ const base64 = base64String.replace(/-/g, "+").replace(/_/g, "/"); |
|
194 |
+ const jsonPayload = decodeURIComponent( |
|
195 |
+ atob(base64).split("").map((c) => { |
|
196 |
+ return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2); |
|
197 |
+ }).join("") |
|
198 |
+ ); |
|
199 |
+ return JSON.parse(jsonPayload); |
|
200 |
+ }, |
|
201 |
+ |
|
202 |
+ setAuthInfo(loginMode, token, userInfo) { |
|
203 |
+ // Vuex 상태 저장 |
|
204 |
+ store.commit("setLoginMode", loginMode); |
|
205 |
+ store.commit("setAuthorization", token); |
|
206 |
+ store.commit("setMbrId", userInfo.mbrId); |
|
207 |
+ store.commit("setMbrNm", userInfo.mbrNm); |
|
208 |
+ store.commit("setRoles", userInfo.roles); |
|
209 |
+ |
|
210 |
+ // localStorage 저장 |
|
211 |
+ localStorage.setItem("loginMode", loginMode); |
|
212 |
+ localStorage.setItem("mbrId", userInfo.mbrId); |
|
213 |
+ localStorage.setItem("mbrNm", userInfo.mbrNm); |
|
214 |
+ localStorage.setItem("roles", JSON.stringify(userInfo.roles)); |
|
215 |
+ |
|
216 |
+ if (token) { |
|
217 |
+ localStorage.setItem("authorization", token); |
|
222 | 218 |
} else { |
223 |
- const defaultPath = isAdmin ? this.$filters.ctxPath("/adm/main.page") : this.$filters.ctxPath("/"); |
|
224 |
- await this.$nextTick(); |
|
225 |
- this.$router.push({ path: defaultPath }); |
|
219 |
+ localStorage.removeItem("authorization"); |
|
220 |
+ } |
|
221 |
+ }, |
|
222 |
+ |
|
223 |
+ // ========== OAuth2 로그인 ========== |
|
224 |
+ fnOAuthLogin(provider) { |
|
225 |
+ const redirectUrl = this.restoreRedirect("redirect") || this.$route.fullPath; |
|
226 |
+ sessionStorage.setItem('oauth_redirect', redirectUrl); |
|
227 |
+ oauthLogin(provider); |
|
228 |
+ }, |
|
229 |
+ |
|
230 |
+ async handleOAuthCallback() { |
|
231 |
+ const { error, errorMessage, oauthSuccess, loginMode } = this.parseOAuthParams(); |
|
232 |
+ |
|
233 |
+ if (error) { |
|
234 |
+ this.handleOAuthError(error, errorMessage); |
|
235 |
+ return; |
|
226 | 236 |
} |
227 | 237 |
|
228 |
- // redirect 세션 정리 |
|
238 |
+ if (oauthSuccess !== 'true' && oauthSuccess !== true) return; |
|
239 |
+ |
|
240 |
+ try { |
|
241 |
+ store.commit("setLoginMode", loginMode || "J"); |
|
242 |
+ localStorage.setItem("loginMode", loginMode || "J"); |
|
243 |
+ |
|
244 |
+ if (loginMode === 'J') { |
|
245 |
+ await this.handleOAuthJWT(); |
|
246 |
+ } else if (loginMode === 'S') { |
|
247 |
+ await this.handleOAuthSession(); |
|
248 |
+ } else { |
|
249 |
+ throw new Error('알 수 없는 로그인 모드: ' + loginMode); |
|
250 |
+ } |
|
251 |
+ |
|
252 |
+ this.cleanupOAuth(); |
|
253 |
+ await this.$nextTick(); |
|
254 |
+ await this.handleLoginSuccess(); |
|
255 |
+ |
|
256 |
+ } catch (error) { |
|
257 |
+ this.handleOAuthError('processing_error', error.message); |
|
258 |
+ } |
|
259 |
+ }, |
|
260 |
+ |
|
261 |
+ parseOAuthParams() { |
|
262 |
+ const urlParams = new URLSearchParams(window.location.search); |
|
263 |
+ const routeQuery = this.$route.query; |
|
264 |
+ |
|
265 |
+ return { |
|
266 |
+ error: urlParams.get('error') || routeQuery.error, |
|
267 |
+ errorMessage: urlParams.get('message') || routeQuery.message, |
|
268 |
+ oauthSuccess: urlParams.get('oauth_success') || routeQuery.oauth_success, |
|
269 |
+ loginMode: urlParams.get('loginMode') || routeQuery.loginMode |
|
270 |
+ }; |
|
271 |
+ }, |
|
272 |
+ |
|
273 |
+ async handleOAuthJWT() { |
|
274 |
+ const oauthToken = this.getCookie('oauth_access_token'); |
|
275 |
+ if (!oauthToken) { |
|
276 |
+ throw new Error('OAuth 토큰을 찾을 수 없습니다.'); |
|
277 |
+ } |
|
278 |
+ |
|
279 |
+ const fullToken = oauthToken.startsWith('Bearer ') ? oauthToken : `Bearer ${oauthToken}`; |
|
280 |
+ store.commit("setAuthorization", fullToken); |
|
281 |
+ localStorage.setItem("authorization", fullToken); |
|
282 |
+ this.deleteCookie('oauth_access_token'); |
|
283 |
+ |
|
284 |
+ try { |
|
285 |
+ const userInfo = this.parseJWT(fullToken.replace('Bearer ', '')); |
|
286 |
+ this.setAuthInfo("J", fullToken, userInfo); |
|
287 |
+ } catch (jwtError) { |
|
288 |
+ await this.fetchUserInfoFromServer(); |
|
289 |
+ } |
|
290 |
+ }, |
|
291 |
+ |
|
292 |
+ async handleOAuthSession() { |
|
293 |
+ store.commit("setAuthorization", null); |
|
294 |
+ localStorage.removeItem("authorization"); |
|
295 |
+ this.deleteCookie('oauth_access_token'); |
|
296 |
+ |
|
297 |
+ await this.fetchUserInfoFromServer(); |
|
298 |
+ }, |
|
299 |
+ |
|
300 |
+ async fetchUserInfoFromServer() { |
|
301 |
+ const userInfoRes = await getUserInfo(); |
|
302 |
+ if (!userInfoRes || userInfoRes.status !== 200) { |
|
303 |
+ throw new Error('사용자 정보를 가져올 수 없습니다.'); |
|
304 |
+ } |
|
305 |
+ |
|
306 |
+ const userInfo = userInfoRes.data.data; |
|
307 |
+ const roles = Array.isArray(userInfo.roles) ? |
|
308 |
+ userInfo.roles.map(r => ({ authority: r.authrtCd || r.authority })) : |
|
309 |
+ userInfo.roles; |
|
310 |
+ |
|
311 |
+ const loginMode = localStorage.getItem("loginMode"); |
|
312 |
+ const token = userInfo.token || localStorage.getItem("authorization"); |
|
313 |
+ |
|
314 |
+ this.setAuthInfo(loginMode, token, { ...userInfo, roles }); |
|
315 |
+ }, |
|
316 |
+ |
|
317 |
+ // ========== 로그인 성공 후 처리 ========== |
|
318 |
+ async handleLoginSuccess() { |
|
319 |
+ const isAdmin = store.state.roles.some(role => role.authority === "ROLE_ADMIN"); |
|
320 |
+ let redirectUrl = this.restoreRedirect("redirect") || sessionStorage.getItem('oauth_redirect'); |
|
321 |
+ |
|
322 |
+ // 리다이렉트 URL 정리 |
|
323 |
+ if (redirectUrl && this.shouldRedirectToMain(redirectUrl)) { |
|
324 |
+ redirectUrl = this.$filters.ctxPath("/"); |
|
325 |
+ } |
|
326 |
+ |
|
327 |
+ // Context Path 처리 |
|
328 |
+ if (redirectUrl && !redirectUrl.startsWith(store.state.contextPath) && store.state.contextPath) { |
|
329 |
+ redirectUrl = this.$filters.ctxPath(redirectUrl); |
|
330 |
+ } |
|
331 |
+ |
|
332 |
+ // 라우터 존재 여부 확인 후 이동 |
|
333 |
+ const targetPath = this.getValidRedirectPath(redirectUrl, isAdmin); |
|
334 |
+ await this.$nextTick(); |
|
335 |
+ this.$router.push({ path: targetPath }); |
|
336 |
+ |
|
337 |
+ // 세션 정리 |
|
229 | 338 |
sessionStorage.removeItem("redirect"); |
339 |
+ sessionStorage.removeItem("oauth_redirect"); |
|
340 |
+ }, |
|
341 |
+ |
|
342 |
+ shouldRedirectToMain(url) { |
|
343 |
+ return url.includes("/searchId.page") || |
|
344 |
+ url.includes("/resetPswd.page") || |
|
345 |
+ url.includes("/login.page"); |
|
346 |
+ }, |
|
347 |
+ |
|
348 |
+ getValidRedirectPath(redirectUrl, isAdmin) { |
|
349 |
+ if (redirectUrl) { |
|
350 |
+ const routeExists = this.$router.getRoutes().some(route => route.path === redirectUrl); |
|
351 |
+ if (routeExists) return redirectUrl; |
|
352 |
+ } |
|
353 |
+ |
|
354 |
+ return isAdmin ? |
|
355 |
+ this.$filters.ctxPath("/adm/main.page") : |
|
356 |
+ this.$filters.ctxPath("/"); |
|
357 |
+ }, |
|
358 |
+ |
|
359 |
+ // ========== 에러 처리 및 정리 ========== |
|
360 |
+ handleOAuthError(error, errorMessage) { |
|
361 |
+ this.isOAuthLoading = false; |
|
362 |
+ this.cleanupOAuth(); |
|
363 |
+ |
|
364 |
+ const message = decodeURIComponent(errorMessage || error || 'OAuth 로그인에 실패했습니다.'); |
|
365 |
+ alert(`소셜 로그인 실패: ${message}`); |
|
366 |
+ |
|
367 |
+ this.$router.push({ path: this.$filters.ctxPath("/login.page") }); |
|
368 |
+ }, |
|
369 |
+ |
|
370 |
+ cleanupOAuth() { |
|
371 |
+ sessionStorage.removeItem('oauth_redirect'); |
|
372 |
+ sessionStorage.removeItem('oauth_provider'); |
|
373 |
+ sessionStorage.removeItem('oauth_start_time'); |
|
374 |
+ this.isOAuthLoading = false; |
|
375 |
+ |
|
376 |
+ const cleanUrl = window.location.pathname; |
|
377 |
+ window.history.replaceState({}, document.title, cleanUrl); |
|
378 |
+ }, |
|
379 |
+ |
|
380 |
+ // ========== 유틸리티 메서드 ========== |
|
381 |
+ getCookie(name) { |
|
382 |
+ const value = `; ${document.cookie}`; |
|
383 |
+ const parts = value.split(`; ${name}=`); |
|
384 |
+ return parts.length === 2 ? parts.pop().split(';').shift() : null; |
|
385 |
+ }, |
|
386 |
+ |
|
387 |
+ deleteCookie(name) { |
|
388 |
+ document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`; |
|
230 | 389 |
}, |
231 | 390 |
|
232 | 391 |
moveSearchId() { |
233 | 392 |
this.$router.push({ |
234 | 393 |
path: this.$filters.ctxPath("/resetPswd.page"), |
235 |
- query: { |
|
236 |
- tab: "id", |
|
237 |
- }, |
|
238 |
- }); |
|
239 |
- }, |
|
240 |
- moveResetPswd() { |
|
241 |
- this.$router.push({ |
|
242 |
- path: this.$filters.ctxPath("/resetPswd.page"), |
|
243 |
- query: { |
|
244 |
- tab: "pw", |
|
245 |
- }, |
|
394 |
+ query: { tab: "id" } |
|
246 | 395 |
}); |
247 | 396 |
}, |
248 | 397 |
|
249 |
- // OAuth2 로그인 성공 후 처리 |
|
250 |
-async handleOAuthCallback() { |
|
251 |
- const urlParams = new URLSearchParams(window.location.search); |
|
252 |
- const error = urlParams.get('error'); |
|
253 |
- |
|
254 |
- if (error) { |
|
255 |
- alert('소셜 로그인에 실패했습니다: ' + decodeURIComponent(urlParams.get('message') || error)); |
|
256 |
- return; |
|
257 |
- } |
|
258 |
- |
|
259 |
- // URL에 OAuth 관련 파라미터가 있는 경우 |
|
260 |
- if (this.$route.query.oauth_success) { |
|
261 |
- try { |
|
262 |
- console.log("OAuth2 로그인 성공! 간단한 세션 처리를 시작합니다."); |
|
263 |
- |
|
264 |
- // OAuth는 일반적으로 세션 방식이므로 세션으로 설정 |
|
265 |
- store.commit("setLoginMode", "S"); |
|
266 |
- localStorage.setItem("loginMode", "S"); |
|
267 |
- |
|
268 |
- // 기본 사용자 정보 설정 - 서버에서 세션에 저장된 정보 사용 |
|
269 |
- // 실제 사용자 정보는 다른 API나 페이지 로드 시 가져올 수 있음 |
|
270 |
- const defaultMbrId = 'oauth_user_' + Date.now(); // 임시 ID |
|
271 |
- const defaultMbrNm = 'OAuth 사용자'; |
|
272 |
- const defaultRoles = [{ authority: 'ROLE_USER' }]; |
|
273 |
- |
|
274 |
- store.commit("setAuthorization", null); |
|
275 |
- store.commit("setMbrId", defaultMbrId); |
|
276 |
- store.commit("setMbrNm", defaultMbrNm); |
|
277 |
- store.commit("setRoles", defaultRoles); |
|
278 |
- |
|
279 |
- // localStorage에 정보 저장 |
|
280 |
- localStorage.setItem("mbrId", defaultMbrId); |
|
281 |
- localStorage.setItem("mbrNm", defaultMbrNm); |
|
282 |
- localStorage.setItem("roles", JSON.stringify(defaultRoles)); |
|
283 |
- |
|
284 |
- console.log("OAuth 세션 로그인 완료 - Store 상태:", { |
|
285 |
- authorization: store.state.authorization, |
|
286 |
- mbrId: store.state.mbrId, |
|
287 |
- mbrNm: store.state.mbrNm, |
|
288 |
- roles: store.state.roles, |
|
289 |
- loginMode: store.state.loginMode |
|
398 |
+ moveResetPswd() { |
|
399 |
+ this.$router.push({ |
|
400 |
+ path: this.$filters.ctxPath("/resetPswd.page"), |
|
401 |
+ query: { tab: "pw" } |
|
290 | 402 |
}); |
291 |
- |
|
292 |
- // 페이지 이동 처리 |
|
293 |
- await this.$nextTick(); |
|
294 |
- |
|
295 |
- // 리다이렉트 URL 처리 |
|
296 |
- const redirectUrl = sessionStorage.getItem('oauth_redirect'); |
|
297 |
- sessionStorage.removeItem('oauth_redirect'); |
|
298 |
- |
|
299 |
- if (redirectUrl && redirectUrl !== '/login' && !redirectUrl.includes('/login.page')) { |
|
300 |
- console.log("OAuth 리다이렉트:", redirectUrl); |
|
301 |
- this.$router.replace({ path: redirectUrl }); |
|
302 |
- } else { |
|
303 |
- // 메인 페이지로 이동 |
|
304 |
- const mainPath = this.$filters.ctxPath("/"); |
|
305 |
- console.log("OAuth 메인 페이지로 이동:", mainPath); |
|
306 |
- this.$router.replace({ path: mainPath }); |
|
307 |
- } |
|
308 |
- |
|
309 |
- } catch (error) { |
|
310 |
- console.error('OAuth 콜백 처리 실패:', error); |
|
311 |
- alert('OAuth 로그인 처리 중 오류가 발생했습니다: ' + (error.message || '알 수 없는 오류')); |
|
312 |
- |
|
313 |
- // 에러 발생 시 로그인 페이지로 이동 |
|
314 |
- store.commit("setStoreReset"); |
|
315 |
- this.$router.push({ path: this.$filters.ctxPath("/login.page") }); |
|
316 | 403 |
} |
317 | 404 |
} |
318 |
-} |
|
319 |
- }, |
|
320 |
- watch: {}, |
|
321 |
- computed: {}, |
|
322 |
- components: {}, |
|
323 |
- created() { |
|
324 |
- this.checkAdminPage(); |
|
325 |
- // OAuth2 콜백 처리 |
|
326 |
- this.handleOAuthCallback(); |
|
327 |
- }, |
|
328 |
- mounted() {}, |
|
329 | 405 |
}; |
330 | 406 |
</script>(파일 끝에 줄바꿈 문자 없음) |
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?