package com.zioinfo.mall.config;
import com.zioinfo.mall.auth.JwtFilter;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
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.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;
/**
* GUARDiA Mall 보안 — JWT 무상태 + RBAC.
*
*
* - 스토어프론트 조회(상품/카탈로그/리뷰 GET) — 공개
* - 장바구니/주문/결제/배송조회/찜/CS — 인증 고객
* - 상품/카탈로그/프로모션/정산 정의 변경 — MANAGER 이상
* - 관리자 API(/api/admin) — ADMIN (감사/설정 조회는 MANAGER 허용)
*
*/
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final JwtFilter jwtFilter;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.cors(cors -> {})
.sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/mall/auth/**").permitAll()
.requestMatchers("/actuator/health").permitAll()
.requestMatchers("/ws/mall/**").permitAll()
.requestMatchers("/swagger-ui/**", "/v3/api-docs/**", "/api/mall/docs/**", "/api/mall/swagger/**").permitAll()
// 스토어프론트 공개 조회 (상품·카탈로그·리뷰·기획전·매장·권역·스케줄 브라우징)
.requestMatchers(HttpMethod.GET, "/api/mall/product/**", "/api/mall/category/**",
"/api/mall/review/**", "/api/mall/promotion/**",
"/api/mall/store/**", "/api/mall/schedule/availability",
"/api/mall/schedule/holidays").permitAll()
// ZIP 진입 조회(공개) — 배송 가능 매장 판정
.requestMatchers(HttpMethod.GET, "/api/mall/zone/lookup").permitAll()
// 등급 혜택 안내(공개) + 진행중 이벤트/배너(공개 스토어프론트)
.requestMatchers(HttpMethod.GET, "/api/mall/loyalty/tiers", "/api/mall/loyalty/tiers/**",
"/api/mall/event/ongoing", "/api/mall/event/banners",
"/api/mall/event/{id}").permitAll()
// AI 스토어프론트(추천·리뷰요약·자연어검색·카드메시지) 공개
.requestMatchers(HttpMethod.GET, "/api/mall/ai/recommend", "/api/mall/ai/review-summary/**").permitAll()
.requestMatchers(HttpMethod.POST, "/api/mall/ai/nl-search", "/api/mall/ai/card-message").permitAll()
// 관리자 API
.requestMatchers("/api/admin/users/**").hasRole("ADMIN")
.requestMatchers("/api/admin/audit").hasAnyRole("ADMIN", "MANAGER")
.requestMatchers(HttpMethod.GET, "/api/admin/settings").hasAnyRole("ADMIN", "MANAGER")
.requestMatchers("/api/admin/settings/**").hasRole("ADMIN")
// 운영 분석 — MANAGER 이상
.requestMatchers("/api/mall/analytics/**").hasAnyRole("ADMIN", "MANAGER")
.requestMatchers("/api/mall/settlement/**").hasAnyRole("ADMIN", "MANAGER")
// 상품·카탈로그·프로모션·재고 정의 변경 — MANAGER 이상
.requestMatchers(HttpMethod.POST, "/api/mall/product/**", "/api/mall/category/**",
"/api/mall/promotion/**", "/api/mall/inventory/**").hasAnyRole("ADMIN", "MANAGER")
.requestMatchers(HttpMethod.PUT, "/api/mall/product/**", "/api/mall/category/**",
"/api/mall/promotion/**", "/api/mall/inventory/**").hasAnyRole("ADMIN", "MANAGER")
.requestMatchers(HttpMethod.DELETE, "/api/mall/product/**", "/api/mall/category/**",
"/api/mall/promotion/**", "/api/mall/inventory/**").hasAnyRole("ADMIN", "MANAGER")
// 그 외 mall API (장바구니·주문·결제·배송·찜·CS·회원·AI) — 인증 사용자
.requestMatchers("/api/mall/**").authenticated()
.anyRequest().authenticated()
)
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
@Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration cfg) throws Exception { return cfg.getAuthenticationManager(); }
}