在Springboot中添加过滤器,解决前端访问跨域问题

最近在做一个前后端分离的项目,前端是基于Node.js,后端是用的springboot框架,本地调试的时候,前端发起ajax请求,会出现跨域的问题,为了解决这个问题,参考网上的已有方案,将自己的解决方法记录一下。

  • 在项目中添加一个过滤器,过滤前端发起的请求,通过设置响应的响应头信息,来达到跨域的目的。而前端不需要做任何处理就可以实现跨域访问。

1.实现Filter接口,重写doFilter方法

import org.apache.http.HttpStatus;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 允许跨域访问过滤器
 */
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 {

        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;

        //指定允许其他域名访问
        httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");

        //响应头设置
        httpServletResponse.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");

        //响应类型
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");

        if ("OPTIONS".equals(httpServletRequest.getMethod())) {
            httpServletResponse.setStatus(HttpStatus.SC_NO_CONTENT);
        }

        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }
}

  • 其中httpServletResponse.setStatus(HttpStatus.SC_NO_CONTENT);这段代码是设置响应的状态码,HttpStatus.SC_NO_CONTENT这个常量代表以下意思:
状态码 状态码英文名称 中文描述
204 No Content 无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档

2.添加相应的配置类

import com.maxus.portal.api.controller.common.CorsFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean CorsFilter() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        //注入过滤器
        registrationBean.setFilter(new CorsFilter());
        //过滤器名称
        registrationBean.setName("CorsFilter");
        //拦截规则
        registrationBean.addUrlPatterns("/*");
        //过滤器顺序
        registrationBean.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);

        return registrationBean;
    }
}

3.重启你的springboot,前端再次发起访问,就没有了跨域问题了。至此,问题就得到了解决,把这个记录一下,希望能帮到其他人。如果你想了解其中的原理,请继续往下看。


1. Access-Control-Allow-Origin
  • 服务器默认是不被允许跨域的。配置Access-Control-Allow-Origin *后,表示服务器可以接受所有的请求源(Origin),即接受所有跨域的请求。
2. Access-Control-Allow-Headers 是为了防止出现以下错误:
  • Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.
  • 这个错误表示当前请求Content-Type的值不被支持。其实是我们发起了application/json的类型请求导致的。这里涉及到一个概念:预检请求(preflight request),请看下面"预检请求"的介绍。
3. Access-Control-Allow-Methods 是为了防止出现以下错误:
  • Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.
  • 发送"预检请求"时,需要用到方法 OPTIONS ,所以服务器需要允许该方法。

预检请求(preflight request)

  • 上面的配置涉及到了一个W3C标准:CROS,全称是跨域资源共享 (Cross-origin resource sharing),它的提出就是为了解决跨域请求的。

跨域资源共享(CORS)标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。

  • 其实Content-Type字段的类型为application/json的请求就是上面所说的搭配某些 MIME 类型的POST请求,CORS规定,Content-Type不属于以下MIME类型的,都应该发起"预检"请求:

application/x-www-form-urlencoded
multipart/form-data
text/plain

  • 所以 application/json的请求会在正式通信之前,增加一次"预检"请求,这次"预检"请求会带上头部信息 Access-Control-Request-Headers: Content-Type

例如:

OPTIONS /api/test HTTP/1.1
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type
... 省略了一些
  • 服务器响应时,返回的头部信息如果不包含Access-Control-Request-Headers: Content-Type则表示不接受非默认的的Content-Type。即出现以下错误:
    Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.

文中如有错误,欢迎指正。
参考链接

你可能感兴趣的:(在Springboot中添加过滤器,解决前端访问跨域问题)