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

@@ -1,6 +1,18 @@
// /js/api-base.js
// API 기본 설정 및 보안 유틸리티 - System 2 (신고 시스템)
// 서비스 워커 해제 (캐시 간섭으로 인한 인증 루프 방지)
if ('serviceWorker' in navigator) {
navigator.serviceWorker.getRegistrations().then(function(registrations) {
registrations.forEach(function(registration) { registration.unregister(); });
});
if (typeof caches !== 'undefined') {
caches.keys().then(function(names) {
names.forEach(function(name) { caches.delete(name); });
});
}
}
(function() {
'use strict';
@@ -35,10 +47,11 @@
*/
window.getLoginUrl = function() {
var hostname = window.location.hostname;
var 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;
};
window.clearSSOAuth = function() {
@@ -123,10 +136,9 @@
var response = await fetch(url, config);
// 401 Unauthorized 처리
// 401 Unauthorized 처리 — 토큰만 정리하고 에러 throw (리다이렉트는 app-init이 처리)
if (response.status === 401) {
window.clearSSOAuth();
window.location.href = window.getLoginUrl();
throw new Error('인증이 만료되었습니다.');
}

View File

@@ -4,6 +4,21 @@
(function() {
'use strict';
// ===== 리다이렉트 루프 방지 =====
var REDIRECT_KEY = '_sso_redirect_ts';
var REDIRECT_COOLDOWN = 5000; // 5초 내 재리다이렉트 방지
function safeRedirectToLogin() {
var lastRedirect = parseInt(sessionStorage.getItem(REDIRECT_KEY) || '0', 10);
var now = Date.now();
if (now - lastRedirect < REDIRECT_COOLDOWN) {
console.warn('[System2] 리다이렉트 루프 감지 — 로그인 페이지로 이동하지 않음');
return;
}
sessionStorage.setItem(REDIRECT_KEY, String(now));
window.location.href = window.getLoginUrl ? window.getLoginUrl() : '/login';
}
// ===== 인증 함수 (api-base.js의 전역 헬퍼 활용) =====
function isLoggedIn() {
var token = window.getSSOToken ? window.getSSOToken() : localStorage.getItem('sso_token');
@@ -28,17 +43,21 @@
// 1. 인증 확인
if (!isLoggedIn()) {
clearAuthData();
window.location.href = window.getLoginUrl ? window.getLoginUrl() : '/login';
safeRedirectToLogin();
return;
}
var currentUser = getUser();
if (!currentUser || !currentUser.username) {
clearAuthData();
window.location.href = window.getLoginUrl ? window.getLoginUrl() : '/login';
safeRedirectToLogin();
return;
}
// 인증 성공 — 루프 카운터 리셋 + localStorage 백업
sessionStorage.removeItem(REDIRECT_KEY);
var token = window.getSSOToken ? window.getSSOToken() : null;
if (token && !localStorage.getItem('sso_token')) localStorage.setItem('sso_token', token);
console.log('[System2] 인증 확인:', currentUser.username);
}

View File

@@ -8,8 +8,8 @@
<link rel="stylesheet" href="/css/common.css?v=2">
<link rel="stylesheet" href="/css/project-management.css?v=3">
<link rel="icon" type="image/png" href="/img/favicon.png">
<script src="/js/api-base.js"></script>
<script src="/js/app-init.js?v=2" defer></script>
<script src="/js/api-base.js?v=20260309"></script>
<script src="/js/app-init.js?v=20260309" defer></script>
<script src="https://instant.page/5.2.0" type="module"></script>
<style>
/* 상태 배지 */

View File

@@ -8,8 +8,8 @@
<link rel="stylesheet" href="/css/common.css?v=2">
<link rel="stylesheet" href="/css/project-management.css?v=3">
<link rel="icon" type="image/png" href="/img/favicon.png">
<script src="/js/api-base.js"></script>
<script src="/js/app-init.js?v=2" defer></script>
<script src="/js/api-base.js?v=20260309"></script>
<script src="/js/app-init.js?v=20260309" defer></script>
<script src="https://instant.page/5.2.0" type="module"></script>
<style>
/* 통계 카드 */