diff --git a/main/build.gradle b/main/build.gradle index 28eb9001..ed190c18 100644 --- a/main/build.gradle +++ b/main/build.gradle @@ -78,6 +78,9 @@ dependencies { // csv 관련 implementation 'com.opencsv:opencsv:5.5.2' + + // prometheus + implementation 'io.micrometer:micrometer-registry-prometheus' } tasks.named('test') { diff --git a/main/src/main/java/org/sopt/makers/crew/main/common/config/SecurityConfig.java b/main/src/main/java/org/sopt/makers/crew/main/common/config/SecurityConfig.java index b3121b7d..2dfaef06 100644 --- a/main/src/main/java/org/sopt/makers/crew/main/common/config/SecurityConfig.java +++ b/main/src/main/java/org/sopt/makers/crew/main/common/config/SecurityConfig.java @@ -2,10 +2,13 @@ import java.util.Arrays; import java.util.stream.Stream; + import lombok.RequiredArgsConstructor; + import org.sopt.makers.crew.main.common.jwt.JwtAuthenticationEntryPoint; import org.sopt.makers.crew.main.common.jwt.JwtAuthenticationFilter; import org.sopt.makers.crew.main.common.jwt.JwtTokenProvider; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; @@ -25,122 +28,132 @@ @EnableWebSecurity public class SecurityConfig { - private final JwtTokenProvider jwtTokenProvider; - private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; + private final JwtTokenProvider jwtTokenProvider; + private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; + + @Value("${management.endpoints.web.base-path}") + private String actuatorEndPoint; - private static final String[] SWAGGER_URL = { - "/swagger-resources/**", - "/favicon.ico", - "/api-docs/**", - "/swagger-ui/**", - "/swagger-ui.html", - "/swagger-ui/index.html", - "/docs/swagger-ui/index.html", - "/swagger-ui/swagger-ui.css", - }; + private static final String[] SWAGGER_URL = { + "/swagger-resources/**", + "/favicon.ico", + "/api-docs/**", + "/swagger-ui/**", + "/swagger-ui.html", + "/swagger-ui/index.html", + "/docs/swagger-ui/index.html", + "/swagger-ui/swagger-ui.css", + }; - private static final String[] AUTH_WHITELIST = { - "/health", - "/health/v2", - "/meeting/v2/org-user/**", - "/auth/v2", - "/auth/v2/**" - }; + private String[] getAuthWhitelist() { + return new String[] { + "/health", + "/health/v2", + "/meeting/v2/org-user/**", + "/auth/v2", + "/auth/v2/**", + actuatorEndPoint + "/health" + }; + } - @Bean - @Profile("dev") - SecurityFilterChain devSecurityFilterChain(HttpSecurity http) throws Exception { - http.csrf((csrfConfig) -> csrfConfig.disable()) - .cors(Customizer.withDefaults()) - .sessionManagement( - (sessionManagement) -> sessionManagement.sessionCreationPolicy( - SessionCreationPolicy.STATELESS)) - .authorizeHttpRequests( - authorize -> authorize.requestMatchers(Stream - .of(SWAGGER_URL) - .map(AntPathRequestMatcher::antMatcher) - .toArray(AntPathRequestMatcher[]::new)).permitAll() - .requestMatchers(Stream - .of(AUTH_WHITELIST) - .map(AntPathRequestMatcher::antMatcher) - .toArray(AntPathRequestMatcher[]::new)).permitAll() - .anyRequest().authenticated()) - .addFilterBefore( - new JwtAuthenticationFilter(this.jwtTokenProvider, - this.jwtAuthenticationEntryPoint), - UsernamePasswordAuthenticationFilter.class) - .exceptionHandling(exceptionHandling -> exceptionHandling - .authenticationEntryPoint(this.jwtAuthenticationEntryPoint)); - return http.build(); - } + @Bean + @Profile("dev") + SecurityFilterChain devSecurityFilterChain(HttpSecurity http) throws Exception { + http.csrf((csrfConfig) -> csrfConfig.disable()) + .cors(Customizer.withDefaults()) + .sessionManagement( + (sessionManagement) -> sessionManagement.sessionCreationPolicy( + SessionCreationPolicy.STATELESS)) + .authorizeHttpRequests( + authorize -> authorize + .requestMatchers(Stream + .of(SWAGGER_URL) + .map(AntPathRequestMatcher::antMatcher) + .toArray(AntPathRequestMatcher[]::new)).permitAll() + .requestMatchers(Stream + .of(getAuthWhitelist()) + .map(AntPathRequestMatcher::antMatcher) + .toArray(AntPathRequestMatcher[]::new)).permitAll() + .anyRequest().authenticated()) + .addFilterBefore( + new JwtAuthenticationFilter(this.jwtTokenProvider, + this.jwtAuthenticationEntryPoint), + UsernamePasswordAuthenticationFilter.class) + .exceptionHandling(exceptionHandling -> exceptionHandling + .authenticationEntryPoint(this.jwtAuthenticationEntryPoint)); + return http.build(); + } - @Bean - @Profile("test") - SecurityFilterChain testSecurityFilterChain(HttpSecurity http) throws Exception { - http.csrf((csrfConfig) -> csrfConfig.disable()) - .cors(Customizer.withDefaults()) - .sessionManagement( - (sessionManagement) -> sessionManagement.sessionCreationPolicy( - SessionCreationPolicy.STATELESS)) - .authorizeHttpRequests( - authorize -> authorize.requestMatchers(Stream - .of(SWAGGER_URL) - .map(AntPathRequestMatcher::antMatcher) - .toArray(AntPathRequestMatcher[]::new)).permitAll() - .requestMatchers(Stream - .of(AUTH_WHITELIST) - .map(AntPathRequestMatcher::antMatcher) - .toArray(AntPathRequestMatcher[]::new)).permitAll() - .anyRequest().authenticated()) - .addFilterBefore( - new JwtAuthenticationFilter(this.jwtTokenProvider, - this.jwtAuthenticationEntryPoint), - UsernamePasswordAuthenticationFilter.class) - .exceptionHandling(exceptionHandling -> exceptionHandling - .authenticationEntryPoint(this.jwtAuthenticationEntryPoint)); - return http.build(); - } + @Bean + @Profile("test") + SecurityFilterChain testSecurityFilterChain(HttpSecurity http) throws Exception { + http.csrf((csrfConfig) -> csrfConfig.disable()) + .cors(Customizer.withDefaults()) + .sessionManagement( + (sessionManagement) -> sessionManagement.sessionCreationPolicy( + SessionCreationPolicy.STATELESS)) + .authorizeHttpRequests( + authorize -> authorize + .requestMatchers(Stream + .of(SWAGGER_URL) + .map(AntPathRequestMatcher::antMatcher) + .toArray(AntPathRequestMatcher[]::new)).permitAll() + .requestMatchers(Stream + .of(getAuthWhitelist()) + .map(AntPathRequestMatcher::antMatcher) + .toArray(AntPathRequestMatcher[]::new)).permitAll() + .anyRequest().authenticated()) + .addFilterBefore( + new JwtAuthenticationFilter(this.jwtTokenProvider, + this.jwtAuthenticationEntryPoint), + UsernamePasswordAuthenticationFilter.class) + .exceptionHandling(exceptionHandling -> exceptionHandling + .authenticationEntryPoint(this.jwtAuthenticationEntryPoint)); + return http.build(); + } - @Bean - @Profile("prod") - SecurityFilterChain prodSecurityFilterChain(HttpSecurity http) throws Exception { - http.csrf((csrfConfig) -> csrfConfig.disable()) - .cors(Customizer.withDefaults()) - .sessionManagement( - (sessionManagement) -> sessionManagement.sessionCreationPolicy( - SessionCreationPolicy.STATELESS)) - .authorizeHttpRequests( - authorize -> authorize.requestMatchers(Stream - .of(SWAGGER_URL) - .map(AntPathRequestMatcher::antMatcher) - .toArray(AntPathRequestMatcher[]::new)).permitAll() - .requestMatchers(Stream - .of(AUTH_WHITELIST) - .map(AntPathRequestMatcher::antMatcher) - .toArray(AntPathRequestMatcher[]::new)).permitAll() - .anyRequest().authenticated()) - .addFilterBefore( - new JwtAuthenticationFilter(this.jwtTokenProvider, - this.jwtAuthenticationEntryPoint), - UsernamePasswordAuthenticationFilter.class) - .exceptionHandling(exceptionHandling -> exceptionHandling - .authenticationEntryPoint(this.jwtAuthenticationEntryPoint)); - return http.build(); - } + @Bean + @Profile("prod") + SecurityFilterChain prodSecurityFilterChain(HttpSecurity http) throws Exception { + http.csrf((csrfConfig) -> csrfConfig.disable()) + .cors(Customizer.withDefaults()) + .sessionManagement( + (sessionManagement) -> sessionManagement.sessionCreationPolicy( + SessionCreationPolicy.STATELESS)) + .authorizeHttpRequests( + authorize -> authorize + .requestMatchers(Stream + .of(SWAGGER_URL) + .map(AntPathRequestMatcher::antMatcher) + .toArray(AntPathRequestMatcher[]::new)).permitAll() + .requestMatchers(Stream + .of(getAuthWhitelist()) + .map(AntPathRequestMatcher::antMatcher) + .toArray(AntPathRequestMatcher[]::new)).permitAll() + .anyRequest().authenticated()) + .addFilterBefore( + new JwtAuthenticationFilter(this.jwtTokenProvider, + this.jwtAuthenticationEntryPoint), + UsernamePasswordAuthenticationFilter.class) + .exceptionHandling(exceptionHandling -> exceptionHandling + .authenticationEntryPoint(this.jwtAuthenticationEntryPoint)); + return http.build(); + } - @Bean - CorsConfigurationSource corsConfigurationSource() { - CorsConfiguration configuration = new CorsConfiguration(); - configuration.setAllowedOrigins( - Arrays.asList("https://playground.sopt.org/", "http://localhost:3000/", - "https://sopt-internal-dev.pages.dev/", "https://crew.api.dev.sopt.org", "https://crew.api.prod.sopt.org")); - configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PATCH", "DELETE", "PUT", "OPTIONS")); - configuration.addAllowedHeader("*"); - configuration.setAllowCredentials(false); + @Bean + CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + configuration.setAllowedOrigins( + Arrays.asList("https://playground.sopt.org/", "http://localhost:3000/", + "https://sopt-internal-dev.pages.dev/", "https://crew.api.dev.sopt.org", + "https://crew.api.prod.sopt.org")); + configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PATCH", "DELETE", "PUT", "OPTIONS")); + configuration.addAllowedHeader("*"); + configuration.setAllowCredentials(false); - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - source.registerCorsConfiguration("/**", configuration); - return source; - } + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); + return source; + } } \ No newline at end of file diff --git a/main/src/main/resources/application-dev.yml b/main/src/main/resources/application-dev.yml index b406236b..431727d2 100644 --- a/main/src/main/resources/application-dev.yml +++ b/main/src/main/resources/application-dev.yml @@ -66,4 +66,28 @@ notice: playground: server: url: ${DEV_PLAYGROUND_URL} - endpoint: ${PLAYGROUND_ENDPOINT} \ No newline at end of file + endpoint: ${PLAYGROUND_ENDPOINT} + +management: + server: + port: ${ACTUATOR_PORT} + endpoints: + enabled-by-default: false + jmx: + exposure: + exclude: "*" + web: + exposure: + include: info, health, prometheus + base-path: ${ACTUATOR_PATH} + endpoint: + health: + enabled: true + info: + enabled: true + prometheus: + enabled: true + prometheus: + metrics: + export: + enabled: true \ No newline at end of file diff --git a/main/src/main/resources/application-prod.yml b/main/src/main/resources/application-prod.yml index 7b593c9c..84cc0c0b 100644 --- a/main/src/main/resources/application-prod.yml +++ b/main/src/main/resources/application-prod.yml @@ -65,4 +65,29 @@ notice: playground: server: url: ${PROD_PLAYGROUND_URL} - endpoint: ${PLAYGROUND_ENDPOINT} \ No newline at end of file + endpoint: ${PLAYGROUND_ENDPOINT} + + +management: + server: + port: ${ACTUATOR_PORT} + endpoints: + enabled-by-default: false + jmx: + exposure: + exclude: "*" + web: + exposure: + include: info, health, prometheus + base-path: ${ACTUATOR_PATH} + endpoint: + health: + enabled: true + info: + enabled: true + prometheus: + enabled: true + prometheus: + metrics: + export: + enabled: true \ No newline at end of file diff --git a/main/src/main/resources/application-test.yml b/main/src/main/resources/application-test.yml index 5e4abe55..a0e60eb0 100644 --- a/main/src/main/resources/application-test.yml +++ b/main/src/main/resources/application-test.yml @@ -69,4 +69,29 @@ notice: playground: server: url: ${DEV_PLAYGROUND_URL} - endpoint: ${PLAYGROUND_ENDPOINT} \ No newline at end of file + endpoint: ${PLAYGROUND_ENDPOINT} + + +management: + server: + port: ${ACTUATOR_PORT} + endpoints: + enabled-by-default: false + jmx: + exposure: + exclude: "*" + web: + exposure: + include: info, health, prometheus + base-path: ${ACTUATOR_PATH} + endpoint: + health: + enabled: true + info: + enabled: true + prometheus: + enabled: true + prometheus: + metrics: + export: + enabled: true \ No newline at end of file