본문 바로가기

Spring boot

[Spring Security] Security Config 적용법 (Jwt Filter와 관계)

 

public class SecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
                .authorizeHttpRequests(requests ->
                                               requests.requestMatchers("/signUp", "/signIn", "/reissue").permitAll()
                                      )
    }
}

 

"저 주소들은 permitAll이니까 모든 요청을 허용한다." ( = jwt 검사를 안 한다. -> jwt filter 검증을 거치지 않는다.)라고 생각했다.

 

하지만 저 주소에 요청을 보내도 filter에서 jwt null exception이 잡혔습니다.

그래서 알게된,, 저 주소로 요청이 들어와도 Filter는 거친다. (= 모든 요청은 filter를 거친다.) 

이렇게 filter를 거치지 않으려면 permitAll()이 아니라 ignoring()을 사용했어야 한다..!

 

우선 해결은 filter에서 StringUtils.hasText(token)으로 token의 유무를 검사해서,

token이 있으면 Authentication을 만들어주고 token이 없으면 그대로 request가 지나가도록 했습니다.

 


 

 

Security Config의 각 기능을 제대로 알 필요가 있다고 생각했습니다.

URL Matchers 기능

  • antMatchers()
    • antMatchers("/signup").permitAll()
    • "/signup" 요청을 모두에게 허용
  • mvcMatchers()
    • mvcMatchers("/signup").permitAll()
    • "/signup", "/signup/", "/signup.html" 와 같은 유사 signup 요청을 모두에게 허용한다.
  • regexMatchers()
    • 정규표현식으로 매칭한다
  • requestMatchers
    • 위의 세개 모두 requestMatchers로 이루어져 있다.
    • 명확하게 요청 대상을 지정하는 경우에 requestMatchers를 사용한다.

 

인가 관련 설정

  • http.authorizeRequests()
    • 인가를 설정
  • permitAll()
    • http.authorizeRequests().antMatchers("/signup").permitAll()
    • "/signup" 요청을 모두에게 허용한다.
  • hasRole()
    • http.authorizeRequests().antMatchers(HttpMethod.POST, "/notice").hasRole("ADMIN")
    • 권한을 검증
  • authenticated()
    • 인증이 되었는지를 검증한다.
  • ignoring()
    • 특정 리소스에 대해서 SpringSecurity 자체를 적용하고 싶지 않을때
    • ignoring을 사용한 코드는 permitAll과 다르게 SpringSecurity의 대상에 포함하지 않는다.
    • 어떤 필터도 실행되지 않아 성능적으로 우수
    • 경고 메세지 : web.ignoring()은 Spring Security 가 해당 엔드포인트에 보안헤더 또는 기타 보호 조치를 제공할 수 없다. 대신에 permitAll() 메서드를 사용하여 권한 검증 없이 요청을 보호할 수 있다.
@Override
public void configure(WebSecurity web) {
	// 정적 리소스 spring security 대상에서 제외
	web.ignoring().antMatchers("/images/**", "/css/**"); // 아래 코드와 같은 코드
	web.ignoring().requestMatchers(PathRequest.toStaticResources().atCommonLocations());
}

 

 

Security config 적용

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {

    private final String[] allowedUrls =
            { "/", "/swagger-ui/**", "/swagger-resources/**", "/v3/**", "/signUp", "/signIn", "/reissue" };
    private final JwtAuthenticationFilter jwtAuthenticationFilter;
    private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
    private final JwtAccessDeniedHandler jwtAccessDeniedHandler;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
                .csrf(csrfConfig -> csrfConfig.disable())
                .cors(httpSecurityCorsConfigurer ->
                              httpSecurityCorsConfigurer.configurationSource(corsConfigurationSource()))
                .headers(headerConfig ->
                                 headerConfig.frameOptions(frameOptionsConfig -> frameOptionsConfig.disable())
                )
                .authorizeHttpRequests(requests ->
                                               requests.requestMatchers(allowedUrls).permitAll()
                                                       .requestMatchers(PathRequest.toH2Console()).permitAll()
                                                       .anyRequest().authenticated()
                )
                .addFilterBefore(jwtAuthenticationFilter, BasicAuthenticationFilter.class)
                .exceptionHandling(exceptionConfig ->
                                           exceptionConfig.accessDeniedHandler(jwtAccessDeniedHandler)
                                                          .authenticationEntryPoint(jwtAuthenticationEntryPoint)
                )
                .sessionManagement(sessionManagement ->   // 세션을 사용하지 않으므로 STATELESS 설정
                                           sessionManagement.sessionCreationPolicy(
                                                   SessionCreationPolicy.STATELESS)
                )
                .build();
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration config = new CorsConfiguration();

        config.setAllowCredentials(true);
        config.setAllowedOrigins(List.of("http://localhost:8080"));
        config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"));
        config.setAllowedHeaders(List.of("*"));
        config.setExposedHeaders(List.of("*"));

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return source;
    }
    
}

 

 

출처:

https://vanillacreamdonut.tistory.com/360