web-dev-qa-db-fra.com

Comment installer le filtre Access-Control-Allow-Origin de manière problématique dans Spring Security 3.2

J'essaie de configurer mon serveur Spring avec Spring Security 3.2 pour pouvoir faire une demande de connexion ajax.

J'ai suivi la Spring Security 3.2 video et quelques posts mais le problème est que je reçois 

 No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:9000' is therefore not allowed access. 

Pour les demandes de connexion (voir ci-dessous). 

J'ai créé une configuration CORSFilter et je peux accéder aux ressources non protégées de mon système avec les en-têtes appropriés ajoutés à la réponse. 

Mon hypothèse est que je n’ajoute pas la CORSFilter à la chaîne de filtrage de sécurité ou qu’il est peut-être trop tard dans la chaîne. Toute idée sera appréciée. 

WebAppInitializer

public class WebAppInitializer implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext) {
        WebApplicationContext rootContext = createRootContext(servletContext);

        configureSpringMvc(servletContext, rootContext);

        FilterRegistration.Dynamic corsFilter = servletContext.addFilter("corsFilter", CORSFilter.class);
        corsFilter.addMappingForUrlPatterns(null, false, "/*");
    }

    private WebApplicationContext createRootContext(ServletContext servletContext) {
        AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();

        rootContext.register(SecurityConfig.class, PersistenceConfig.class, CoreConfig.class);

        servletContext.addListener(new ContextLoaderListener(rootContext));
        servletContext.setInitParameter("defaultHtmlEscape", "true");

        return rootContext;
    }


    private void configureSpringMvc(ServletContext servletContext, WebApplicationContext rootContext) {
        AnnotationConfigWebApplicationContext mvcContext = new AnnotationConfigWebApplicationContext();
        mvcContext.register(MVCConfig.class);

        mvcContext.setParent(rootContext);
        ServletRegistration.Dynamic appServlet = servletContext.addServlet(
                "webservice", new DispatcherServlet(mvcContext));
        appServlet.setLoadOnStartup(1);
        Set<String> mappingConflicts = appServlet.addMapping("/api/*");

        if (!mappingConflicts.isEmpty()) {
            for (String s : mappingConflicts) {
                LOG.error("Mapping conflict: " + s);
            }
            throw new IllegalStateException(
                    "'webservice' cannot be mapped to '/'");
        }
    }

SecurityWebAppInitializer:

public class SecurityWebAppInitializer extends AbstractSecurityWebApplicationInitializer {
}

SecurityConfig:

Les demandes à / api/users - fonctionnent bien et les en-têtes Access-Control-Allow sont ajoutés. J'ai désactivé csrf et les en-têtes pour que ce ne soit pas le cas. 

@EnableWebMvcSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
    protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("user").password("password").roles("USER");
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .headers().disable()                
            .authorizeRequests()
                    .antMatchers("/api/users/**").permitAll()
                    .anyRequest().authenticated()
                    .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()
                .permitAll();
    }

CORFilter:  

@Component
public class CORSFilter implements Filter{
    static Logger logger = LoggerFactory.getLogger(CORSFilter.class);

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

    @Override
    public void doFilter(ServletRequest request, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
        chain.doFilter(request, response);
    }

    public void destroy() {}
}

Demande de connexion:  

Request URL:http://localhost:8080/devstage-1.0/login
Request Headers CAUTION: Provisional headers are shown.
Accept:application/json, text/plain, */*
Cache-Control:no-cache
Content-Type:application/x-www-form-urlencoded
Origin:http://127.0.0.1:9000
Pragma:no-cache
Referer:http://127.0.0.1:9000/
User-Agent:Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.154 Safari/537.36
Form Dataview sourceview URL encoded
username:user
password:password
13
special0ne

Tout ce qui me manquait était AddFilterBefore lors de la configuration de la configuration de la sécurité. 

La version finale était donc:

@EnableWebMvcSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

  @Autowired
  protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
        .withUser("user").password("password").roles("USER");
  }


  @Override
  protected void configure(HttpSecurity http) throws Exception {
      http
          .addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class)

          .formLogin()
              .loginPage("/login")
              .and()
          .authorizeRequests()
              .anyRequest().authenticated();

Et supprimez le CORSFilter de WebAppInitializer

17
special0ne

Je sais qu'il est un peu trop tard pour répondre à votre question, mais une réflexion pourrait valoir la peine d'être partagée. Dans votre configuration initiale, vous aviez enregistré les métadonnées de configuration de l'initialiseur Spring Security avec le contexte racine:

rootContext.register(SecurityConfig.class, PersistenceConfig.class, CoreConfig.class);

Lorsque vous pouvez faire cela, vous n’avez pas besoin de le faire car cela va coupler la chaîne de filtres de sécurité avec le contexte d’application Web qui n’est pas requis. Au lieu de cela, vous pouvez simplement ajouter la chaîne de filtres de la manière la plus ancienne en enregistrant DelegatingFilterProxy en tant que filtre. Bien sûr, vous devez maintenir l'ordre en ajoutant le filtre Cors avant d'ajouter la chaîne du filtre de sécurité à ressort.

De cette façon, vous pourrez utiliser le CorsFilter stock (simplement en ajoutant init params) fourni avec le package org.Apache.catalina.filters. Quoi qu'il en soit, vous pouvez aussi rester avec votre propre configuration! :)

0
Aaron Stone