
File name
Commit message
Commit date
05-22
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
<template>
<div class="login-page page" :class="{ 'loading-blur': isLoading }" :style="{ 'cursor': isLoading ? 'wait' : '' }">
<div>
<div class="admin-background-img">
<p>
콘텐츠 관리 시스템에 오신 것을 환영합니다.<br />
원활한 관리와 운영을 위해 로그인하세요.
</p>
</div>
<div class="login-wrap" v-if="!loginStep1 && !loginStep2">
<div class="login">
<div :class="{
'login-title': true,
'user-login': !isAdminPage,
}">
<router-link :to="{ path: $filters.ctxPath('/') }">LOGIN(홈으로 이동)</router-link>
</div>
<div class="form-group">
<label for="id" class="login-label">아이디</label>
<input type="text" name="" id="id" class="form-control md" placeholder="아이디를 입력하세요"
v-model="member['lgnId']" />
</div>
<div class="form-group">
<label for="pw" class="login-label">비밀번호</label>
<input type="password" name="" id="pw" class="form-control md" placeholder="비밀번호를 입력하세요"
v-model="member['pswd']" @keydown.enter="fnLogin" />
</div>
<button class="btn md main user-btn" v-if="!isAdminPage" @click="fnLogin" @keydown.enter="fnLogin">
로그인
</button>
<button class="btn md main" v-else @click="fnLogin" @keydown.enter="fnLogin">
로그인
</button>
<div class="input-group" v-if="!isAdminPage">
<p class="pl10 pr10 cursor" @click="moveSearchId">아이디찾기</p>
<p class="pl10 pr0 cursor" @click="moveResetPswd">비밀번호 초기화</p>
</div>
</div>
</div>
<div class="login-wrap" v-else-if="loginStep1 && !loginStep2">
<div>
<p>인증코드 입력</p>
</div>
<div>
<p>{{ memberInfo.email }}로 전송된 6자리 인증코드를 입력하세요.</p>
<input type="text" class="form-control md" ref="code" @input="inputCode" v-model="memberInfo.code"
maxlength="6" placeholder="인증코드를 입력하세요." />
</div>
<div class="btn-wrap">
<button class="btn sm main" @click="fnCheck">인증코드 확인</button>
</div>
<div>
<p>인증코드를 받지 못하셨나요?</p>
<button class="btn sm tertiary" @click="fnResend">인증코드 다시받기</button>
</div>
</div>
</div>
</div>
</template>
<script>
import { useStore } from "vuex";
// import store from "../AppStore";
import { loginProc } from "../../../resources/api/login";
import { check2ndAuthProc, sendAuthEmailProc } from "../../../resources/api/email";
import queryParams from "../../../resources/js/queryParams";
export default {
mixins: [queryParams],
data: () => {
return {
isLoading: false,
member: {
lgnId: null,
pswd: null,
lgnReqPage: 'A'
},
// store: useStore(),
isAdminPage: false,
memberInfo: {
email: '', // 이메일
code: '', // 인증코드
},
// 인증 절차
loginStep1: false, // 1차 인증
loginStep2: false, // 2차 인증
storageType: null, // 로컬 스토리지 타입 (세션/로컬)
};
},
methods: {
init() {
this.member = {
lgnId: null,
pswd: null,
lgnReqPage: 'A'
};
this.memberInfo = {
mbrId: '',
email: '',
code: '',
};
this.loginStep1 = false;
this.loginStep2 = false;
},
checkAdminPage() {
if (
this.restoreRedirect("redirect") &&
this.restoreRedirect("redirect").includes("/adm/")
) {
this.isAdminPage = true;
} else {
this.isAdminPage = false;
}
},
async fnLogin() {
this.isLoading = true;
try {
const res = await loginProc(this.member);
if (res.status == 200) {
// 2차 인증에 필요한 이메일 정보가 있는지 확인
if (res.data.email) {
this.memberInfo = res.data; // 인증코드 전송을 위한 이메일 정보 저장
// 없을 경우 2차 인증 패스
} else {
this.loginStep2 = true; // 2차 인증 패스
this.loginSuccessProc(res); // 로그인 성공 처리
}
this.loginStep1 = true; // 1차 인증 성공
}
} catch (error) {
const errorData = error.response.data;
if (errorData.message != null && errorData.message != "") {
alert(error.response.data.message);
} else {
alert("에러가 발생했습니다.\n관리자에게 문의해주세요.");
}
} finally {
this.isLoading = false;
}
},
moveSearchId() {
this.$router.push({
path: this.$filters.ctxPath("/resetPswd.page"),
query: {
tab: "id",
},
});
},
moveResetPswd() {
this.$router.push({
path: this.$filters.ctxPath("/resetPswd.page"),
query: {
tab: "pw",
},
});
},
// 인증코드 입력
inputCode(event) {
const input = event.target.value.replace(/[^0-9]/g, '');
this.memberInfo.code = input;
},
// 인증코드 확인
async fnCheck() {
try {
const res = await check2ndAuthProc(this.memberInfo);
if (res.status == 200) {
await this.loginSuccessProc(res); // 로그인 성공 처리
}
} catch (error) {
const errorData = error.response.data;
if (errorData.message != null && errorData.message != "") {
alert(error.response.data.message);
this.$refs.code.focus();
} else {
alert("에러가 발생했습니다.\n관리자에게 문의해주세요.");
}
}
},
// 로그인 성공 시
async loginSuccessProc(res) {
const loginType = res.headers['loginmode']; // 세션/토큰 로그인 구분
this.storageType = res.headers['storage-type']; // 로컬 스토리지 타입 (세션/로컬)
if (loginType === 'J') {
this.handleJWTLogin(res);
// 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') {
this.handleSessionLogin(res);
// 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;
}
await this.handleLoginSuccess();
// const isAdmin = store.state.roles.some(role => role.authority === "ROLE_ADMIN");
// let url = this.restoreRedirect("redirect");
// console.log("리다이렉트 URL:", url, isAdmin);
// if (url != null && url != "") {
// const ctx = store.state.contextPath;
// console.log("Context Path:", ctx);
// if (ctx !== "") {
// console.log("Context Path가 설정되어 있습니다.");
// // redirect 값에서 Context Path 추가
// url = this.$filters.ctxPath(url);
// } else {
// console.log("Context Path가 설정되어 있지 않습니다.");
// // redirect 값에서 기존 Context Path 제거
// url = url.replace(/^\/[^\/]+/, ""); // 첫 번째 '/' 이후의 경로만 남김
// }
// console.log("Context Path 처리 후 리다이렉트 URL:", url);
// const routeExists = this.$router.getRoutes().some(route => route.path === url);
// console.log("리다이렉트 경로 존재 여부:", routeExists);
// 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("/")
// });
// }
},
// JWT 로그인 처리
handleJWTLogin(res) {
const token = res.headers.authorization;
const userInfo = this.parseJWT(token);
this.setAuthInfo("J", token, userInfo);
},
// 세션 로그인 처리
handleSessionLogin(res) {
const userInfo = res.data;
const roles = userInfo.roles.map(r => ({ authority: r.authrtCd }));
this.setAuthInfo("S", null, { ...userInfo, roles });
},
// JWT 토큰 파싱
parseJWT(token) {
const base64String = token.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("")
);
return JSON.parse(jsonPayload);
},
// 인증 정보 저장
setAuthInfo(loginMode, token, userInfo) {
// Vuex 상태 저장
this.$store.commit("setLoginMode", loginMode);
this.$store.commit("setAuthorization", token);
this.$store.commit("setMbrId", userInfo.mbrId);
this.$store.commit("setMbrNm", userInfo.mbrNm);
this.$store.commit("setRoles", userInfo.roles);
// localStorage 저장
// localStorage.setItem("loginMode", loginMode);
// localStorage.setItem("mbrId", userInfo.mbrId);
// localStorage.setItem("mbrNm", userInfo.mbrNm);
// localStorage.setItem("roles", JSON.stringify(userInfo.roles));
// if (token) {
// localStorage.setItem("authorization", token);
// } else {
// localStorage.removeItem("authorization");
// }
},
// ========== 로그인 성공 후 처리 ==========
async handleLoginSuccess() {
const isAdmin = this.$store.state.roles.some(role => role.authority === "ROLE_ADMIN");
let redirectUrl = this.restoreRedirect("redirect")
// 리다이렉트 URL 정리
if (redirectUrl && this.shouldRedirectToMain(redirectUrl)) {
redirectUrl = this.$filters.ctxPath("/");
}
// Context Path 처리
if (redirectUrl && !redirectUrl.startsWith(this.$store.state.contextPath) && this.$store.state.contextPath) {
redirectUrl = this.$filters.ctxPath(redirectUrl);
}
// 라우터 존재 여부 확인 후 이동
const targetPath = this.getValidRedirectPath(redirectUrl, isAdmin);
await this.$nextTick();
this.$router.push({ path: targetPath });
// 세션 정리
sessionStorage.removeItem("redirect");
},
// 리다이렉트 URL이 메인 페이지로 이동해야 하는지 확인
shouldRedirectToMain(url) {
return url.includes("/searchId.page") ||
url.includes("/resetPswd.page") ||
url.includes("/login.page");
},
// 유효한 리다이렉트 경로를 반환
getValidRedirectPath(redirectUrl, isAdmin) {
if (redirectUrl && !redirectUrl.includes("login.page")) {
const routeExists = this.$router.getRoutes().some(route => route.path === redirectUrl);
if (routeExists) return redirectUrl;
}
return isAdmin ?
this.$filters.ctxPath("/adm/main.page") :
this.$filters.ctxPath("/");
},
// 인증코드 재전송
async fnResend() {
this.isLoading = true;
try {
const res = await sendAuthEmailProc(this.memberInfo);
if (res.status == 200) {
alert(res.data.message);
}
} catch (error) {
const errorData = error.response.data;
if (errorData.message != null && errorData.message != "") {
alert(error.response.data.message);
} else {
alert("에러가 발생했습니다.\n관리자에게 문의해주세요.");
}
} finally {
this.isLoading = false;
}
},
},
watch: {
// loginStep2(newVal) {
// if (newVal) {
// // 2차 인증이 패스되면 로그인 성공 처리
// this.loginSuccess();
// }
// },
},
computed: {},
components: {},
created() {
this.init();
this.checkAdminPage();
},
mounted() { },
};
</script>
<style scoped>
.loading-blur {
pointer-events: none;
opacity: 0.4;
filter: grayscale(20%) blur(1px);
}
</style>