1+ package com .demo .config ;
2+
3+ import com .demo .config .properties .JWTProperties ;
4+ import com .demo .jwt .JwtAccessDeniedHandler ;
5+ import com .demo .jwt .JwtAuthenticationEntryPoint ;
6+ import com .demo .jwt .TokenProvider ;
7+ import com .demo .jwt .TokenValidator ;
8+ import com .demo .repository .StudentRepository ;
9+ import com .demo .service .CustomLoginService ;
10+ import lombok .AccessLevel ;
11+ import lombok .RequiredArgsConstructor ;
12+ import lombok .extern .slf4j .Slf4j ;
13+ import org .springframework .boot .autoconfigure .security .servlet .PathRequest ;
14+ import org .springframework .context .annotation .Bean ;
15+ import org .springframework .context .annotation .Configuration ;
16+ import org .springframework .http .HttpMethod ;
17+ import org .springframework .security .authentication .AuthenticationManager ;
18+ import org .springframework .security .config .annotation .authentication .builders .AuthenticationManagerBuilder ;
19+ import org .springframework .security .config .annotation .method .configuration .EnableMethodSecurity ;
20+ import org .springframework .security .config .annotation .web .builders .HttpSecurity ;
21+ import org .springframework .security .config .annotation .web .configuration .EnableWebSecurity ;
22+ import org .springframework .security .config .annotation .web .configurers .AbstractHttpConfigurer ;
23+ import org .springframework .security .config .annotation .web .configurers .HeadersConfigurer ;
24+ import org .springframework .security .config .http .SessionCreationPolicy ;
25+ import org .springframework .security .crypto .bcrypt .BCryptPasswordEncoder ;
26+ import org .springframework .security .crypto .password .PasswordEncoder ;
27+ import org .springframework .security .web .SecurityFilterChain ;
28+ import org .springframework .security .web .authentication .UsernamePasswordAuthenticationFilter ;
29+ import org .springframework .web .filter .CorsFilter ;
30+ import org .springframework .web .cors .CorsUtils ;
31+
32+ @ Slf4j
33+ @ EnableWebSecurity
34+ @ EnableMethodSecurity
35+ @ Configuration
36+ @ RequiredArgsConstructor (access = AccessLevel .PROTECTED )
37+ public class SecurityConfig {
38+ private final CorsFilter corsFilter ;
39+ private final TokenProvider tokenProvider ;
40+ private final TokenValidator tokenValidator ;
41+ private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint ;
42+ private final JwtAccessDeniedHandler jwtAccessDeniedHandler ;
43+ private final JWTProperties jwtProperties ;
44+
45+ @ Bean
46+ public PasswordEncoder passwordEncoder () {
47+ return new BCryptPasswordEncoder ();
48+ }
49+
50+ @ Bean
51+ public CustomLoginService customLoginService (StudentRepository studentRepository ) {
52+ return new CustomLoginService (studentRepository );
53+ }
54+
55+ @ Bean
56+ public AuthenticationManager authenticationManager (HttpSecurity http , PasswordEncoder passwordEncoder , CustomLoginService customLoginService ) throws Exception {
57+ return http .getSharedObject (AuthenticationManagerBuilder .class )
58+ .userDetailsService (customLoginService )
59+ .passwordEncoder (passwordEncoder )
60+ .and ()
61+ .build ();
62+ }
63+
64+ @ Bean
65+ public SecurityFilterChain filterChain (HttpSecurity http ) throws Exception {
66+ http .csrf (AbstractHttpConfigurer ::disable )
67+ .addFilterBefore (corsFilter , UsernamePasswordAuthenticationFilter .class )
68+ .exceptionHandling (exception -> {
69+ exception .accessDeniedHandler (jwtAccessDeniedHandler )
70+ .authenticationEntryPoint (jwtAuthenticationEntryPoint );
71+ })
72+ .authorizeHttpRequests (request -> {
73+ request
74+ // UI 라우트: 비로그인 허용
75+ .requestMatchers ("/" , "/sign-in" , "/sign-up" ).permitAll ()
76+ .requestMatchers ("/posts" , "/posts/*" , "/posts/edit/*" ).permitAll ()
77+ .requestMatchers ("/js/**" , "/css/**" , "/images/**" , "/favicon.ico" , "/error" ).permitAll ()
78+
79+ // API 조회는 비로그인 허용
80+ .requestMatchers (HttpMethod .GET , "/api/v1/posts/**" ).permitAll ()
81+
82+ // API 쓰기 작업은 인증 필요
83+ .requestMatchers (HttpMethod .POST , "/api/v1/posts/**" ).authenticated ()
84+ .requestMatchers (HttpMethod .PUT , "/api/v1/posts/**" ).authenticated ()
85+ .requestMatchers (HttpMethod .DELETE , "/api/v1/posts/**" ).authenticated ()
86+
87+ // 나머지 기존 정책
88+ .requestMatchers ("/api/v1/auth/login" , "/api/v1/auth/register" , "/api/v1/auth/check-id" ).permitAll ()
89+ .requestMatchers ("/api/v1/admin/**" ).hasRole ("ADMIN" )
90+ .requestMatchers ("/api/v1/students/**" ).hasAnyRole ("ADMIN" , "USER" )
91+ .requestMatchers (org .springframework .web .cors .CorsUtils ::isPreFlightRequest ).permitAll ()
92+ .anyRequest ().authenticated ();
93+ })
94+ .sessionManagement (session -> {
95+ session .sessionCreationPolicy (SessionCreationPolicy .STATELESS );
96+ })
97+ .headers (headers -> {
98+ headers .frameOptions (HeadersConfigurer .FrameOptionsConfig ::sameOrigin );
99+ })
100+ .with (new JwtSecurityConfig (tokenProvider , tokenValidator , jwtProperties ), customizer -> {
101+ });
102+ return http .build ();
103+ }
104+ }
0 commit comments