itsource

스프링 부트 보안 CORS

mycopycode 2023. 3. 18. 08:37
반응형

스프링 부트 보안 CORS

봄 보안 URL의 CORS 필터에 문제가 있습니다.설정되지 않다Access-Control-Allow-Origin스프링 초(로그인/로그아웃)에 속하거나 스프링 보안에 의해 필터링된 URL의 기타 노출된 헤더.

설정은 다음과 같습니다.

CORS:

@Configuration
@EnableWebMvc
public class MyWebMvcConfig extends WebMvcConfigurerAdapter {
********some irrelevant configs************
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/*").allowedOrigins("*").allowedMethods("GET", "POST", "OPTIONS", "PUT")
                .allowedHeaders("Content-Type", "X-Requested-With", "accept", "Origin", "Access-Control-Request-Method",
                        "Access-Control-Request-Headers")
                .exposedHeaders("Access-Control-Allow-Origin", "Access-Control-Allow-Credentials")
                .allowCredentials(true).maxAge(3600);
    }
}

보안:

@Configuration
@EnableWebSecurity
public class OAuth2SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).and()
                .formLogin()
                    .successHandler(ajaxSuccessHandler)
                    .failureHandler(ajaxFailureHandler)
                    .loginProcessingUrl("/authentication")
                    .passwordParameter("password")
                    .usernameParameter("username")
                .and()
                .logout()
                    .deleteCookies("JSESSIONID")
                    .invalidateHttpSession(true)
                    .logoutUrl("/logout")
                    .logoutSuccessUrl("/")
                .and()
                .csrf().disable()
                .anonymous().disable()
                .authorizeRequests()
                .antMatchers("/authentication").permitAll()
                .antMatchers("/oauth/token").permitAll()
                .antMatchers("/admin/*").access("hasRole('ROLE_ADMIN')")
                .antMatchers("/user/*").access("hasRole('ROLE_USER')");
    }
}

따라서 보안에서 수신하지 않는 URL에 요청을 하면 CORS 헤더가 설정됩니다.스프링 보안 URL이 설정되지 않았습니다.

스프링 부트 1.4.1

옵션 1(WebMvcConfigurer bean 사용):

처음에 사용한 CORS 설정은 Spring Boot에서 수행하는 적절한 방법이 아닙니다.를 등록해야 합니다.WebMvcConfigurer콩. 여기를 참고하세요.

스프링 부트 CORS 설정 예:

@Configuration
@Profile("dev")
public class DevConfig {

    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**").allowedOrigins("http://localhost:4200");
            }
        };
    }

}

이것에 의해, 기본적인(시큐러티 스타터 없음) 스프링 부트 애플리케이션의 CORS 설정이 가능하게 됩니다.CORS 지원은 스프링 보안과는 무관하게 존재합니다.

Spring Security를 도입하면 보안 설정에 CORS를 등록해야 합니다.Spring Security는 기존 CORS 구성을 선택할 수 있을 만큼 스마트합니다.

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .cors().and()              
         ....

옵션 2(Cors Configuration Source bean 사용):

첫 번째 옵션은 기존 애플리케이션에 Spring Security를 추가하는 것입니다.처음부터 Spring Security를 추가할 경우 Spring Security Docs에서 설명하는 방법에는 Cors Configuration Source bean을 추가합니다.

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // by default uses a Bean by the name of corsConfigurationSource
            .cors().and()
            ...
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
        configuration.setAllowedMethods(Arrays.asList("GET","POST"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}

CorsRegistry를 사용하는 대신 자체 CorsFilter를 작성하여 보안 구성에 추가할 수 있습니다.

커스텀 CorsFilter 클래스:

public class CorsFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        HttpServletRequest request= (HttpServletRequest) servletRequest;

        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "GET,POST,DELETE,PUT,OPTIONS");
        response.setHeader("Access-Control-Allow-Headers", "*");
        response.setHeader("Access-Control-Allow-Credentials", true);
        response.setHeader("Access-Control-Max-Age", 180);
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }
}

보안 설정 클래스:

@Configuration
@EnableWebSecurity
public class OAuth2SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Bean
    CorsFilter corsFilter() {
        CorsFilter filter = new CorsFilter();
        return filter;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .addFilterBefore(corsFilter(), SessionManagementFilter.class) //adds your custom CorsFilter
                .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).and()
                .formLogin()
                    .successHandler(ajaxSuccessHandler)
                    .failureHandler(ajaxFailureHandler)
                    .loginProcessingUrl("/authentication")
                    .passwordParameter("password")
                    .usernameParameter("username")
                .and()
                .logout()
                    .deleteCookies("JSESSIONID")
                    .invalidateHttpSession(true)
                    .logoutUrl("/logout")
                    .logoutSuccessUrl("/")
                .and()
                .csrf().disable()
                .anonymous().disable()
                .authorizeRequests()
                .antMatchers("/authentication").permitAll()
                .antMatchers("/oauth/token").permitAll()
                .antMatchers("/admin/*").access("hasRole('ROLE_ADMIN')")
                .antMatchers("/user/*").access("hasRole('ROLE_USER')");
    }
}

이것은 매우 깨끗하며 추가 구성이 필요하지 않습니다.모든 옵션을 유효하게 하는 위치에 별표를 전달합니다(setAllowed에서 한 것처럼).헤더).

@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Override
  protected void configure(HttpSecurity httpSecurity) throws Exception {
    httpSecurity.cors().configurationSource(request -> {
      var cors = new CorsConfiguration();
      cors.setAllowedOrigins(List.of("http://localhost:4200", "http://127.0.0.1:80", "http://example.com"));
      cors.setAllowedMethods(List.of("GET","POST", "PUT", "DELETE", "OPTIONS"));
      cors.setAllowedHeaders(List.of("*"));
      return cors;
    }).and()...
  }
}

나는 가지고 있다React기반 웹 클라이언트 및 백엔드 REST API가 실행되고 있습니다.Spring Boot버전 1.5.2

신속한 대응이 필요했습니다.CORS에서 실행되고 있는 클라이언트로부터의 모든 컨트롤러 루트 요구에서localhost:8080보안 설정 내에 단순히 1개의 IP 주소를 추가했을 뿐입니다.@Bean타입의FilterRegistrationBean쉽게 할 수 있었어요.

코드는 다음과 같습니다.

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class AuthConfiguration extends WebSecurityConfigurerAdapter {

....
....

  @Bean
  public FilterRegistrationBean corsFilter() {
    final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    CorsConfiguration config = new CorsConfiguration();
    config.setAllowCredentials(true);
    config.addAllowedOrigin(corsAllowedOrigin); // @Value: http://localhost:8080
    config.addAllowedHeader("*");
    config.addAllowedMethod("*");
    source.registerCorsConfiguration("/**", config);
    FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
    bean.setOrder(0);
    return bean;
  }

  @Override
  protected void configure(HttpSecurity httpSecurity) throws Exception {    
      httpSecurity
        .authorizeRequests()
        .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() // **permit OPTIONS call to all**
        ....
  }

....
....

}

Spring Boot 문서는 여기에서 참조할 수 있습니다.

비슷한 문제가 있었는데, 리액트의 프런트엔드에서 http://localhost:3000으로 실행되는 요청을 SpringBoot의 백엔드에서 http://localhost:8080으로 실행하려고 했습니다.두 가지 오류가 있었습니다.

접근통제 허용원점

이 문제는 Rest Controller에 추가함으로써 매우 쉽게 해결되었습니다.

@CrossOrigin(origins = ["http://localhost:3000"])

이것을 수정한 후, 다음의 에러가 표시되기 시작했습니다.응답의 'Access-Control-Allow-Credentials' 헤더 값은 'true'여야 합니다.

액세스 제어 허가 자격 정보

이것은, 다음의 2개의 방법으로 해결할 수 있습니다.

  1. " " " allowCredentials = "true"크로스 오리진

    @CrossOrigin(origins = ["http://localhost:3000"], allowCredentials = "true")

  2. 프런트 엔드 요청에서 가져오기 자격 증명 옵션 변경.기본적으로 다음과 같이 fetch 호출을 수행해야 합니다.

    fetch('http://localhost:8080/your/api', { credentials: 'same-origin' })

도움이 되길 바랍니다 =)

현재 보안이 설정되어 있는 경우 OPTIONS 요청은 기본적으로 차단됩니다.

bean을 추가하기만 하면 비행 전 요청이 올바르게 처리됩니다.

   @Bean
   public IgnoredRequestCustomizer optionsIgnoredRequestsCustomizer() {
      return configurer -> {
         List<RequestMatcher> matchers = new ArrayList<>();
         matchers.add(new AntPathRequestMatcher("/**", "OPTIONS"));
         configurer.requestMatchers(new OrRequestMatcher(matchers));
      };
   }

사용하시는 어플리케이션에 따라서는 악용 가능성이 있는 경우가 있습니다.

보다 나은 솔루션을 위해 발행된 호:https://github.com/spring-projects/spring-security/issues/4448

빠른 로컬 개발을 위해 필요한 경우 컨트롤러에 이 주석을 추가하십시오.(필요에 따라 코스 변경 원점)

@CrossOrigin(origins = "http://localhost:4200", maxAge = 3600)

요격기로도 이 작업을 수행할 수 있습니다.

다음 예외를 사용하여 요청 라이프사이클을 종료합니다.

@ResponseStatus (
    value = HttpStatus.NO_CONTENT
)
public class CorsException extends RuntimeException
{
}

그런 다음 인터셉터에서 모든 OPTIONS 요청의 헤더를 설정하고 예외를 슬로우합니다.

public class CorsMiddleware extends HandlerInterceptorAdapter
{
    @Override
    public boolean preHandle (
        HttpServletRequest request,
        HttpServletResponse response,
        Object handler
    ) throws Exception
    {
        if (request.getMethod().equals("OPTIONS")) {
            response.addHeader("Access-Control-Allow-Origin", "*");
            response.addHeader("Access-Control-Allow-Credentials", "true");
            response.addHeader("Access-Control-Allow-Methods","GET, POST, PUT, OPTIONS, DELETE");
            response.addHeader("Access-Control-Allow-Headers", "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,Authorization,If-Modified-Since,Cache-Control,Content-Type");
            response.addHeader("Access-Control-Max-Age", "3600");
            response.addHeader("charset", "utf-8");
            throw new CorsException();
        }

        return super.preHandle(request, response, handler);
    }
}

마지막으로 인터셉터를 모든 루트에 적용합니다.

@Configuration
public class MiddlewareConfig extends WebMvcConfigurerAdapter
{
    @Override
    public void addInterceptors (InterceptorRegistry registry)
    {
        registry.addInterceptor(new CorsMiddleware())
                .addPathPatterns("/**");
    }
}

2020년에도 같은 문제로 고민하는 사람이 있다면.이게 날 위해 한 일이야이 앱은 학습용 앱이기 때문에 모든 것을 활성화했습니다.

CorsFilter 클래스:

public class CorsFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Content-Length, X-Requested-With");
        chain.doFilter(req, res);
    }

    @Override
    public void destroy() {

    }
}

다음으로 Web Security Configurer Adapter를 확장하는 클래스의 헤더 셋업을 실시합니다.

@Configuration
@EnableWebSecurity
public class SpringSecurityConfigurationBasicAuth extends WebSecurityConfigurerAdapter {

@Bean
CorsFilter corsFilter() {
    CorsFilter filter = new CorsFilter();
    return filter;
}

    protected void configure(HttpSecurity http) throws Exception {
        System.out.println("Im configuring it");
        (
                (HttpSecurity)
                        (
                                (HttpSecurity)
                                        (
                                                (ExpressionUrlAuthorizationConfigurer.AuthorizedUrl)
                                                        http
                                                                .headers().addHeaderWriter(
                                                                new StaticHeadersWriter("Access-Control-Allow-Origin", "*")).and()
                                                                .addFilterBefore(corsFilter(), SessionManagementFilter.class)
                                                                .csrf().disable()
                                                                .authorizeRequests()
                                                                .antMatchers(HttpMethod.OPTIONS,"/**").permitAll()
                                                                .anyRequest()

                                        ).authenticated().and()
                        ).formLogin().and()
        ).httpBasic();
    }

}

아래 구성으로 시도했더니 작동했어요!

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable().cors().configurationSource(configurationSource()).and()
        .requiresChannel()
        .anyRequest()
        .requiresSecure();   
}


private CorsConfigurationSource configurationSource() {
      UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
      CorsConfiguration config = new CorsConfiguration();
      config.addAllowedOrigin("*");
      config.setAllowCredentials(true);
      config.addAllowedHeader("X-Requested-With");
      config.addAllowedHeader("Content-Type");
      config.addAllowedMethod(HttpMethod.POST);
      source.registerCorsConfiguration("/**", config);
      return source;
    }
 }

언급URL : https://stackoverflow.com/questions/40286549/spring-boot-security-cors

반응형