使用CORS在REST中使用Spring登录的问题

时间:2015-11-07 22:56:26

标签: spring rest spring-security cors

我正在尝试使用REST实现一个网站。我对用户进行身份验证的策略包括向用户发送JWT令牌以回复通过POST发送的用户名/密码组合。我的安全配置中最相关的部分如下所示。

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/images/**", "/scripts/**", "/styles/**", "favicon.ico");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf()
                .disable()
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
            .exceptionHandling()
                .authenticationEntryPoint(new RESTAuthenticationEntryPoint()).and()
            .formLogin()
                .successHandler(authenticationSuccessHandler())
                .failureHandler(new SimpleUrlAuthenticationFailureHandler())
                .loginProcessingUrl("/login") //Not necesary because is the default
                .permitAll().and()
            .authorizeRequests()
                .antMatchers("/api/getStatistics").permitAll()
                .anyRequest().denyAll().and()
            .addFilterBefore(new JwtTokenAuthenticationFilter(jWTTokenService()), UsernamePasswordAuthenticationFilter.class);
    }

    @Bean
    public SavedRequestAwareAuthenticationSuccessHandler authenticationSuccessHandler() {
        return new RESTAuthenticationSuccessHandler(jWTTokenService());
    }

    @Bean
    public JWTTokenService jWTTokenService() {
        return new JWTTokenServiceImpl();
    }

为了允许CORS访问,我在WebMvcConfigurerAdapter扩展的类中编写了以下行

@Override
public void addCorsMappings(CorsRegistry registry){
    registry.addMapping("/api/**")
                .allowedOrigins("*")
                .allowedHeaders("Origin", "X-Requested-With", "Content-Type", "Accept")
                .allowedMethods("GET", "POST", "OPTIONS")
                .allowCredentials(true).maxAge(3600);
    registry.addMapping("/login")
                .allowedOrigins("*")
                .allowedHeaders("Origin", "X-Requested-With", "Content-Type", "Accept")
                .allowedMethods("POST", "OPTIONS")
                .allowCredentials(true).maxAge(3600);
}

因此,当我调用/ login发送用户名和密码时,假设Spring将捕获请求,将处理它,然后将重定向到成功或失败处理程序。

嗯,而不是我在CORS预检期间得到了403 Forbidden响应。我决定调试该程序,因为我认为当我编写formLogin()时,UsernamePasswordAuthenticationFilter创建一个带有值的新AntPathRequestMatcher(" / login"," POST")。

我在调试控制台中找到的内容如下     请求'选项/登录'没有匹配' POST / login

当然不是!几个小时后试图解决问题我发现一切都有效,如果我声明一个空方法/登录,因为在预检期间Spring找到方法,然后向客户端发送200OK,然后允许客户端发送捕获的POST通过UsernamePasswordAuthenticationFilter。

@Controller
public class LoginController {

    @RequestMapping(value = { "/login" }, method = RequestMethod.POST)
    public void dummyLogin() {

    }
}

所以,我的问题是:我是否真的要声明一个空方法来"作弊"在CORS预检期间,还是我错过了一些东西?因为当你真的想要将作业委托给UsernamePasswordAuthenticationFilter时,声明一个虚方法并不是那么优雅......

2 个答案:

答案 0 :(得分:3)

问题是org.springframework.security.web.authentication.logout.LogoutFilterorg.springframework.security.web.authenticationUsernamePasswordAuthenticationFilter如果处理登录/注销,则不会继续使用过滤器链。由于通过WebSecurityConfigurerAdapter的配置稍后会在链中处理,因此永远不会应用CorsProcessor

我决定保留旧解决方案并使用org.springframework.web.filter.CorsFilter

答案 1 :(得分:0)

没有必要使用空方法来使其工作。您唯一要做的就是允许OPTIONS调用/login网址。

.antMatchers(HttpMethod.OPTIONS," / login")。permitAll()

前:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
      .csrf()
        .disable()
      .sessionManagement()
        .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
      .exceptionHandling()
        .authenticationEntryPoint(new RESTAuthenticationEntryPoint()).and()
      .formLogin()
        .successHandler(authenticationSuccessHandler())
        .failureHandler(new SimpleUrlAuthenticationFailureHandler())
        .loginProcessingUrl("/login") //Not necesary because is the default
        .permitAll().and()
      .authorizeRequests()
        .antMatchers("/api/getStatistics").permitAll()
        .antMatchers(HttpMethod.OPTIONS, "/login").permitAll()
        .anyRequest().denyAll().and()
      .addFilterBefore(new JwtTokenAuthenticationFilter(jWTTokenService()), UsernamePasswordAuthenticationFilter.class);
}