Phase 3-1~10: 백엔드
- pom.xml에 spring-boot-starter-aop 추가
- JPA 엔티티 12종 + Repository 9종
(User/LoginHistory/Role/UserRole/PermTree/Perm/AuditLog/AccessLog 등)
- PermResolver: wing 프로젝트의 permResolver.ts를 Java로 이식
- 트리 BFS + 부모 READ 게이팅 + 다중 역할 OR 합집합 + 부모 fallback
- PermissionService: Caffeine 캐싱 (TTL 10분)
- JwtService + JwtAuthFilter (HttpOnly 쿠키 + Authorization 헤더 fallback)
- AuthProvider 인터페이스 + PasswordAuthProvider (BCrypt + 5회 잠금)
- REQUIRES_NEW + noRollbackFor로 fail_cnt 증가 보존
- AuthService + LoginAuditWriter (REQUIRES_NEW로 실패 기록 보존)
- AuthController: /api/auth/login, /logout, /me
- @RequirePermission 어노테이션 + PermissionAspect (메서드 권한 체크)
- @Auditable 어노테이션 + AuditAspect (의사결정 자동 기록)
- AccessLogFilter: 모든 HTTP 요청 비동기 기록 (BlockingQueue)
- SecurityConfig 본격 도입 (CORS + JWT 필터 + 401/403 핸들러)
Phase 3-10: 데모 계정
- V006__demo_accounts.sql: 5개 데모 계정 (admin/operator/analyst/field/viewer)
+ 역할 매핑 (PLACEHOLDER 해시)
- AccountSeeder.java: 시동 시 BCrypt 해시 시드 (PLACEHOLDER만 갱신)
- 데모 계정도 실제 권한, 로그인 이력, 감사로그 기록 대상
Phase 3-11: 백엔드 검증 완료
- admin/operator/viewer 로그인 성공
- 권한 매트릭스: ADMIN(49), OPERATOR(40), VIEWER(35)
- 트리 상속 검증: detection READ → 자식 4개 자동 상속
- 잘못된 비밀번호 → fail_cnt 증가 + login_hist FAILED 기록
- 정상 로그인 → fail_cnt 0 초기화
- 모든 요청 access_log에 비동기 기록
V001/V002: CHAR(1) → VARCHAR(1) 변경 (Hibernate validate 호환성)
Phase 3-12: 프론트엔드 연동
- services/authApi.ts: 백엔드 호출 클라이언트 (login/logout/me)
- AuthContext.tsx: 백엔드 API 통합 + 트리 기반 hasPermission
+ 부모 fallback (예: detection:gear-detection 미등록 시 detection 검사)
+ 30분 세션 타임아웃 유지
- DemoQuickLogin.tsx: 데모 퀵로그인 컴포넌트 분리
+ isDemoLoginEnabled() = VITE_SHOW_DEMO_LOGIN === 'true'
+ 데모 클릭 시에도 정상 백엔드 인증 플로우 사용
- LoginPage.tsx: 백엔드 인증 호출 + DemoQuickLogin 통합
+ 에러 메시지 한국어 변환 (WRONG_PASSWORD:N, ACCOUNT_LOCKED 등)
+ GPKI/SSO 탭은 disabled (Phase 9 도입 예정)
- frontend/.env.development: VITE_SHOW_DEMO_LOGIN=true
- frontend/.env.production: VITE_SHOW_DEMO_LOGIN=true (현재 단계)
- .gitignore에 frontend/.env.{development,production} 예외 추가
설계 핵심:
- 데모 계정은 백엔드 DB에 실제 권한 부여 + 로그인/감사 기록 대상
- DemoQuickLogin 컴포넌트는 환경변수로 토글 가능하도록 구조 분리
- 향후 운영 배포 시 .env.production만 false로 변경하면 데모 영역 숨김
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
62 lines
2.2 KiB
TypeScript
62 lines
2.2 KiB
TypeScript
import { useTranslation } from 'react-i18next';
|
|
|
|
/*
|
|
* 데모 퀵로그인 영역.
|
|
*
|
|
* 표시 조건: VITE_SHOW_DEMO_LOGIN === 'true' (.env 또는 빌드 환경변수)
|
|
* - 로컬 개발: .env.development에 VITE_SHOW_DEMO_LOGIN=true
|
|
* - 배포 환경: .env.production에 VITE_SHOW_DEMO_LOGIN=false (또는 미설정)
|
|
*
|
|
* 데모 계정은 백엔드 DB(kcgaidb)에 실제 BCrypt 해시로 시드되어 있으며,
|
|
* 권한, 로그인 이력, 감사 로그도 동일하게 기록된다.
|
|
* 따라서 클릭 시에도 정상 로그인 플로우를 거쳐 백엔드 인증을 수행한다.
|
|
*/
|
|
|
|
export interface DemoAccount {
|
|
account: string;
|
|
password: string;
|
|
roleLabelKey: string; // i18n 키
|
|
}
|
|
|
|
export const DEMO_ACCOUNTS: DemoAccount[] = [
|
|
{ account: 'admin', password: 'admin1234!', roleLabelKey: 'demo.admin' },
|
|
{ account: 'operator', password: 'oper12345!', roleLabelKey: 'demo.operator' },
|
|
{ account: 'analyst', password: 'anal12345!', roleLabelKey: 'demo.analyst' },
|
|
{ account: 'field', password: 'field1234!', roleLabelKey: 'demo.field' },
|
|
{ account: 'viewer', password: 'view12345!', roleLabelKey: 'demo.viewer' },
|
|
];
|
|
|
|
export function isDemoLoginEnabled(): boolean {
|
|
return import.meta.env.VITE_SHOW_DEMO_LOGIN === 'true';
|
|
}
|
|
|
|
interface DemoQuickLoginProps {
|
|
onSelect: (account: DemoAccount) => void;
|
|
disabled?: boolean;
|
|
}
|
|
|
|
export function DemoQuickLogin({ onSelect, disabled }: DemoQuickLoginProps) {
|
|
const { t } = useTranslation('auth');
|
|
|
|
if (!isDemoLoginEnabled()) return null;
|
|
|
|
return (
|
|
<div className="pt-2 border-t border-border">
|
|
<div className="text-[9px] text-hint text-center mb-2">{t('demo.title')}</div>
|
|
<div className="grid grid-cols-5 gap-1.5">
|
|
{DEMO_ACCOUNTS.map((acct) => (
|
|
<button
|
|
key={acct.account}
|
|
type="button"
|
|
disabled={disabled}
|
|
onClick={() => onSelect(acct)}
|
|
className="py-1.5 rounded-md text-[9px] font-medium bg-surface-overlay border border-border text-muted-foreground hover:text-heading hover:bg-switch-background/60 transition-colors whitespace-nowrap disabled:opacity-50"
|
|
>
|
|
{t(acct.roleLabelKey)}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|