SpringBoot+Spring Security無法實現跨域的解決方案

SpringBoot+Spring Security無法實現跨域

未使用Security時跨域:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.*;
@Configuration
@AutoConfigureBefore(SecurityConfig.class)
public class MyMvcConfigurer implements WebMvcConfigurer {
    public void addCorsMappings(CorsRegistry registry){
        LOGGER.info("跨域已設置");
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("*")
                .allowedHeaders("*")
                .allowCredentials(true)
                .maxAge(3600);
    }
}

整合Security時發現隻用上述方法前後端分離時仍存在跨域問題,

解決方法如下:

@Configuration
@AutoConfigureBefore(Swagger2Configuration.class)
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Order(-1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                .loginProcessingUrl("/user/login")
                .loginPage("/singIn.html")
                .successHandler(moyuAuthenticationSuccessHandler)
                .failureHandler(moyuAuthenticationFailureHandler)
                .and()
                .apply(moyuSocialSecurityConfig)
                .and()
                .rememberMe()
                .tokenRepository(persistentTokenRepository())
                .tokenValiditySeconds(3600*24*7)
                .userDetailsService(userDetailsService)
                .and()
                .authorizeRequests()
                .antMatchers("/user/login","/login","/singIn.html","**","/**").permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .cors()
                .and()
                .csrf().disable();
    }
}

重點加入代碼:

   .and()
   .cors()//新加入
   .and()
   .csrf().disable();

引用Spring Security 項目的跨域處理

最近項目采用瞭前後端分離的框架,前端和後臺接口沒有部署到一個站點,出現瞭跨域問題,什麼是跨域,這裡就不再贅述,直接說解決辦法。

Spring 解決跨域的方式有很多,個人采用瞭Crosfilter的方式

具體代碼如下:

@Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new      UrlBasedCorsConfigurationSource();
        final CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowCredentials(true);
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }

配置完成後,測試調用,報錯401,依然不行。網上查資料得知,跨域請求會進行兩次。具體流程見下圖:

這裡寫圖片描述

每次跨域請求,真正請求到達後端之前,瀏覽器都會先發起一個preflight request,請求方式為OPTIONS 詢問服務端是否接受該跨域請求,具體參數如下圖:

這裡寫圖片描述

但是該請求不能攜帶cookie和自己定義的header。

由於項目中引入瞭Spring security ,而我使用的token傳遞方式是在header中使用authorization 字段,這樣依賴Spring Security攔截到 preflight request 發現它沒有攜帶token,就會報錯401,沒有授權。

解決這個問題很簡單,可以使用以下配置

讓Spring security 不校驗preflight request 。

 @Override
    public void configure(HttpSecurity http) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry 
        = http.authorizeRequests();
        registry.requestMatchers(CorsUtils::isPreFlightRequest).permitAll();//讓Spring security放行所有preflight request 
    }

再試就搞定瞭,但是後端直接配置支持跨域會導致兩次請求。還使用另一種方式,使用Nginx 轉發一下請求也可以。

以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。

推薦閱讀: