前端怎么设置允许跨域、后端怎么配置跨域,前后端分离如何解决跨域问题

虽然做了很多项目,但是每每新项目时候跨域情况经常一而再,再而三的出现,并且可能并不是同一个问题,但都是跨域引起的,今天来归纳下跨域,一文彻底解决。
项目背景技术栈:react、vue、java、fetch、axios
主要为以下几点:
1、为什么跨域
2、好用的解决方式
3、options请求优化
认真读完这篇文章你将会收获:快速解决跨域问题能力、摆脱纠缠跨域是前端还是后端解决的问题。

一、为什么跨域?

首先要明确一点的是,跨域实际出现一般都是前后端分离场景,并且,跨域请求实际上是浏览器进行拦截的(浏览器的安全策略拦截)。你通过linux的curl去请求不同服务器的接口是怎么也不会遇到跨域。
跨域情况:

1、域名不同:http://a.com、http://b.com
2、协议不同:http、https
3、端口不同:http://a.com:8000、http://a.com:8001

注意:即使是同一个ip但是域名不同还是会跨域。例如:http://a.com的ip是192.168.1.1但是这两个还是会跨域。

二、跨域解决方式

有好几种:jsonp、nginx、cors。但是jsonp就不说了,老、烂、不好用浪费时间扯这个。

1、说下实际开发中用到的:cors

Cross-origin resource sharing 跨域资源共享。
后端设置允许跨域:需要设置请求返回的响应头,就是需要再过滤器里设置响应头,代码如下(跟我们后端要了代码哈哈):请注意,每一行都有单独的作用!

@Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        req.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);
        HttpServletResponse response = (HttpServletResponse) res;
        response.setHeader("Access-Control-Allow-Origin", ((HttpServletRequest) req).getHeader("Origin"));
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE,PUT");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "Content-Disposition,Origin, X-Requested-With, Content-Type, Accept,Authorization,id_token");
        response.setHeader("Access-Control-Allow-Credentials","true");
        response.setHeader("Content-Security-Policy", "default-src 'self' 'unsafe-inline'; script-src 'self'; frame-ancestors 'self'; object-src 'none'");
        response.setHeader("X-Content-Type-Options", "nosniff");
        response.setHeader("X-XSS-Protection", "1; mode=block");
        chain.doFilter(req, res);
    }

此时开发环境下如果前端是使用axios请求,并且没有自定义请求头则不需要设置任何代理就能正常请求了。如果你使用fetch还需要 mode: 'cors'来允许跨域。
如果你想跨域携带cookie,则需要自己在前端如下设置,看下下面两个请求库的如何设置的(默认下任何请求库都是不携带cookie,需要自己开启配置)。
前端设置跨域并允许携带cookie:
前端请求库一般有两种:流行框架下react或者vue使用axios、fetch这两者都可以,设置允许跨域的方式有点不一样。
原生fetch:

fetch('localhost:3000',{
      /*允许携带cookies,默认情况没写这个是不会携带的*/
      credentials: 'include',
      /*允许跨域**/
      mode: 'cors'
})

axios:

import axios from 'axios'
// 对所有 axios 请求允许携带cookie
axios.defaults.withCredentials = true;

// 对单独的 axios 请求允许携带cookie
 axios.get('localhost:3000', {
  withCredentials: true    
})

2、不用cors解决跨域

  • 本地开发环境:
    如果不需要后端设置cors允许跨域,那么则需要前端自己配置本地代理了。在你的项目上设置代理,所有接口处前面加上/api用来识别做代理。
  proxy: {
    '/api': {
      target: 'http://172.17.168.60:8000/',
      changeOrigin: true,
      pathRewrite: { '^/api': '' },
    },
  },
  • 线上环境nginx:
location ^~ /api/ {
  proxy_pass   http://127.0.0.1:81/;
}

三、cors:简单请求、非简单请求,优化option请求

上面虽然有cors解决跨域,但是呢,cors会有一个情况,它会将请求分为简单请求和非简单请求。

满足以下几点是简单请求:
1、只限于get、post、head方法
2、请求头不超出以下字段(且没有其他自定义字段):
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
如果不满足以上其中之一,那就是非简单请求!

非简单请求会发送一个预检请求options,用来嗅探服务端是否允许非简单请求跨域访问资源。

  • 注意:这个options请求是浏览器自己发出的!

有option时请求同个接口时会出现请求两次的情况,并option请求没有返回任何数据,还会导致请求变慢了,那么如何优化?
后端设置允许缓存时间来优化:

//加上这一行,用意为当下次请求碰到这个接口时候就会在这个时长内忽略option请求不再发起option请求。
response.setHeader("Access-Control-Max-Age", "3600");   //3600为时长

但这并不能完美解决,如果谷歌浏览器勾选了Disable cache按钮,这个效果将会失效。


添加自定义请求头跨域:
还有个特殊情况,就是当你添加了自定义请求头,例如再请求添加了'id_token'字段用来判断用户登录情况,或者其他字段(叫啥都行,只要是自定义的),那么你需要让后端加上去允许这个携带这个字段跨域。

 response.setHeader(
"Access-Control-Allow-Headers", 
"Content-Disposition,
Origin, 
X-Requested-With,
 Content-Type,
Accept,
Authorization,
id_token"   //自定义请求头要加上去,否则还是会跨域,在任何情况下
);
      

注意:上面这个id_token在使用ningx转发代理时候会导致请求投内容丢失情况。
原因:nginx不会识别"_"这个符号,默认情况下它会忽略,所以后端没接收到,也就是说请求时候没将这个id_token转发过去导致的。

解决办法有两种:

1、修改nginx配置
在nginx 的 http部分添加如下:
underscores_in_headers on; (默认 underscores_in_headers 为off)

2、修改这个字段,取消下划线
列如 把原来的id_token 换为 idToken

你可能感兴趣的:(前端怎么设置允许跨域、后端怎么配置跨域,前后端分离如何解决跨域问题)