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

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

一、为什么跨域?

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

1、域名不同:http://a.comhttp://b.com
2、协议不同:http、https
3、端口不同:http://a.com:8000http://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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容