前后端分离需要处理的跨域解决方案

环境

前端

vue + axios

后端

springboot

问题与方案

前后端分离需要处理的跨域解决方案

这里可以看文献的《跨域资源共享》;请求分为简单请求复杂请求简单请求直接进行正式请求,而复杂请求则会在正式请求之前发送请求方式为OPTIONS的预检请求。

跨域解决方案(针对复杂请求)

第一种

前后端分离需要处理的跨域解决方案

原因: 缺少跨域的Header标头

解决:

    // 实现 implements WebMvcConfigurer 接口
	@Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowCredentials(true)
                .allowedOrigins("http://localhost:8080")
                .allowedMethods("GET","POST","DELETE","PUT","OPTIONS")
                .allowedHeaders("*");
                // .exposedHeaders("Allow"); //这一句可以不需要,暴露指定的header
    }

第二种

前后端分离需要处理的跨域解决方案

原因: (OPTIONS)预检请求返回的不是OK的状态码(200)

解决: 解决后台的报错问题即可

第三种

前后端分离需要处理的跨域解决方案

由于我是集成了SpringSecurity,那么只需添加以下代码片段即可

    /**
     * 注入CORS配置源
     * @see WebMvcConfiguration#addCorsMappings(org.springframework.web.servlet.config.annotation.CorsRegistry)
     */
    @Resource
    private CorsConfigurationSource corsConfigurationSource;

protected void configure(HttpSecurity http) throws Exception
   http.cors().configurationSource(corsConfigurationSource)
}

如果没有集成SpringSecurity,那么添加以下代码(未测试)

    /**
     * 注入CORS配置源
     * @see WebMvcConfiguration#addCorsMappings(org.springframework.web.servlet.config.annotation.CorsRegistry)
     */
    @Resource
    private CorsConfigurationSource corsConfigurationSource;


@Bean
    public FilterRegistrationBean<CorsFilter> corsFilterFilterRegistrationBean(){
        FilterRegistrationBean<CorsFilter> corsFilterFilterRegistrationBean = new FilterRegistrationBean<>();
        corsFilterFilterRegistrationBean.setUrlPatterns(Collections.singleton("/*"));
        corsFilterFilterRegistrationBean.setOrder(0); // 这里调整的执行次序,按自己所需的值修改
        corsFilterFilterRegistrationBean.setFilter(new CorsFilter(configSource));
        corsFilterFilterRegistrationBean.setEnabled(true);
        return corsFilterFilterRegistrationBean;
    }

第四种:忽略OPTIONS预检请求

//注册一个 Filter 过滤器,对OPTIONS请求直接返回即可
if (request.getMethod().equals(RequestMethod.OPTIONS.name())) {
            response.addHeader("Allow", ""); //这个Allow有大用
            return false;
}
/**
 * Security的自定义认证过滤器实现,此为参考即可;只列出了主要代码以供参考
*/
public class JwtAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    /**
     * 此方法返回是否需要认证
     * @param request
     * @param response
     * @return
     */
    @Override
    protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
        if (request.getMethod().equals(RequestMethod.OPTIONS.name())) {
            response.addHeader("Allow", "");
            return false;
        }
        return super.requiresAuthentication(request, response);
    }
    ...
}
/**
  只列出了主要代码以供参考;可以在doFilter方法里过滤OPTIONS预检请求
*/
public class JwtOnceAuthenctionFilter extends OncePerRequestFilter {

    /**
    	此方法的返回值只决定了执行不执行当前过滤器而已,这里只是位置,并未加代码,可参考上方忽略
    	预检请求(OPTIONS)的片段
    */
    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
        return super.shouldNotFilter(request);
    }
    ...
}

response.addHeader("Allow", ""); 这里说下这句代码的用意,主要就是终结这个请求的执行而已

/**
留下代码定位,Debug看吧

org.springframework.web.servlet.FrameworkServlet#doOptions

*/	

@Override
	protected void doOptions(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

		if (this.dispatchOptionsRequest || CorsUtils.isPreFlightRequest(request)) 		{
			processRequest(request, response);
            //这里会判断响应Header是否包含`Allow`,值目前并没有找到需要的地方
			if (response.containsHeader("Allow")) {
				// 来自处理程序的正确OPTIONS响应-我们完成了。
				return; //这个请求就这样执行完了
			}
		}
        ......................
 }

相关文献