diff --git a/.env.example b/.env.example index 3a2f8b41..e333c26b 100644 --- a/.env.example +++ b/.env.example @@ -5,6 +5,10 @@ JWT_ISSUER=stream-server # Access Token 만료 시간 (ms) JWT_ACCESS_TOKEN_EXPIRY=3600000 +### gateway:auth — CORS ### +# 허용할 origin (쉼표 구분). 운영 환경에는 localhost를 넣지 않는다 +CORS_ALLOWED_ORIGINS=http://localhost:5173 + ### infrastructure:db — MySQL ### DB_URL=jdbc:mysql://localhost:3306/stream?serverTimezone=Asia/Seoul&characterEncoding=UTF-8 DB_USERNAME=stream diff --git a/bootstrap/src/test/resources/application-gateway-auth.yml b/bootstrap/src/test/resources/application-gateway-auth.yml index 26ee8b54..cabd4446 100644 --- a/bootstrap/src/test/resources/application-gateway-auth.yml +++ b/bootstrap/src/test/resources/application-gateway-auth.yml @@ -4,3 +4,6 @@ jwt: secret-key: test-secret-key-must-be-at-least-32-bytes-long issuer: stream-server access-token-expiry: 3600000 + +cors: + allowed-origins: http://localhost:5173 diff --git a/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/config/CorsProperties.java b/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/config/CorsProperties.java new file mode 100644 index 00000000..4d9ead2a --- /dev/null +++ b/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/config/CorsProperties.java @@ -0,0 +1,9 @@ +package kr.ac.kookmin.stream.security.config; + +import java.util.List; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties(prefix = "cors") +public record CorsProperties( + List allowedOrigins +) {} diff --git a/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/config/SecurityConfig.java b/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/config/SecurityConfig.java index c980b1d9..0b124a42 100644 --- a/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/config/SecurityConfig.java +++ b/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/config/SecurityConfig.java @@ -1,5 +1,6 @@ package kr.ac.kookmin.stream.security.config; +import java.util.List; import kr.ac.kookmin.stream.common.Role; import kr.ac.kookmin.stream.security.PublicEndpoints; import kr.ac.kookmin.stream.security.handler.RestAccessDeniedHandler; @@ -9,12 +10,16 @@ import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpHeaders; 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.web.SecurityFilterChain; import org.springframework.security.web.access.intercept.AuthorizationFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; @Configuration @EnableWebSecurity @@ -24,10 +29,12 @@ public class SecurityConfig { private final JwtProvider jwtProvider; private final RestAuthenticationEntryPoint authenticationEntryPoint; private final RestAccessDeniedHandler accessDeniedHandler; + private final CorsProperties corsProperties; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { return http + .cors(cors -> cors.configurationSource(corsConfigurationSource())) .csrf(AbstractHttpConfigurer::disable) .formLogin(AbstractHttpConfigurer::disable) .httpBasic(AbstractHttpConfigurer::disable) @@ -44,4 +51,15 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .addFilterBefore(JwtAuthFilter.of(jwtProvider), AuthorizationFilter.class) .build(); } + + private CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + configuration.setAllowedOrigins(corsProperties.allowedOrigins()); + configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS")); + configuration.setAllowedHeaders(List.of(HttpHeaders.AUTHORIZATION, HttpHeaders.CONTENT_TYPE)); + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); + return source; + } } diff --git a/gateway/auth/src/main/resources/application-gateway-auth.yml b/gateway/auth/src/main/resources/application-gateway-auth.yml index f209f038..0de731c0 100644 --- a/gateway/auth/src/main/resources/application-gateway-auth.yml +++ b/gateway/auth/src/main/resources/application-gateway-auth.yml @@ -2,3 +2,6 @@ jwt: secret-key: ${JWT_SECRET_KEY} issuer: ${JWT_ISSUER} access-token-expiry: ${JWT_ACCESS_TOKEN_EXPIRY:3600000} + +cors: + allowed-origins: ${CORS_ALLOWED_ORIGINS}