fix: 전 시스템 Chrome 무한 로그인 루프 해결 및 role 대소문자 통일

- gateway: 로그인 페이지 자동 리다이렉트 시 SSO 쿠키 재설정 + Cache-Control no-store
- tkreport(system2): SW 해제, 401 핸들러 리다이렉트 제거, 루프 방지, localStorage 백업
- TKQC 모바일(system3): mCheckAuth를 authManager 위임으로 변경, 루프 방지
- TKQC 공통(system3): api.js 로그인 URL 캐시 버스팅, auth-manager localStorage 백업
- tkuser: SW 해제, 401 핸들러 수정, 루프 방지, localStorage 백업, requireAdmin role 소문자 통일
- system1: 작업보고서 admin role 대소문자 무시, refresh 토큰에 role 필드 추가

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-03-09 14:10:46 +09:00
parent df0a125faa
commit 5aeda43605
18 changed files with 144 additions and 49 deletions

View File

@@ -189,7 +189,7 @@
<script src="/static/js/core/auth-manager.js?v=20260308"></script>
<script src="/static/js/core/permissions.js?v=20260308"></script>
<script src="/static/js/utils/issue-helpers.js?v=20260308"></script>
<script src="/static/js/m/m-common.js?v=20260308"></script>
<script src="/static/js/m/m-common.js?v=20260309"></script>
<script src="/static/js/m/m-dashboard.js?v=20260308"></script>
</body>
</html>

View File

@@ -197,7 +197,7 @@
<script src="/static/js/core/auth-manager.js?v=20260308"></script>
<script src="/static/js/core/permissions.js?v=20260308"></script>
<script src="/static/js/utils/issue-helpers.js?v=20260308"></script>
<script src="/static/js/m/m-common.js?v=20260308"></script>
<script src="/static/js/m/m-common.js?v=20260309"></script>
<script src="/static/js/m/m-inbox.js?v=20260308"></script>
</body>
</html>

View File

@@ -173,7 +173,7 @@
<script src="/static/js/core/auth-manager.js?v=20260308"></script>
<script src="/static/js/core/permissions.js?v=20260308"></script>
<script src="/static/js/utils/issue-helpers.js?v=20260308"></script>
<script src="/static/js/m/m-common.js?v=20260308"></script>
<script src="/static/js/m/m-common.js?v=20260309"></script>
<script src="/static/js/m/m-management.js?v=20260308"></script>
</body>
</html>

View File

@@ -11,13 +11,14 @@ function _cookieRemove(name) {
document.cookie = cookie;
}
// 중앙 로그인 URL
// 중앙 로그인 URL (캐시 버스팅 포함)
function _getLoginUrl() {
const hostname = window.location.hostname;
const t = Date.now();
if (hostname.includes('technicalkorea.net')) {
return window.location.protocol + '//tkfb.technicalkorea.net/login?redirect=' + encodeURIComponent(window.location.href);
return window.location.protocol + '//tkfb.technicalkorea.net/login?redirect=' + encodeURIComponent(window.location.href) + '&_t=' + t;
}
return window.location.protocol + '//' + hostname + ':30000/login?redirect=' + encodeURIComponent(window.location.href);
return window.location.protocol + '//' + hostname + ':30000/login?redirect=' + encodeURIComponent(window.location.href) + '&_t=' + t;
}
// API 기본 설정 (통합 환경 지원)

View File

@@ -154,7 +154,9 @@ class AuthManager {
this.isAuthenticated = true;
this.lastAuthCheck = Date.now();
// localStorage 업데이트
// localStorage 업데이트 (쿠키 소실 대비 백업)
const token = this._getToken();
if (token) localStorage.setItem('sso_token', token);
localStorage.setItem('sso_user', JSON.stringify(user));
this.notifyListeners('auth-success', user);

View File

@@ -13,9 +13,9 @@
})();
/* ===== KST Date Utilities ===== */
// DB에 KST로 저장된 naive datetime을 그대로 표시 (이중 변환 방지)
function getKSTDate(date) {
var d = new Date(date);
return new Date(d.getTime() + 9 * 60 * 60 * 1000);
return new Date(date);
}
function formatKSTDate(date) {
return new Date(date).toLocaleDateString('ko-KR', { timeZone: 'Asia/Seoul' });
@@ -27,13 +27,12 @@ function formatKSTDateTime(date) {
return new Date(date).toLocaleString('ko-KR', { timeZone: 'Asia/Seoul', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' });
}
function getKSTToday() {
var now = new Date();
var kst = getKSTDate(now);
var kst = new Date(new Date().toLocaleString('en-US', { timeZone: 'Asia/Seoul' }));
return new Date(kst.getFullYear(), kst.getMonth(), kst.getDate());
}
function getTimeAgo(date) {
var now = getKSTDate(new Date());
var d = getKSTDate(date);
var now = new Date();
var d = new Date(date);
var diff = now - d;
var mins = Math.floor(diff / 60000);
var hours = Math.floor(diff / 3600000);
@@ -147,20 +146,45 @@ function closePhotoModal() {
}
}
/* ===== Auth Helper ===== */
/* ===== Auth Helper (authManager 위임 + 루프 방지) ===== */
var _mRedirectKey = '_sso_redirect_ts';
var _mRedirectCooldown = 5000; // 5초 내 재리다이렉트 방지
function _mSafeRedirectToLogin() {
var last = parseInt(sessionStorage.getItem(_mRedirectKey) || '0', 10);
if (Date.now() - last < _mRedirectCooldown) {
console.warn('[TKQC-M] 리다이렉트 루프 감지 — 로그인 페이지로 이동하지 않음');
return;
}
sessionStorage.setItem(_mRedirectKey, String(Date.now()));
window.location.href = _getLoginUrl();
}
async function mCheckAuth() {
// authManager가 있으면 위임 (SW 정리 + 캐시 관리 포함)
if (window.authManager && typeof window.authManager.checkAuth === 'function') {
var user = await window.authManager.checkAuth();
if (user) {
sessionStorage.removeItem(_mRedirectKey);
return user;
}
_mSafeRedirectToLogin();
return null;
}
// 폴백: authManager 없는 경우
var token = TokenManager.getToken();
if (!token) {
window.location.href = _getLoginUrl();
_mSafeRedirectToLogin();
return null;
}
try {
var user = await AuthAPI.getCurrentUser();
sessionStorage.removeItem(_mRedirectKey);
return user;
} catch (e) {
TokenManager.removeToken();
TokenManager.removeUser();
window.location.href = _getLoginUrl();
_mSafeRedirectToLogin();
return null;
}
}