From b4a448656007a90290fce68edb5c79440b136422 Mon Sep 17 00:00:00 2001 From: teddy Date: Fri, 6 Feb 2026 14:51:45 +0900 Subject: [PATCH] =?UTF-8?q?=EC=8A=A4=EC=9B=A8=EA=B1=B0=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=EC=84=A4=EC=A0=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/{ => swagger}/SwaggerConfig.java | 2 +- .../swagger/SwaggerUiAutoAuthConfig.java | 97 +++++++++++++++++++ 2 files changed, 98 insertions(+), 1 deletion(-) rename src/main/java/com/kamco/cd/training/config/{ => swagger}/SwaggerConfig.java (88%) create mode 100644 src/main/java/com/kamco/cd/training/config/swagger/SwaggerUiAutoAuthConfig.java diff --git a/src/main/java/com/kamco/cd/training/config/SwaggerConfig.java b/src/main/java/com/kamco/cd/training/config/swagger/SwaggerConfig.java similarity index 88% rename from src/main/java/com/kamco/cd/training/config/SwaggerConfig.java rename to src/main/java/com/kamco/cd/training/config/swagger/SwaggerConfig.java index 8fc87dc..d31bc41 100644 --- a/src/main/java/com/kamco/cd/training/config/SwaggerConfig.java +++ b/src/main/java/com/kamco/cd/training/config/swagger/SwaggerConfig.java @@ -1,4 +1,4 @@ -package com.kamco.cd.training.config; +package com.kamco.cd.training.config.swagger; import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; import io.swagger.v3.oas.annotations.security.SecurityScheme; diff --git a/src/main/java/com/kamco/cd/training/config/swagger/SwaggerUiAutoAuthConfig.java b/src/main/java/com/kamco/cd/training/config/swagger/SwaggerUiAutoAuthConfig.java new file mode 100644 index 0000000..cdfbbf9 --- /dev/null +++ b/src/main/java/com/kamco/cd/training/config/swagger/SwaggerUiAutoAuthConfig.java @@ -0,0 +1,97 @@ +package com.kamco.cd.training.config.swagger; + +import jakarta.servlet.http.HttpServletRequest; +import java.nio.charset.StandardCharsets; +import org.springdoc.core.properties.SwaggerUiConfigProperties; +import org.springdoc.core.properties.SwaggerUiOAuthProperties; +import org.springdoc.core.providers.ObjectMapperProvider; +import org.springdoc.webmvc.ui.SwaggerIndexPageTransformer; +import org.springdoc.webmvc.ui.SwaggerIndexTransformer; +import org.springdoc.webmvc.ui.SwaggerWelcomeCommon; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.context.annotation.Profile; +import org.springframework.core.io.Resource; +import org.springframework.web.servlet.resource.ResourceTransformerChain; +import org.springframework.web.servlet.resource.TransformedResource; + +@Profile({"local", "dev"}) +@Configuration +public class SwaggerUiAutoAuthConfig { + + @Bean + @Primary + public SwaggerIndexTransformer swaggerIndexTransformer( + SwaggerUiConfigProperties swaggerUiConfigProperties, + SwaggerUiOAuthProperties swaggerUiOAuthProperties, + SwaggerWelcomeCommon swaggerWelcomeCommon, + ObjectMapperProvider objectMapperProvider) { + + SwaggerIndexPageTransformer delegate = + new SwaggerIndexPageTransformer( + swaggerUiConfigProperties, + swaggerUiOAuthProperties, + swaggerWelcomeCommon, + objectMapperProvider); + + return new SwaggerIndexTransformer() { + private static final String TOKEN_KEY = "SWAGGER_ACCESS_TOKEN"; + + @Override + public Resource transform( + HttpServletRequest request, Resource resource, ResourceTransformerChain chain) { + try { + // 1) springdoc 기본 변환 먼저 적용 + Resource transformed = delegate.transform(request, resource, chain); + + String html = + new String(transformed.getInputStream().readAllBytes(), StandardCharsets.UTF_8); + + String loginPathContains = "/api/auth/signin"; + + String inject = + """ + tagsSorter: (a, b) => { + const TOP = '인증(Auth)'; + if (a === TOP && b !== TOP) return -1; + if (b === TOP && a !== TOP) return 1; + return a.localeCompare(b); + }, + requestInterceptor: (req) => { + const token = localStorage.getItem('%s'); + if (token) { + req.headers = req.headers || {}; + req.headers['Authorization'] = 'Bearer ' + token; + } + return req; + }, + responseInterceptor: async (res) => { + try { + const isLogin = (res?.url?.includes('%s') && res?.status === 200); + if (isLogin) { + const text = (typeof res.data === 'string') ? res.data : JSON.stringify(res.data); + const json = JSON.parse(text); + const token = json?.data?.accessToken; + + if (token) { + localStorage.setItem('%s', token); + } + } + } catch (e) {} + return res; + }, + """ + .formatted(TOKEN_KEY, loginPathContains, TOKEN_KEY); + + html = html.replace("SwaggerUIBundle({", "SwaggerUIBundle({\n" + inject); + + return new TransformedResource(transformed, html.getBytes(StandardCharsets.UTF_8)); + } catch (Exception e) { + // 실패 시 원본 반환(문서 깨짐 방지) + return resource; + } + } + }; + } +} -- 2.49.1