66import org .springframework .context .annotation .Bean ;
77import org .springframework .context .annotation .Configuration ;
88import org .springframework .context .annotation .Profile ;
9- import org .springframework .core .annotation .Order ;
109import org .springframework .http .HttpHeaders ;
10+ import org .springframework .http .HttpMethod ;
1111import org .springframework .http .MediaType ;
1212import org .springframework .security .config .annotation .web .builders .HttpSecurity ;
1313import org .springframework .security .config .annotation .web .configuration .EnableWebSecurity ;
1414import org .springframework .security .config .annotation .web .configurers .AbstractHttpConfigurer ;
15+ import org .springframework .security .config .http .SessionCreationPolicy ;
1516import org .springframework .security .crypto .bcrypt .BCryptPasswordEncoder ;
1617import org .springframework .security .crypto .password .PasswordEncoder ;
1718import org .springframework .security .web .SecurityFilterChain ;
@@ -35,85 +36,81 @@ public class SecurityConfig {
3536 @ Value ("${compiler.python.url}" )
3637 private String compilerPythonUrl ;
3738
38- // 1. JWT 인증이 필요한 API를 위한 필터 체인 (우선순위 1)
39+ /**
40+ * "dev" 프로파일 (개발 환경)을 위한 보안 설정
41+ * ADMIN 권한 체크를 제외하여 USER 권한으로도 ADMIN API 테스트가 가능합니다.
42+ */
3943 @ Bean
40- @ Order (1 )
41- @ Profile ("!dev" )
42- public SecurityFilterChain jwtFilterChain (HttpSecurity http ) throws Exception {
44+ @ Profile ("dev" )
45+ public SecurityFilterChain devSecurityFilterChain (HttpSecurity http ) throws Exception {
4346 http
44- .securityMatcher ("/api/posts/**" , "/api/notifications/**" , "/api/admin/**" , "/api/report/**" , "/api/comments/**" ) // 이 경로들에 대해서만 이 필터 체인을 적용
4547 .cors (cors -> cors .configurationSource (corsConfigurationSource ()))
4648 .csrf (AbstractHttpConfigurer ::disable )
49+ .sessionManagement (session -> session .sessionCreationPolicy (SessionCreationPolicy .STATELESS ))
50+ .formLogin (AbstractHttpConfigurer ::disable )
51+ .httpBasic (AbstractHttpConfigurer ::disable )
4752 .authorizeHttpRequests (auth -> auth
53+ // 1. 누구나 접근 가능한 경로
54+ .requestMatchers ("/swagger-ui/**" , "/v3/api-docs/**" ).permitAll ()
55+ .requestMatchers ("/api/users/login" , "/api/users/signup" ).permitAll ()
56+ .requestMatchers ("/api/code/**" ).permitAll ()
4857 .requestMatchers (HttpMethod .GET , "/api/posts/**" , "/api/comments/**" ).permitAll ()
49- .requestMatchers ("/api/admin/**" ).hasRole ("ADMIN" )
50- .requestMatchers ("/api/posts/**" ).hasAnyRole ("USER" , "ADMIN" )
51- .requestMatchers ("/api/notifications/**" ).hasAnyRole ("USER" , "ADMIN" )
52- .requestMatchers ("/api/report/**" ).hasAnyRole ("USER" , "ADMIN" )
53- .requestMatchers ("/api/comments/**" ).hasAnyRole ("USER" , "ADMIN" )
58+
59+ // 2. USER 권한이 필요한 경로
60+ .requestMatchers ("/api/posts/**" ).hasRole ("USER" )
61+ .requestMatchers ("/api/notifications/**" ).hasRole ("USER" )
62+ .requestMatchers ("/api/report/**" ).hasRole ("USER" )
63+ .requestMatchers ("/api/comments/**" ).hasRole ("USER" )
64+ .requestMatchers ("/api/files/upload" ).hasRole ("USER" )
65+
66+ // 3. 나머지 모든 요청은 인증된 사용자만 접근 가능 (ADMIN 경로 포함)
5467 .anyRequest ().authenticated ()
5568 )
5669 .addFilterBefore (new JwtAuthenticationFilter (jwtTokenProvider , userRepository ),
57- UsernamePasswordAuthenticationFilter .class )
58- .formLogin (AbstractHttpConfigurer ::disable )
59- .httpBasic (AbstractHttpConfigurer ::disable );
60-
70+ UsernamePasswordAuthenticationFilter .class );
6171 return http .build ();
6272 }
6373
64- // 2. JWT 인증이 필요 없는 API 및 기타 경로를 위한 필터 체인 (우선순위 2)
74+ /**
75+ * "prod", "default" 등 운영 환경을 위한 보안 설정
76+ * ADMIN 경로는 ADMIN 권한이 있는 사용자만 접근 가능합니다.
77+ */
6578 @ Bean
66- @ Order (2 )
6779 @ Profile ("!dev" )
68- public SecurityFilterChain publicFilterChain (HttpSecurity http ) throws Exception {
80+ public SecurityFilterChain defaultSecurityFilterChain (HttpSecurity http ) throws Exception {
6981 http
70- // securityMatcher를 지정하지 않으면 나머지 모든 요청을 처리합니다.
7182 .cors (cors -> cors .configurationSource (corsConfigurationSource ()))
7283 .csrf (AbstractHttpConfigurer ::disable )
84+ .sessionManagement (session -> session .sessionCreationPolicy (SessionCreationPolicy .STATELESS ))
85+ .formLogin (AbstractHttpConfigurer ::disable )
86+ .httpBasic (AbstractHttpConfigurer ::disable )
7387 .authorizeHttpRequests (auth -> auth
74- // 로그인, 회원가입, Swagger 등 인증이 필요 없는 경로는 여기서 permitAll() 처리
75- .requestMatchers (
76- "/api/users/login" ,
77- "/api/users/signup" ,
78- "/swagger-ui/**" ,
79- "/v3/api-docs/**" ,
80- "/api/code/**"
81- ).permitAll ()
88+ // 1. 누구나 접근 가능한 경로
89+ .requestMatchers ("/swagger-ui/**" , "/v3/api-docs/**" ).permitAll ()
90+ .requestMatchers ("/api/users/login" , "/api/users/signup" ).permitAll ()
91+ .requestMatchers ("/api/code/**" ).permitAll ()
8292 .requestMatchers (HttpMethod .GET , "/api/posts/**" , "/api/comments/**" ).permitAll ()
83- .anyRequest ().permitAll ()
84- );
8593
86- return http . build ();
87- }
94+ // 2. ADMIN 권한이 필요한 경로
95+ . requestMatchers ( "/api/admin/**" ). hasRole ( "ADMIN" )
8896
89- // "dev" 프로파일에서 사용할 보안 설정
90- @ Bean
91- @ Profile ("dev" )
92- public SecurityFilterChain devSecurityFilterChain (HttpSecurity http ) throws Exception {
93- http
94- .cors (cors -> cors .configurationSource (corsConfigurationSource ()))
95- .csrf (AbstractHttpConfigurer ::disable )
96- .authorizeHttpRequests (auth -> auth
97- .requestMatchers (HttpMethod .OPTIONS , "/**" ).permitAll () // CORS preflight 허용
98- // dev 환경에서 인증 없이 접근을 허용할 경로
99- .requestMatchers (
100- "/api/admin/**" ,
101- "/api/users/login" ,
102- "/api/users/signup" ,
103- "/swagger-ui/**" ,
104- "/v3/api-docs/**" ,
105- "/api/code/**"
106- ).permitAll ()
107- // 그 외 모든 요청은 인증을 요구하도록 설정 (!dev 환경과 유사하게)
97+ // 3. USER 권한이 필요한 경로
98+ .requestMatchers ("/api/posts/**" ).hasRole ("USER" )
99+ .requestMatchers ("/api/notifications/**" ).hasRole ("USER" )
100+ .requestMatchers ("/api/report/**" ).hasRole ("USER" )
101+ .requestMatchers ("/api/comments/**" ).hasRole ("USER" )
102+ .requestMatchers ("/api/files/upload" ).hasRole ("USER" )
103+
104+
105+ // 4. 나머지 모든 요청은 인증된 사용자만 접근 가능
108106 .anyRequest ().authenticated ()
109107 )
110108 .addFilterBefore (new JwtAuthenticationFilter (jwtTokenProvider , userRepository ),
111- UsernamePasswordAuthenticationFilter .class )
112- .formLogin (AbstractHttpConfigurer ::disable )
113- .httpBasic (AbstractHttpConfigurer ::disable );
109+ UsernamePasswordAuthenticationFilter .class );
114110 return http .build ();
115111 }
116112
113+ // --- 공통 Bean 설정 ---
117114 @ Bean
118115 public PasswordEncoder passwordEncoder () {
119116 return new BCryptPasswordEncoder ();
@@ -139,4 +136,4 @@ public CorsConfigurationSource corsConfigurationSource() {
139136 source .registerCorsConfiguration ("/**" , config );
140137 return source ;
141138 }
142- }
139+ }
0 commit comments