From d4ebdf3d325dcd8d9dc586510b210174ef5596b9 Mon Sep 17 00:00:00 2001 From: choiseoji Date: Wed, 2 Jul 2025 17:04:29 +0900 Subject: [PATCH 1/3] =?UTF-8?q?[#11]=20feat:=20swagger=20config=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/global/config/SwaggerConfig.java | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/main/java/run/backend/global/config/SwaggerConfig.java diff --git a/src/main/java/run/backend/global/config/SwaggerConfig.java b/src/main/java/run/backend/global/config/SwaggerConfig.java new file mode 100644 index 0000000..b535bd2 --- /dev/null +++ b/src/main/java/run/backend/global/config/SwaggerConfig.java @@ -0,0 +1,49 @@ +package run.backend.global.config; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.servers.Server; +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.security.SecurityScheme; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.List; + + +@Configuration +@OpenAPIDefinition( + info = @Info( + title = "Runners-Fight API", + description = "Runners-Fight 서비스의 API 명세서입니다.", + version = "v1" + ) +) +public class SwaggerConfig { + + @Value("${swagger.server.url.prod}") + private String prodUrl; + + @Bean + public OpenAPI openAPI() { + String jwt = "JWT"; + SecurityRequirement securityRequirement = new SecurityRequirement().addList(jwt); + Components components = new Components().addSecuritySchemes(jwt, new SecurityScheme() + .name(jwt) + .type(SecurityScheme.Type.HTTP) + .scheme("bearer") + .bearerFormat("jwt") + ); + + return new OpenAPI() + .addSecurityItem(securityRequirement) + .components(components) + .servers(List.of( + new Server().url("http://localhost:8080").description("local server"), + new Server().url(prodUrl).description("production server") + )); + } +} From 95e58828f2be53775d94fc366555d08f6977ebc2 Mon Sep 17 00:00:00 2001 From: choiseoji Date: Wed, 2 Jul 2025 17:04:46 +0900 Subject: [PATCH 2/3] =?UTF-8?q?[#11]=20feat:=20swagger=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 11 +++++++++-- src/main/resources/application.yml | 17 ++++++++++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 75d8a10..33e2dfb 100644 --- a/build.gradle +++ b/build.gradle @@ -29,16 +29,23 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' - implementation 'org.postgresql:postgresql:42.7.3' compileOnly 'org.projectlombok:lombok' - runtimeOnly 'org.postgresql:postgresql' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + + // postgreSql + runtimeOnly 'org.postgresql:postgresql' + implementation 'org.postgresql:postgresql:42.7.3' + + // jwt implementation 'io.jsonwebtoken:jjwt-api:0.11.5' runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' + + // swagger + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2' } tasks.named('test') { diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 494d3b7..d9b36eb 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -41,4 +41,19 @@ jwt: file: upload: - dir: ${user.home}/${UPLOAD_DIR} \ No newline at end of file + dir: ${user.home}/${UPLOAD_DIR} + +swagger: + id: ${SWAGGER_ID} + pwd: ${SWAGGER_PWD} + server: + url: + prod: ${SERVER_URI} + +springdoc: + api-docs: + enabled: true + swagger-ui: + enabled: true + path: /swagger + override-with-generic-response: false \ No newline at end of file From 3134fce914326429996950c3a50aad486a9215f0 Mon Sep 17 00:00:00 2001 From: choiseoji Date: Wed, 2 Jul 2025 17:06:05 +0900 Subject: [PATCH 3/3] =?UTF-8?q?[#11]=20feat:=20swagger=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=EA=B4=80=EB=A0=A8=20securityFilterChain?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/security/SecurityConfig.java | 81 ++++++++++++++++--- 1 file changed, 70 insertions(+), 11 deletions(-) diff --git a/src/main/java/run/backend/global/security/SecurityConfig.java b/src/main/java/run/backend/global/security/SecurityConfig.java index 99627db..4990ef4 100644 --- a/src/main/java/run/backend/global/security/SecurityConfig.java +++ b/src/main/java/run/backend/global/security/SecurityConfig.java @@ -2,13 +2,22 @@ import java.util.List; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; +import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.web.cors.CorsConfiguration; @@ -22,24 +31,74 @@ @RequiredArgsConstructor public class SecurityConfig { + @Value("${swagger.id}") + private String swaggerUser; + + @Value("${swagger.pwd}") + private String swaggerPassword; + private final JwtTokenProvider jwtTokenProvider; + private final String[] SwaggerPatterns = { + "/v3/api-docs/**", + "/swagger-ui/**", + "/swagger-ui.html", + "/swagger" + }; + + private final String[] PermitAllPatterns = { + "/api/v1/auth/**" + }; + @Bean - public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - http.csrf(AbstractHttpConfigurer::disable).httpBasic(AbstractHttpConfigurer::disable) - .formLogin(AbstractHttpConfigurer::disable); + public UserDetailsService userDetailsService() { + UserDetails user = + User.withUsername(swaggerUser) + .password(passwordEncoder().encode(swaggerPassword)) + .roles("SWAGGER") + .build(); + return new InMemoryUserDetailsManager(user); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(8); + } + + @Bean + @Order(1) + public SecurityFilterChain swaggerSecurityFilterChain(HttpSecurity http) throws Exception { + http + .securityMatcher(SwaggerPatterns) + .authorizeHttpRequests(authorize -> authorize + .anyRequest().hasRole("SWAGGER") + ) + .httpBasic(Customizer.withDefaults()) + .csrf(AbstractHttpConfigurer::disable); + + return http.build(); + } - http.sessionManagement( - session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); + @Bean + @Order(2) + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http + .securityMatcher(PermitAllPatterns) + .csrf(AbstractHttpConfigurer::disable) + .httpBasic(AbstractHttpConfigurer::disable) + .formLogin(AbstractHttpConfigurer::disable); - http.authorizeHttpRequests( - authorize -> authorize.requestMatchers("/api/v1/auth/**").permitAll().anyRequest() - .authenticated()); + http + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .cors(cors -> cors.configurationSource(corsConfigurationSource())); - http.cors(cors -> cors.configurationSource(corsConfigurationSource())); + http + .authorizeHttpRequests(authorize -> authorize + .requestMatchers(PermitAllPatterns).permitAll() + .anyRequest().authenticated()); - http.addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider), - UsernamePasswordAuthenticationFilter.class); + http + .addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class); return http.build(); }