fix: 로그아웃 후 자동 재로그인 버그 수정
쿠키를 단일 진실 출처로 만들어 서브도메인 간 로그아웃 불일치 해결: - login.html: logout=1 파라미터 시 localStorage+쿠키 전부 정리 후 토큰 체크 스킵 - 각 시스템 logout 함수에 &logout=1 추가 (6개 파일) - 각 시스템 initAuth에 쿠키 우선 검증 추가 (7개 파일) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -201,8 +201,20 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// logout=1 파라미터가 있으면 모든 인증 데이터 정리
|
||||||
|
var isLogout = new URLSearchParams(location.search).get('logout') === '1';
|
||||||
|
if (isLogout) {
|
||||||
|
ssoCookie.remove('sso_token');
|
||||||
|
ssoCookie.remove('sso_user');
|
||||||
|
ssoCookie.remove('sso_refresh_token');
|
||||||
|
['sso_token','sso_user','sso_refresh_token','token','user','access_token',
|
||||||
|
'currentUser','current_user','userInfo','userPageAccess'].forEach(function(k) {
|
||||||
|
localStorage.removeItem(k);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 이미 로그인 되어있으면 포털로 (쿠키 또는 localStorage 체크 + 만료 확인)
|
// 이미 로그인 되어있으면 포털로 (쿠키 또는 localStorage 체크 + 만료 확인)
|
||||||
var existingToken = ssoCookie.get('sso_token') || localStorage.getItem('sso_token');
|
var existingToken = isLogout ? null : (ssoCookie.get('sso_token') || localStorage.getItem('sso_token'));
|
||||||
if (existingToken && existingToken !== 'undefined' && existingToken !== 'null') {
|
if (existingToken && existingToken !== 'undefined' && existingToken !== 'null') {
|
||||||
if (isTokenValid(existingToken)) {
|
if (isTokenValid(existingToken)) {
|
||||||
// 쿠키 재설정 (localStorage에만 있고 쿠키가 없는 경우 대비)
|
// 쿠키 재설정 (localStorage에만 있고 쿠키가 없는 경우 대비)
|
||||||
|
|||||||
@@ -51,7 +51,7 @@
|
|||||||
['sso_token','sso_user','sso_refresh_token','token','user','access_token','currentUser','current_user','userInfo','userPageAccess'].forEach(function(k) {
|
['sso_token','sso_user','sso_refresh_token','token','user','access_token','currentUser','current_user','userInfo','userPageAccess'].forEach(function(k) {
|
||||||
localStorage.removeItem(k);
|
localStorage.removeItem(k);
|
||||||
});
|
});
|
||||||
window.location.href = this.getLoginUrl();
|
window.location.href = this.getLoginUrl(window.location.href) + '&logout=1';
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ if ('caches' in window) {
|
|||||||
// 401 Unauthorized 처리
|
// 401 Unauthorized 처리
|
||||||
if (response.status === 401) {
|
if (response.status === 401) {
|
||||||
window.clearSSOAuth();
|
window.clearSSOAuth();
|
||||||
window.location.href = window.getLoginUrl();
|
window.location.href = window.getLoginUrl() + '&logout=1';
|
||||||
throw new Error('인증이 만료되었습니다.');
|
throw new Error('인증이 만료되었습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -120,8 +120,24 @@ async function checkPageAccess(pageKey) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 쿠키 직접 읽기 (api-base.js의 cookieGet은 IIFE 내부 함수이므로 접근 불가)
|
||||||
|
function _authCookieGet(name) {
|
||||||
|
var match = document.cookie.match(new RegExp('(?:^|; )' + name + '=([^;]*)'));
|
||||||
|
return match ? decodeURIComponent(match[1]) : null;
|
||||||
|
}
|
||||||
|
|
||||||
// 즉시 실행 함수로 스코프를 보호하고 로직을 실행
|
// 즉시 실행 함수로 스코프를 보호하고 로직을 실행
|
||||||
(async function() {
|
(async function() {
|
||||||
|
// 쿠키 우선 검증: 쿠키 없고 localStorage에만 토큰이 있으면 정리
|
||||||
|
var cookieToken = _authCookieGet('sso_token');
|
||||||
|
var localToken = localStorage.getItem('sso_token');
|
||||||
|
if (!cookieToken && localToken) {
|
||||||
|
['sso_token','sso_user','sso_refresh_token','token','user','access_token',
|
||||||
|
'currentUser','current_user','userInfo','userPageAccess'].forEach(function(k) { localStorage.removeItem(k); });
|
||||||
|
window.location.href = window.getLoginUrl ? window.getLoginUrl() : '/login';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!isLoggedIn()) {
|
if (!isLoggedIn()) {
|
||||||
clearAuthData(); // 만약을 위해 한번 더 정리
|
clearAuthData(); // 만약을 위해 한번 더 정리
|
||||||
window.location.href = window.getLoginUrl ? window.getLoginUrl() : '/login';
|
window.location.href = window.getLoginUrl ? window.getLoginUrl() : '/login';
|
||||||
|
|||||||
@@ -19,6 +19,12 @@
|
|||||||
window.location.href = window.getLoginUrl ? window.getLoginUrl() : '/login';
|
window.location.href = window.getLoginUrl ? window.getLoginUrl() : '/login';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== 쿠키 직접 읽기 (api-base.js의 cookieGet은 IIFE 내부이므로) =====
|
||||||
|
function cookieGet(name) {
|
||||||
|
var match = document.cookie.match(new RegExp('(?:^|; )' + name + '=([^;]*)'));
|
||||||
|
return match ? decodeURIComponent(match[1]) : null;
|
||||||
|
}
|
||||||
|
|
||||||
// ===== 인증 함수 (api-base.js의 전역 헬퍼 활용) =====
|
// ===== 인증 함수 (api-base.js의 전역 헬퍼 활용) =====
|
||||||
function isLoggedIn() {
|
function isLoggedIn() {
|
||||||
var token = window.getSSOToken ? window.getSSOToken() : localStorage.getItem('sso_token');
|
var token = window.getSSOToken ? window.getSSOToken() : localStorage.getItem('sso_token');
|
||||||
@@ -40,6 +46,16 @@
|
|||||||
|
|
||||||
// ===== 메인 초기화 =====
|
// ===== 메인 초기화 =====
|
||||||
async function init() {
|
async function init() {
|
||||||
|
// 쿠키 우선 검증: 쿠키 없고 localStorage에만 토큰이 있으면 정리
|
||||||
|
var cookieToken = cookieGet('sso_token');
|
||||||
|
var localToken = localStorage.getItem('sso_token');
|
||||||
|
if (!cookieToken && localToken) {
|
||||||
|
['sso_token','sso_user','sso_refresh_token','token','user','access_token',
|
||||||
|
'currentUser','current_user','userInfo','userPageAccess'].forEach(function(k) { localStorage.removeItem(k); });
|
||||||
|
safeRedirectToLogin();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 1. 인증 확인
|
// 1. 인증 확인
|
||||||
if (!isLoggedIn()) {
|
if (!isLoggedIn()) {
|
||||||
clearAuthData();
|
clearAuthData();
|
||||||
|
|||||||
@@ -46,8 +46,17 @@ class App {
|
|||||||
* 인증 확인
|
* 인증 확인
|
||||||
*/
|
*/
|
||||||
async checkAuth() {
|
async checkAuth() {
|
||||||
|
// 쿠키 우선 검증: 쿠키 없고 localStorage에만 토큰이 있으면 정리
|
||||||
|
const cookieToken = this._cookieGet('sso_token');
|
||||||
|
const localToken = localStorage.getItem('sso_token');
|
||||||
|
if (!cookieToken && localToken) {
|
||||||
|
['sso_token','sso_user','sso_refresh_token','token','user','access_token',
|
||||||
|
'currentUser','current_user','userInfo','userPageAccess'].forEach(k => localStorage.removeItem(k));
|
||||||
|
throw new Error('쿠키 없음 - 로그아웃 상태');
|
||||||
|
}
|
||||||
|
|
||||||
// SSO 쿠키 우선, localStorage 폴백
|
// SSO 쿠키 우선, localStorage 폴백
|
||||||
const token = this._cookieGet('sso_token') || localStorage.getItem('sso_token');
|
const token = cookieToken || localToken;
|
||||||
if (!token) {
|
if (!token) {
|
||||||
throw new Error('토큰 없음');
|
throw new Error('토큰 없음');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,6 +97,15 @@ class AuthManager {
|
|||||||
* 저장소에서 사용자 정보 복원 (SSO 쿠키 + localStorage)
|
* 저장소에서 사용자 정보 복원 (SSO 쿠키 + localStorage)
|
||||||
*/
|
*/
|
||||||
restoreUserFromStorage() {
|
restoreUserFromStorage() {
|
||||||
|
// 쿠키 우선 검증: 쿠키 없고 localStorage에만 토큰이 있으면 정리
|
||||||
|
const cookieToken = this._cookieGet('sso_token');
|
||||||
|
const localToken = localStorage.getItem('sso_token');
|
||||||
|
if (!cookieToken && localToken) {
|
||||||
|
['sso_token','sso_user','sso_refresh_token','token','user','access_token',
|
||||||
|
'currentUser','current_user','userInfo','userPageAccess'].forEach(k => localStorage.removeItem(k));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const token = this._getToken();
|
const token = this._getToken();
|
||||||
const user = this._getUser();
|
const user = this._getUser();
|
||||||
|
|
||||||
@@ -240,7 +249,7 @@ class AuthManager {
|
|||||||
logout() {
|
logout() {
|
||||||
this.clearAuth();
|
this.clearAuth();
|
||||||
this.notifyListeners('logout');
|
this.notifyListeners('logout');
|
||||||
window.location.href = this._getLoginUrl();
|
window.location.href = this._getLoginUrl() + '&logout=1';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ function doLogout() {
|
|||||||
if (!confirm('로그아웃?')) return;
|
if (!confirm('로그아웃?')) return;
|
||||||
_cookieRemove('sso_token'); _cookieRemove('sso_user'); _cookieRemove('sso_refresh_token');
|
_cookieRemove('sso_token'); _cookieRemove('sso_user'); _cookieRemove('sso_refresh_token');
|
||||||
['sso_token','sso_user','sso_refresh_token','token','user','access_token','currentUser','current_user','userInfo','userPageAccess'].forEach(k => localStorage.removeItem(k));
|
['sso_token','sso_user','sso_refresh_token','token','user','access_token','currentUser','current_user','userInfo','userPageAccess'].forEach(k => localStorage.removeItem(k));
|
||||||
location.href = getLoginUrl();
|
location.href = getLoginUrl() + '&logout=1';
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===== Navbar ===== */
|
/* ===== Navbar ===== */
|
||||||
@@ -106,6 +106,16 @@ let currentUser = null;
|
|||||||
|
|
||||||
/* ===== Init ===== */
|
/* ===== Init ===== */
|
||||||
function initAuth() {
|
function initAuth() {
|
||||||
|
// 쿠키 우선 검증: 쿠키 없고 localStorage에만 토큰이 있으면 정리
|
||||||
|
const cookieToken = _cookieGet('sso_token');
|
||||||
|
const localToken = localStorage.getItem('sso_token');
|
||||||
|
if (!cookieToken && localToken) {
|
||||||
|
['sso_token','sso_user','sso_refresh_token','token','user','access_token',
|
||||||
|
'currentUser','current_user','userInfo','userPageAccess'].forEach(k => localStorage.removeItem(k));
|
||||||
|
_safeRedirect();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const token = getToken();
|
const token = getToken();
|
||||||
if (!token) { _safeRedirect(); return false; }
|
if (!token) { _safeRedirect(); return false; }
|
||||||
const decoded = decodeToken(token);
|
const decoded = decodeToken(token);
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ function doLogout() {
|
|||||||
if (!confirm('로그아웃?')) return;
|
if (!confirm('로그아웃?')) return;
|
||||||
_cookieRemove('sso_token'); _cookieRemove('sso_user'); _cookieRemove('sso_refresh_token');
|
_cookieRemove('sso_token'); _cookieRemove('sso_user'); _cookieRemove('sso_refresh_token');
|
||||||
['sso_token','sso_user','sso_refresh_token','token','user','access_token','currentUser','current_user','userInfo','userPageAccess'].forEach(k => localStorage.removeItem(k));
|
['sso_token','sso_user','sso_refresh_token','token','user','access_token','currentUser','current_user','userInfo','userPageAccess'].forEach(k => localStorage.removeItem(k));
|
||||||
location.href = getLoginUrl();
|
location.href = getLoginUrl() + '&logout=1';
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===== Navbar ===== */
|
/* ===== Navbar ===== */
|
||||||
@@ -100,6 +100,16 @@ let currentUser = null;
|
|||||||
|
|
||||||
/* ===== Init ===== */
|
/* ===== Init ===== */
|
||||||
function initAuth() {
|
function initAuth() {
|
||||||
|
// 쿠키 우선 검증: 쿠키 없고 localStorage에만 토큰이 있으면 정리
|
||||||
|
const cookieToken = _cookieGet('sso_token');
|
||||||
|
const localToken = localStorage.getItem('sso_token');
|
||||||
|
if (!cookieToken && localToken) {
|
||||||
|
['sso_token','sso_user','sso_refresh_token','token','user','access_token',
|
||||||
|
'currentUser','current_user','userInfo','userPageAccess'].forEach(k => localStorage.removeItem(k));
|
||||||
|
_safeRedirect();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const token = getToken();
|
const token = getToken();
|
||||||
if (!token) { _safeRedirect(); return false; }
|
if (!token) { _safeRedirect(); return false; }
|
||||||
const decoded = decodeToken(token);
|
const decoded = decodeToken(token);
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ function doLogout() {
|
|||||||
if (!confirm('로그아웃?')) return;
|
if (!confirm('로그아웃?')) return;
|
||||||
_cookieRemove('sso_token'); _cookieRemove('sso_user'); _cookieRemove('sso_refresh_token');
|
_cookieRemove('sso_token'); _cookieRemove('sso_user'); _cookieRemove('sso_refresh_token');
|
||||||
['sso_token','sso_user','sso_refresh_token','token','user','access_token','currentUser','current_user','userInfo','userPageAccess'].forEach(k => localStorage.removeItem(k));
|
['sso_token','sso_user','sso_refresh_token','token','user','access_token','currentUser','current_user','userInfo','userPageAccess'].forEach(k => localStorage.removeItem(k));
|
||||||
location.href = getLoginUrl();
|
location.href = getLoginUrl() + '&logout=1';
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===== State ===== */
|
/* ===== State ===== */
|
||||||
@@ -83,6 +83,16 @@ let currentUser = null;
|
|||||||
|
|
||||||
/* ===== Init ===== */
|
/* ===== Init ===== */
|
||||||
async function init() {
|
async function init() {
|
||||||
|
// 쿠키 우선 검증: 쿠키 없고 localStorage에만 토큰이 있으면 정리
|
||||||
|
const cookieToken = _cookieGet('sso_token');
|
||||||
|
const localToken = localStorage.getItem('sso_token');
|
||||||
|
if (!cookieToken && localToken) {
|
||||||
|
['sso_token','sso_user','sso_refresh_token','token','user','access_token',
|
||||||
|
'currentUser','current_user','userInfo','userPageAccess'].forEach(k => localStorage.removeItem(k));
|
||||||
|
_safeRedirect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const token = getToken();
|
const token = getToken();
|
||||||
if (!token) { _safeRedirect(); return; }
|
if (!token) { _safeRedirect(); return; }
|
||||||
const decoded = decodeToken(token);
|
const decoded = decodeToken(token);
|
||||||
|
|||||||
Reference in New Issue
Block a user