55import org .springframework .beans .factory .annotation .Value ;
66import org .springframework .context .annotation .Bean ;
77import org .springframework .context .annotation .Configuration ;
8+ import org .springframework .context .annotation .Profile ;
9+ import org .springframework .core .annotation .Order ;
810import org .springframework .http .HttpHeaders ;
911import org .springframework .http .MediaType ;
1012import org .springframework .security .config .annotation .web .builders .HttpSecurity ;
1113import org .springframework .security .config .annotation .web .configuration .EnableWebSecurity ;
14+ import org .springframework .security .config .annotation .web .configurers .AbstractHttpConfigurer ;
1215import org .springframework .security .crypto .bcrypt .BCryptPasswordEncoder ;
1316import org .springframework .security .crypto .password .PasswordEncoder ;
1417import org .springframework .security .web .SecurityFilterChain ;
@@ -32,28 +35,79 @@ public class SecurityConfig {
3235 @ Value ("${compiler.python.url}" )
3336 private String compilerPythonUrl ;
3437
38+ // 1. JWT 인증이 필요한 API를 위한 필터 체인 (우선순위 1)
3539 @ Bean
36- public SecurityFilterChain filterChain (HttpSecurity http ) throws Exception {
40+ @ Order (1 )
41+ @ Profile ("!dev" )
42+ public SecurityFilterChain jwtFilterChain (HttpSecurity http ) throws Exception {
3743 http
44+ .securityMatcher ("/api/posts/**" , "/api/notifications/**" , "/api/admin/**" , "/api/report/**" , "/api/comments/**" ) // 이 경로들에 대해서만 이 필터 체인을 적용
3845 .cors (cors -> cors .configurationSource (corsConfigurationSource ()))
39- .csrf (csrf -> csrf . disable () )
46+ .csrf (AbstractHttpConfigurer :: disable )
4047 .authorizeHttpRequests (auth -> auth
48+ .requestMatchers ("/api/admin/**" ).hasRole ("ADMIN" )
49+ .requestMatchers ("/api/posts/**" ).hasAnyRole ("USER" , "ADMIN" )
50+ .requestMatchers ("/api/notifications/**" ).hasAnyRole ("USER" , "ADMIN" )
51+ .requestMatchers ("/api/report/**" ).hasAnyRole ("USER" , "ADMIN" )
52+ .requestMatchers ("/api/comments/**" ).hasAnyRole ("USER" , "ADMIN" )
53+ .anyRequest ().authenticated ()
54+ )
55+ .addFilterBefore (new JwtAuthenticationFilter (jwtTokenProvider , userRepository ),
56+ UsernamePasswordAuthenticationFilter .class )
57+ .formLogin (AbstractHttpConfigurer ::disable )
58+ .httpBasic (AbstractHttpConfigurer ::disable );
59+
60+ return http .build ();
61+ }
62+
63+ // 2. JWT 인증이 필요 없는 API 및 기타 경로를 위한 필터 체인 (우선순위 2)
64+ @ Bean
65+ @ Order (2 )
66+ @ Profile ("!dev" )
67+ public SecurityFilterChain publicFilterChain (HttpSecurity http ) throws Exception {
68+ http
69+ // securityMatcher를 지정하지 않으면 나머지 모든 요청을 처리합니다.
70+ .cors (cors -> cors .configurationSource (corsConfigurationSource ()))
71+ .csrf (AbstractHttpConfigurer ::disable )
72+ .authorizeHttpRequests (auth -> auth
73+ // 로그인, 회원가입, Swagger 등 인증이 필요 없는 경로는 여기서 permitAll() 처리
4174 .requestMatchers (
4275 "/api/users/login" ,
4376 "/api/users/signup" ,
4477 "/swagger-ui/**" ,
45- "/v3/api-docs/**"
78+ "/v3/api-docs/**" ,
79+ "/api/code/**"
4680 ).permitAll ()
47- .requestMatchers ("/api/admin/**" ).hasRole ("ADMIN" )
48- .requestMatchers ("/api/posts/**" ).hasAnyRole ("USER" , "ADMIN" )
49- .requestMatchers ("/api/notifications/**" ).hasAnyRole ("USER" , "ADMIN" )
81+ .anyRequest ().permitAll ()
82+ );
83+
84+ return http .build ();
85+ }
86+
87+ // "dev" 프로파일에서 사용할 보안 설정
88+ @ Bean
89+ @ Profile ("dev" )
90+ public SecurityFilterChain devSecurityFilterChain (HttpSecurity http ) throws Exception {
91+ http
92+ .cors (cors -> cors .configurationSource (corsConfigurationSource ()))
93+ .csrf (AbstractHttpConfigurer ::disable )
94+ .authorizeHttpRequests (auth -> auth
95+ // dev 환경에서 인증 없이 접근을 허용할 경로
96+ .requestMatchers (
97+ "/api/admin/**" ,
98+ "/api/users/login" ,
99+ "/api/users/signup" ,
100+ "/swagger-ui/**" ,
101+ "/v3/api-docs/**" ,
102+ "/api/code/**"
103+ ).permitAll ()
104+ // 그 외 모든 요청은 인증을 요구하도록 설정 (!dev 환경과 유사하게)
50105 .anyRequest ().authenticated ()
51106 )
52107 .addFilterBefore (new JwtAuthenticationFilter (jwtTokenProvider , userRepository ),
53108 UsernamePasswordAuthenticationFilter .class )
54- .formLogin (form -> form .disable ())
55- .httpBasic (basic -> basic .disable ());
56-
109+ .formLogin (AbstractHttpConfigurer ::disable )
110+ .httpBasic (AbstractHttpConfigurer ::disable );
57111 return http .build ();
58112 }
59113
@@ -74,8 +128,6 @@ public WebClient webClient() {
74128 public CorsConfigurationSource corsConfigurationSource () {
75129 CorsConfiguration config = new CorsConfiguration ();
76130 config .setAllowedOriginPatterns (List .of ("*" ));
77- // 또는 특정 도메인만 허용할 경우:
78- // config.setAllowedOrigins(List.of("https://zivorp.com", "http://zivorp.com"));
79131 config .setAllowedMethods (List .of ("GET" , "POST" , "PUT" , "DELETE" , "OPTIONS" ));
80132 config .setAllowedHeaders (List .of ("*" ));
81133 config .setAllowCredentials (true );
@@ -84,4 +136,4 @@ public CorsConfigurationSource corsConfigurationSource() {
84136 source .registerCorsConfiguration ("/**" , config );
85137 return source ;
86138 }
87- }
139+ }
0 commit comments