diff --git a/smarketing-ai/services/sns_content_service.py b/smarketing-ai/services/sns_content_service.py index f4a1d5e..c62baad 100644 --- a/smarketing-ai/services/sns_content_service.py +++ b/smarketing-ai/services/sns_content_service.py @@ -1380,7 +1380,7 @@ class SnsContentService: 'call_to_action': ['방문', '예약', '문의', '공감', '이웃추가'], 'image_placement_strategy': [ '매장 외관 → 인테리어 → 메뉴판 → 음식 → 분위기', - ##'텍스트 2-3문장마다 이미지 배치', + '텍스트 2-3문장마다 입력받은 이미지 배치', '이미지 설명은 간결하고 매력적으로', '마지막에 대표 이미지로 마무리' ] @@ -1791,6 +1791,7 @@ class SnsContentService: 네이버 검색에서 상위 노출되고, 실제로 도움이 되는 정보를 제공하는 블로그 포스트를 작성해주세요. 필수 요구사항을 반드시 참고하여 작성해주세요. +이미지 배치 위치를 [IMAGE_X] 태그로 명확히 표시해주세요. """ return prompt diff --git a/smarketing-java/ai-recommend/src/main/java/com/won/smarketing/recommend/config/SecurityConfig.java b/smarketing-java/ai-recommend/src/main/java/com/won/smarketing/recommend/config/SecurityConfig.java new file mode 100644 index 0000000..08a3949 --- /dev/null +++ b/smarketing-java/ai-recommend/src/main/java/com/won/smarketing/recommend/config/SecurityConfig.java @@ -0,0 +1,88 @@ +package com.won.smarketing.recommend.config; + +import com.won.smarketing.common.security.JwtAuthenticationFilter; +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.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.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +import java.util.Arrays; + +/** + * Spring Security 설정 클래스 + * JWT 기반 인증 및 CORS 설정 + */ +@Configuration +@EnableWebSecurity +@RequiredArgsConstructor +public class SecurityConfig +{ + + private final JwtAuthenticationFilter jwtAuthenticationFilter; + + @Value("${allowed-origins}") + private String allowedOrigins; + /** + * Spring Security 필터 체인 설정 + * + * @param http HttpSecurity 객체 + * @return SecurityFilterChain + * @throws Exception 예외 + */ + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .csrf(AbstractHttpConfigurer::disable) + .cors(cors -> cors.configurationSource(corsConfigurationSource())) + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .authorizeHttpRequests(auth -> auth + .requestMatchers("/api/auth/**", "/api/member/register", "/api/member/check-duplicate/**", + "/api/member/validate-password", "/swagger-ui/**", "/v3/api-docs/**", + "/swagger-resources/**", "/webjars/**", "/actuator/**", "/health/**", "/error" + ).permitAll() + .anyRequest().authenticated() + ) + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); + + return http.build(); + } + + /** + * 패스워드 인코더 빈 등록 + * + * @return BCryptPasswordEncoder + */ + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + /** + * CORS 설정 + * + * @return CorsConfigurationSource + */ + @Bean + public CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + configuration.setAllowedOrigins(Arrays.asList(allowedOrigins.split(","))); + configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); + configuration.setAllowedHeaders(Arrays.asList("*")); + configuration.setAllowCredentials(true); + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); + return source; + } +} diff --git a/smarketing-java/ai-recommend/src/main/resources/application.yml b/smarketing-java/ai-recommend/src/main/resources/application.yml index d392c82..16fd541 100644 --- a/smarketing-java/ai-recommend/src/main/resources/application.yml +++ b/smarketing-java/ai-recommend/src/main/resources/application.yml @@ -70,4 +70,6 @@ info: app: name: ${APP_NAME:smarketing-recommend} version: "1.0.0-MVP" - description: "AI 마케팅 서비스 MVP - recommend" \ No newline at end of file + description: "AI 마케팅 서비스 MVP - recommend" + +allowed-origins: ${ALLOWED_ORIGINS:http://localhost:3000} \ No newline at end of file diff --git a/smarketing-java/marketing-content/src/main/java/com/won/smarketing/content/config/SecurityConfig.java b/smarketing-java/marketing-content/src/main/java/com/won/smarketing/content/config/SecurityConfig.java new file mode 100644 index 0000000..ca74fc1 --- /dev/null +++ b/smarketing-java/marketing-content/src/main/java/com/won/smarketing/content/config/SecurityConfig.java @@ -0,0 +1,88 @@ +package com.won.smarketing.content.config; + +import com.won.smarketing.common.security.JwtAuthenticationFilter; +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.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.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +import java.util.Arrays; + +/** + * Spring Security 설정 클래스 + * JWT 기반 인증 및 CORS 설정 + */ +@Configuration +@EnableWebSecurity +@RequiredArgsConstructor +public class SecurityConfig +{ + + private final JwtAuthenticationFilter jwtAuthenticationFilter; + + @Value("${allowed-origins}") + private String allowedOrigins; + /** + * Spring Security 필터 체인 설정 + * + * @param http HttpSecurity 객체 + * @return SecurityFilterChain + * @throws Exception 예외 + */ + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .csrf(AbstractHttpConfigurer::disable) + .cors(cors -> cors.configurationSource(corsConfigurationSource())) + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .authorizeHttpRequests(auth -> auth + .requestMatchers("/api/auth/**", "/api/member/register", "/api/member/check-duplicate/**", + "/api/member/validate-password", "/swagger-ui/**", "/v3/api-docs/**", + "/swagger-resources/**", "/webjars/**", "/actuator/**", "/health/**", "/error" + ).permitAll() + .anyRequest().authenticated() + ) + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); + + return http.build(); + } + + /** + * 패스워드 인코더 빈 등록 + * + * @return BCryptPasswordEncoder + */ + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + /** + * CORS 설정 + * + * @return CorsConfigurationSource + */ + @Bean + public CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + configuration.setAllowedOrigins(Arrays.asList(allowedOrigins.split(","))); + configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); + configuration.setAllowedHeaders(Arrays.asList("*")); + configuration.setAllowCredentials(true); + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); + return source; + } +} diff --git a/smarketing-java/marketing-content/src/main/resources/application.yml b/smarketing-java/marketing-content/src/main/resources/application.yml index 819d127..64a7bac 100644 --- a/smarketing-java/marketing-content/src/main/resources/application.yml +++ b/smarketing-java/marketing-content/src/main/resources/application.yml @@ -67,4 +67,7 @@ info: app: name: ${APP_NAME:smarketing-content} version: "1.0.0-MVP" - description: "AI 마케팅 서비스 MVP - content" \ No newline at end of file + description: "AI 마케팅 서비스 MVP - content" + + +allowed-origins: ${ALLOWED_ORIGINS:http://localhost:3000} \ No newline at end of file diff --git a/smarketing-java/member/src/main/java/com/won/smarketing/member/config/SecurityConfig.java b/smarketing-java/member/src/main/java/com/won/smarketing/member/config/SecurityConfig.java new file mode 100644 index 0000000..293caad --- /dev/null +++ b/smarketing-java/member/src/main/java/com/won/smarketing/member/config/SecurityConfig.java @@ -0,0 +1,88 @@ +package com.won.smarketing.member.config; + +import com.won.smarketing.common.security.JwtAuthenticationFilter; +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.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.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +import java.util.Arrays; + +/** + * Spring Security 설정 클래스 + * JWT 기반 인증 및 CORS 설정 + */ +@Configuration +@EnableWebSecurity +@RequiredArgsConstructor +public class SecurityConfig +{ + + private final JwtAuthenticationFilter jwtAuthenticationFilter; + + @Value("${allowed-origins}") + private String allowedOrigins; + /** + * Spring Security 필터 체인 설정 + * + * @param http HttpSecurity 객체 + * @return SecurityFilterChain + * @throws Exception 예외 + */ + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .csrf(AbstractHttpConfigurer::disable) + .cors(cors -> cors.configurationSource(corsConfigurationSource())) + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .authorizeHttpRequests(auth -> auth + .requestMatchers("/api/auth/**", "/api/member/register", "/api/member/check-duplicate/**", + "/api/member/validate-password", "/swagger-ui/**", "/v3/api-docs/**", + "/swagger-resources/**", "/webjars/**", "/actuator/**", "/health/**", "/error" + ).permitAll() + .anyRequest().authenticated() + ) + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); + + return http.build(); + } + + /** + * 패스워드 인코더 빈 등록 + * + * @return BCryptPasswordEncoder + */ + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + /** + * CORS 설정 + * + * @return CorsConfigurationSource + */ + @Bean + public CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + configuration.setAllowedOrigins(Arrays.asList(allowedOrigins.split(","))); + configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); + configuration.setAllowedHeaders(Arrays.asList("*")); + configuration.setAllowCredentials(true); + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); + return source; + } +} diff --git a/smarketing-java/member/src/main/resources/application.yml b/smarketing-java/member/src/main/resources/application.yml index 46d38e4..6812fc3 100644 --- a/smarketing-java/member/src/main/resources/application.yml +++ b/smarketing-java/member/src/main/resources/application.yml @@ -54,3 +54,8 @@ info: name: ${APP_NAME:smarketing-member} version: "1.0.0-MVP" description: "AI 마케팅 서비스 MVP - member" +<<<<<<< HEAD +======= + +allowed-origins: ${ALLOWED_ORIGINS:http://localhost:3000} +>>>>>>> 2c1833ebe1d6be0746597e960dc5217605b2f867 diff --git a/smarketing-java/common/src/main/java/com/won/smarketing/common/config/SecurityConfig.java b/smarketing-java/store/src/main/java/com/won/smarketing/store/config/SecurityConfig.java similarity index 91% rename from smarketing-java/common/src/main/java/com/won/smarketing/common/config/SecurityConfig.java rename to smarketing-java/store/src/main/java/com/won/smarketing/store/config/SecurityConfig.java index 8bc7076..98fdc41 100644 --- a/smarketing-java/common/src/main/java/com/won/smarketing/common/config/SecurityConfig.java +++ b/smarketing-java/store/src/main/java/com/won/smarketing/store/config/SecurityConfig.java @@ -1,7 +1,8 @@ -package com.won.smarketing.common.config; +package com.won.smarketing.store.config; import com.won.smarketing.common.security.JwtAuthenticationFilter; 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.security.config.annotation.web.builders.HttpSecurity; @@ -25,10 +26,13 @@ import java.util.Arrays; @Configuration @EnableWebSecurity @RequiredArgsConstructor -public class SecurityConfig { +public class SecurityConfig +{ private final JwtAuthenticationFilter jwtAuthenticationFilter; + @Value("${allowed-origins}") + private String allowedOrigins; /** * Spring Security 필터 체인 설정 * @@ -72,7 +76,7 @@ public class SecurityConfig { @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); - configuration.setAllowedOriginPatterns(Arrays.asList("*")); + configuration.setAllowedOrigins(Arrays.asList(allowedOrigins.split(","))); configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); configuration.setAllowedHeaders(Arrays.asList("*")); configuration.setAllowCredentials(true); diff --git a/smarketing-java/store/src/main/resources/application.yml b/smarketing-java/store/src/main/resources/application.yml index 18a8934..42a2488 100644 --- a/smarketing-java/store/src/main/resources/application.yml +++ b/smarketing-java/store/src/main/resources/application.yml @@ -68,4 +68,6 @@ info: app: name: ${APP_NAME:smarketing-content} version: "1.0.0-MVP" - description: "AI 마케팅 서비스 MVP - content" \ No newline at end of file + description: "AI 마케팅 서비스 MVP - content" + +allowed-origins: ${ALLOWED_ORIGINS:http://localhost:3000} \ No newline at end of file