Java 跨域问题处理


Java 跨域问题处理

问题

在页面上要使用 Ajax 请求去获取另外一个服务的数据,由于浏览器的 同源策略,所以直接请求会得到一个 Error

Failed to load https://www.baidu.com/: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

大概就是这样的一个错误,关键词是 Access-Control-Allow-Origin,一般出现这个都是跨域问题。

解决方案

解决跨域问题的方式有很多,但这里之说 Cors 的方案。

在后台添加一个 Filter 过滤器

/**
 * 使用自定义的 Filter 拦截器实现跨域请求、
 * 适用于所有的 Java Web 项目并且不局限于某个框架
 * 注:此处的 @Component 仅为让 Spring 知道这个 Bean, 不然拦截器不会加载
 *
 * @author rxliuli
 */
public class CustomCorsFilterConfig implements Filter {
    @Override
    public void init(FilterConfig filterConfig) {
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //允许所有来源
        String allowOrigin = "*";
        //允许以下请求方法
        String allowMethods = "GET,POST,PUT,DELETE,OPTIONS";
        //允许以下请求头
        String allowHeaders = "Content-Type,X-Token,Authorization";
        //允许有认证信息(cookie)
        String allowCredentials = "true";

        String origin = request.getHeader("Origin");
        //此处是为了兼容需要认证信息(cookie)的时候不能设置为 * 的问题
        response.setHeader("Access-Control-Allow-Origin", origin == null ? allowOrigin : origin);
        response.setHeader("Access-Control-Allow-Methods", allowMethods);
        response.setHeader("Access-Control-Allow-Credentials", allowCredentials);
        response.setHeader("Access-Control-Allow-Headers", allowHeaders);

        //处理 OPTIONS 的请求
        if ("OPTIONS".equals(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
            return;
        }
        filterChain.doFilter(request, response);
    }

    @Override
    public void destroy() {
    }
}

web.xml 文件中添加拦截器配置(注:如果可能就配置成第一个 Filter

<!--cors 跨域访问-->
<filter>
  <filter-name>customCorsFilterConfig</filter-name>
  <filter-class>CustomCorsFilterConfig</filter-class>
</filter>
<filter-mapping>
  <filter-name>customCorsFilterConfig</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

Spring Web 的解决方案

配置一个每次请求都过滤一次的 Filter 就好了

@Configuration
public class CorsConfig {
    @Bean
    public OncePerRequestFilter corsFilter() {
        return new OncePerRequestFilter() {
            @Override
            protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
                //允许所有来源
                String allowOrigin = "*";
                //允许以下请求方法
                String allowMethods = "GET,POST,PUT,DELETE,OPTIONS";
                //允许以下请求头
                String allowHeaders = "Content-Type,X-Token,Authorization";
                //允许有认证信息(cookie)
                String allowCredentials = "true";

                String origin = request.getHeader("Origin");
                //此处是为了兼容需要认证信息(cookie)的时候不能设置为 * 的问题
                response.setHeader("Access-Control-Allow-Origin", origin == null ? allowOrigin : origin);
                response.setHeader("Access-Control-Allow-Methods", allowMethods);
                response.setHeader("Access-Control-Allow-Credentials", allowCredentials);
                response.setHeader("Access-Control-Allow-Headers", allowHeaders);

                //处理 OPTIONS 的请求
                if ("OPTIONS".equals(request.getMethod())) {
                    response.setStatus(HttpServletResponse.SC_OK);
                    return;
                }
                filterChain.doFilter(request, response);
            }
        };
    }
}

使用示例

下面是一些简单的使用 fetch 进行跨域请求的示例:

  • 简单 fetch 请求,和正常使用 fetch 并无区别

    fetch(url)
      .then(res => res.json())
      .then(json => console.log(json))
  • 表单请求

    var fd = new FormData()
    fd.append('username', 'rx')
    fd.append('password', 'rx')
    
    fetch(url, {
      method: 'POST',
      body: fd,
    })
      .then(res => res.json())
      .then(json => console.log(json))
  • 需要认证的请求

    fetch(url, {
      /**
       * 关键就在这里,代表用户是否应该在跨域的情况下发送 cookies 和 HTTP Basic authentication 等验信息以及服务端能否返回 Set-Cookie(服务端 Session 需要使用这个向 cookie 中设置 sessionId)。
       * 包含三个可选值:omit(从不发送), same-origin(同源才发送), include(总会发送)
       * 参考链接:<https://developer.mozilla.org/zh-CN/docs/Web/API/Request/credentials>
       */
      credentials: 'include',
    })
      .then(res => res.json())
      .then(json => console.log(json))

    注:如果想要服务端返回 Set-CookieSessionId 也需要通过这个响应属性去设置) 就必须设置这个请求参数!

那么,之后在前端跨域请求的时候就可以愉快地玩耍啦(v^_^)v


文章作者: rxliuli
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 rxliuli !
 上一篇
Git 频繁要输入密码的问题 Git 频繁要输入密码的问题
Git 频繁要输入密码的问题场景突然就遇到了,每次使用 Git 进行 pull/push 操作时都要输入密码,真是超级麻烦! 原因在 Git 服务器上面设置了 SSH 密钥但仍然使用 HTTP/HTTPS 连接就会出现这个问题 解决方案1.
2018-08-20 rxliuli
下一篇 
MySQL 无法插入中文数据 MySQL 无法插入中文数据
MySQL 无法插入中文数据刚刚在 Linux Centos 装完 MySQL 后,想试一下是否能正常使用,但在插入中文数据时发生了下面的这个错误。 [HY000][1366] Incorrect string value: '\xE7\x
2018-08-16 rxliuli
  目录