web-dev-qa-db-fra.com

Sécurité de démarrage à ressort

J'ai un problème avec le filtre CORS sur les URL de sécurité Spring ..... Il ne définit pas Access-Control-Allow-Origin et les autres en-têtes exposés sur les URL appartenant à spring sec (connexion/déconnexion) ou filtré par Spring Security.

Voici les configurations.

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);
    }
}

Sécurité:

@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')");
    }
}

Donc, si je fais une demande aux URL qui ne sont pas écoutées par la sécurité - les en-têtes CORS sont définis. URL de sécurité Spring - non définies.

Botte de printemps 1.4.1

11
user1935987

Au lieu d'utiliser CorsRegistry, vous pouvez écrire votre propre CorsFilter et l'ajouter à votre configuration de sécurité.

Classe CorsFilter personnalisée:

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() {

    }
}

Classe de configuration de sécurité:

@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')");
    }
}
19
mika

Option 1 (utiliser le bean WebMvcConfigurer):

La configuration CORS avec laquelle vous avez commencé n'est pas la bonne façon de procéder avec Spring Boot. Vous devez enregistrer un bean WebMvcConfigurer. Référence ici .

Exemple de configuration CORS Spring Boot:

@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");
            }
        };
    }

}

Ceci fournira la configuration CORS pour une application de base Spring Boot (sans sécurité). Notez que la prise en charge de CORS existe indépendamment de Spring Security.

Une fois que vous avez introduit Spring Security, vous devez enregistrer CORS avec votre configuration de sécurité. Spring Security est assez intelligent pour récupérer votre configuration CORS existante.

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

Option 2 (Utiliser le bean CorsConfigurationSource):

La première option que j'ai décrite est réellement dans la perspective d'ajouter Spring Security à une application existante. Si vous ajoutez Spring Security dès le début, la méthode décrite dans le document Spring Security Docs implique l’ajout d’un bean CorsConfigurationSource.

@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;
    }
}

J'ai un client Web React et mon API backend REST est en cours d'exécution Spring Boot Ver 1.5.2

Je voulais activer rapidement CORS sur toutes les demandes de route de contrôleur de mon client s'exécutant sur localhost:8080. Dans ma configuration de sécurité, j'ai simplement ajouté un @Bean de type FilterRegistrationBean et je l'ai fait fonctionner facilement. 

Voici le code:

@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**
        ....
  }

....
....

}

Vous pouvez consulter Spring Boot docs ici

2
sisanared

Si vous en avez besoin pour un développement local rapide, ajoutez simplement cette annotation à votre contrôleur. (les cours changent d'origines au besoin)

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

Vous pouvez également y parvenir avec un intercepteur.

Utilisez l'exception pour vous assurer de mettre fin au cycle de vie de la demande:

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

Ensuite, dans votre intercepteur, définissez les en-têtes pour toutes les demandes OPTIONS et émettez l'exception:

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);
    }
}

Enfin, appliquez l'intercepteur à tous les itinéraires:

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

Actuellement, les demandes OPTIONS sont bloquées par défaut si la sécurité est activée.

Ajoutez simplement un bean supplémentaire et les requêtes de contrôle en amont seront traitées correctement:

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

Veuillez noter que selon votre application, cela peut ouvrir la porte à des exploits potentiels.

Question ouverte pour une meilleure solution: https://github.com/spring-projects/spring-security/issues/4448

0
Dennis Kieselhorst