diff --git a/src/main/java/com/alist/api/config/cache/CorsAllowedOriginsCache.java b/src/main/java/com/alist/api/config/cache/CorsAllowedOriginsCache.java index f2f908c..28a8a8b 100644 --- a/src/main/java/com/alist/api/config/cache/CorsAllowedOriginsCache.java +++ b/src/main/java/com/alist/api/config/cache/CorsAllowedOriginsCache.java @@ -8,10 +8,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; @Slf4j @Component @@ -20,8 +18,8 @@ public class CorsAllowedOriginsCache { private final TestCorsMapper testCorsMapper; - // Thread-safe Map - private Map> corsMap = new ConcurrentHashMap<>(); + // Thread-safe List (volatile로 가시성 보장) + private volatile List allowedOrigins = new ArrayList<>(); /** * 서버 시작 시 DB에서 전체 CORS 목록 로딩 @@ -37,24 +35,16 @@ public class CorsAllowedOriginsCache { */ public synchronized void refresh() { try { - List allCors = testCorsMapper.selectTestCorsAllowedList(); - - Map> newMap = new HashMap<>(); - - for (TestCorsOriginVo cors : allCors) { - Integer userTokenIdx = cors.getUserTokenIdx(); - String allowedOrigin = cors.getAllowedOrigin(); - - newMap.computeIfAbsent(userTokenIdx, k -> new ArrayList<>()) - .add(allowedOrigin); - } + List origins = testCorsMapper.selectTestCorsAllowedList() + .stream() + .map(TestCorsOriginVo::getAllowedOrigin) + .distinct() + .collect(Collectors.toList()); // 전체 교체 - this.corsMap = new ConcurrentHashMap<>(newMap); + this.allowedOrigins = origins; - log.info("CORS cache refreshed. Total tokens: {}, Total origins: {}", - corsMap.size(), - corsMap.values().stream().mapToInt(List::size).sum()); + log.info("CORS cache refreshed. Total origins: {}", origins.size()); } catch (Exception e) { log.error("Failed to refresh CORS cache", e); @@ -62,43 +52,18 @@ public class CorsAllowedOriginsCache { } /** - * user_token_idx 기반 허용 도메인 목록 조회 - * @param userTokenIdx user_token_idx (null이면 기본값 0만 조회) + * 허용 도메인 목록 조회 * @return 허용 도메인 리스트 */ - public List getAllowedOrigins(Integer userTokenIdx) { - List result = new ArrayList<>(); - - // 기본값 (0) 추가 - List defaultOrigins = corsMap.get(0); - if (defaultOrigins != null) { - result.addAll(defaultOrigins); - } - - // 특정 토큰값 추가 - if (userTokenIdx != null && userTokenIdx != 0) { - List tokenOrigins = corsMap.get(userTokenIdx); - if (tokenOrigins != null) { - result.addAll(tokenOrigins); - } - } - - return result; + public List getAllowedOrigins() { + return new ArrayList<>(allowedOrigins); } /** * 현재 캐시 상태 조회 - * @return 캐시 상태 정보 (토큰별 도메인 목록) + * @return 캐시 상태 정보 */ - public Map getCacheStatus() { - Map status = new HashMap<>(); - - int totalOrigins = corsMap.values().stream().mapToInt(List::size).sum(); - - status.put("totalTokens", corsMap.size()); - status.put("totalOrigins", totalOrigins); - status.put("details", new HashMap<>(corsMap)); - - return status; + public List getCacheStatus() { + return new ArrayList<>(allowedOrigins); } } diff --git a/src/main/java/com/alist/api/config/filter/DynamicCorsFilter.java b/src/main/java/com/alist/api/config/filter/DynamicCorsFilter.java index 302c64e..d136a2d 100644 --- a/src/main/java/com/alist/api/config/filter/DynamicCorsFilter.java +++ b/src/main/java/com/alist/api/config/filter/DynamicCorsFilter.java @@ -1,7 +1,6 @@ package com.alist.api.config.filter; import com.alist.api.config.cache.CorsAllowedOriginsCache; -import com.alist.api.config.jwt.JwtTokenProvider; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; @@ -23,7 +22,6 @@ import java.util.List; public class DynamicCorsFilter extends OncePerRequestFilter { private final CorsAllowedOriginsCache corsCache; - private final JwtTokenProvider jwtTokenProvider; @Override protected void doFilterInternal(HttpServletRequest request, @@ -31,36 +29,22 @@ public class DynamicCorsFilter extends OncePerRequestFilter { FilterChain filterChain) throws ServletException, IOException { String origin = request.getHeader("Origin"); - Integer userTokenIdx = extractUserTokenIdx(request); // 메모리에서 허용 도메인 조회 (DB 조회 없음!) - List allowedOrigins = corsCache.getAllowedOrigins(userTokenIdx); + List allowedOrigins = corsCache.getAllowedOrigins(); // Origin 허용 여부 체크 boolean isAllowed = false; - if (origin != null) { - // 1. 정확히 일치하는 경우 - if (allowedOrigins.contains("*") || allowedOrigins.contains(origin)) { - isAllowed = true; - } - // 2. localhost는 모든 포트 허용 - else if ((origin.startsWith("http://localhost:") || origin.equals("http://localhost")) - && allowedOrigins.contains("http://localhost")) { - isAllowed = true; - } - else if ((origin.startsWith("https://localhost:") || origin.equals("https://localhost")) - && allowedOrigins.contains("https://localhost")) { - isAllowed = true; - } + if (origin != null && (allowedOrigins.contains("*") || allowedOrigins.contains(origin))) { + isAllowed = true; } // CORS 체크 결과 로깅 if (origin != null) { if (isAllowed && log.isDebugEnabled()) { - log.debug("CORS allowed - Origin: {}, UserTokenIdx: {}", origin, userTokenIdx); + log.debug("CORS allowed - Origin: {}", origin); } else if (!isAllowed) { - log.warn("CORS blocked - Origin: {}, UserTokenIdx: {}, AllowedOrigins: {}", - origin, userTokenIdx, allowedOrigins); + log.warn("CORS blocked - Origin: {}, AllowedOrigins: {}", origin, allowedOrigins); } } @@ -69,7 +53,8 @@ public class DynamicCorsFilter extends OncePerRequestFilter { response.setHeader("Access-Control-Allow-Origin", origin); response.setHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS"); - response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With, Accept, Origin, Referer, Content-Disposition, Content-Length"); + response.setHeader("Access-Control-Allow-Headers", + "Content-Type, Authorization, X-Requested-With, Accept, Origin, Referer, Content-Disposition, Content-Length"); response.setHeader("Access-Control-Expose-Headers", "Authorization, Set-Cookie"); response.setHeader("Access-Control-Max-Age", "3600"); } @@ -82,49 +67,4 @@ public class DynamicCorsFilter extends OncePerRequestFilter { filterChain.doFilter(request, response); } - - /** - * Request에서 user_token_idx 추출 - * JWT 토큰 또는 API Key를 통해 추출 - */ - private Integer extractUserTokenIdx(HttpServletRequest request) { - try { - String token = null; - - // 1. Authorization Bearer 토큰 - String authHeader = request.getHeader("Authorization"); - if (authHeader != null && authHeader.startsWith("Bearer ")) { - token = authHeader.substring(7); - } - - // 2. Cookie에서 accessToken - if (token == null && request.getCookies() != null) { - for (var cookie : request.getCookies()) { - if ("accessToken".equals(cookie.getName())) { - token = cookie.getValue(); - break; - } - } - } - - // 토큰이 있으면 검증 후 userTokenIdx 추출 - if (token != null && jwtTokenProvider.validateToken(token)) { - String subject = jwtTokenProvider.getUserId(token); - - // subject를 Integer로 변환 시도 - try { - return Integer.parseInt(subject); - } catch (NumberFormatException e) { - // 숫자가 아닌 문자열이면 null 반환 (기본값 0으로 조회) - log.debug("Subject is not a number: {}", subject); - return null; - } - } - - } catch (Exception e) { - log.debug("Failed to extract userTokenIdx: {}", e.getMessage()); - } - - return null; // 기본값 0만 조회 - } } diff --git a/src/main/java/com/alist/api/modules/auth/TestCorsAdminController.java b/src/main/java/com/alist/api/modules/auth/TestCorsAdminController.java index a5f7725..f4c02ae 100644 --- a/src/main/java/com/alist/api/modules/auth/TestCorsAdminController.java +++ b/src/main/java/com/alist/api/modules/auth/TestCorsAdminController.java @@ -11,6 +11,8 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.util.List; + @Tag(name = "03. 테스트 CRS 갱신", description = "테스트 CORS 설정 관리 API") @RestController @RequestMapping("/test") @@ -24,9 +26,9 @@ public class TestCorsAdminController { description = "DB에서 CORS 허용 도메인 목록을 다시 로딩합니다. DB에 도메인 추가 후 호출하세요." ) @PostMapping("/reloadCors") - public ResponseEntity>> reloadCors() { + public ResponseEntity>> reloadCors() { corsCache.refresh(); - java.util.Map cacheStatus = corsCache.getCacheStatus(); + List cacheStatus = corsCache.getCacheStatus(); return ApiResponse.entity(cacheStatus, ApiResponseCode.CODE_200); } } diff --git a/src/main/java/com/alist/api/modules/auth/vo/TestCorsOriginVo.java b/src/main/java/com/alist/api/modules/auth/vo/TestCorsOriginVo.java index c27cb70..1d3edbd 100644 --- a/src/main/java/com/alist/api/modules/auth/vo/TestCorsOriginVo.java +++ b/src/main/java/com/alist/api/modules/auth/vo/TestCorsOriginVo.java @@ -6,6 +6,5 @@ import lombok.Setter; @Getter @Setter public class TestCorsOriginVo { - private Integer userTokenIdx; private String allowedOrigin; } diff --git a/src/main/resources/mapper/auth/TestCorsMapper.xml b/src/main/resources/mapper/auth/TestCorsMapper.xml index b87b7cc..8cf69b5 100644 --- a/src/main/resources/mapper/auth/TestCorsMapper.xml +++ b/src/main/resources/mapper/auth/TestCorsMapper.xml @@ -6,11 +6,10 @@ \ No newline at end of file