跨域问题
问题描述
浏览器为了防止跨站请求伪造的攻击,设置了同源策略机制。前端本地开发的 URL 大多是“http+localhost+端口”,这会产生跨域、cookie种植和调用外部服务需要真实域名的问题。
同源策略是指两个源相互之间只有同源,换句话说就是协议、域名和端口都一致,才能相互通信。这样是为了保证请求的安全,减少恶意攻击。
cookie 种植问题--如果前后端的鉴权涉及 cookie,那 localhost 之类的域名会影响 cookie 的种植。因为浏览器能否正确种植下 cookie,受到 domain 的影响。
调用外部服务需要真实域名。当我们项目调用外部服务时,经常需要完整的真实域名,比如微信登录。此时本地开发没有完整的真实域名,就无法在本地联调。
为了验证同源,浏览器会在所有请求中附带一个特殊的请求一起发送给域信息接收服务器。服务器返回的response会附带一个包含键为Access-Control-Allow-Origin
的header,用来标示什么样的源可以访问服务器的资源。通常分为两种模式,当值为*时,服务端允许任何源访问自己的资源。另一种模式则是,指定的源的权限访问。
前端 - 【译】3种解决CORS错误的方式与Access-Control-Allow-Origin的作用原理 - 个人文章 - SegmentFault 思否
处理方法
-
使用扩展插件,关闭浏览器的同源策略检查。插件会在每次请求的response中加入一个
Access-Control-Allow-Origin:*
的head。但许多时候并不是很管用。 -
通过代理,作为客户端和服务端之间的中间人,这个代理服务会帮助前端web app发送请求,并且接收服务端的返回数据再传送给前端web app。代理服务在原始的返回中,附加上一个
Access-Control-Allow-Origin: * 的header
。比如通过Vue或React等框架来配置API代理。本文尝试通过Vue建立代理,将本地的数据通过代理端口发出,规避服务器的同源检查策略。Vue的配置文件(vue.config.js)为:module.exports = { devServer: { port: 12234, open: true, proxy: { '/api1': { // 拦截以 本地页面地址/api1 发出的请求 target: 'http://......', // 实际的后端地址 changeOrigin: true, // 这里true表示实现跨域 secure: false, // 如果是https接口,需要配置这个参数 pathRewrite: { '^/api1': '/' // 将/api1重写为 / ; 服务器最终收到的是统一从本地 12234 端口发出的数据 } }, '/api2': { // 拦截以 本地页面地址/api2 发出的请求 target: 'http://.....', // 另外一个后端地址 changeOrigin: true, // 这里true表示实现跨域 secure: false, // 如果是https接口,需要配置这个参数 pathRewrite: { '^/api2': '/' } } } } };
需要注意的是,Vue的配置文件需要放在项目文件夹内,与main.js同级。
-
建立自己的代理,同源策略只会作用于浏览器-服务之间,而不限制服务-服务的通信。
uniapp+uView实现多个域名或端口的请求
问题描述
在项目代码中,原先是参考Http请求 | uView 2.0 - 全面兼容 nvue 的 uni-app 生态框架 - uni-app UI 框架 (uviewui.com)使用uView的http接口直接对访问的参数和后端地址等的全局设置,并配置为插件在main.js
中引入。但当需要访问多个域名或是域名的不同端口时便无法使用这种全局设置。
解决方法
将uview的http方法在封装一层以设置不同的访问域名或接口。
-
在配置文件中,写入需要访问的域名或端口。
baseUrl_a: 'http://...', baseUrl_b: 'http://...',
-
在设置uView http的全局参数不设置其要访问的URL。
uni.$u.http.setConfig((config) => { // 域名设置 // config.baseURL = projectConfig.baseUrl; ... ...
-
新建js文档,根据不同的域名设置不同的访问方法。
// requestConfig.js const http = uni.$u.http; // 第一类接口的两种方法 export function http_a_get(path,params = ''){ return http.get(projectConfig.baseUrl_a + path, {params}); } // 第二类接口的两种方法 export function http_b_get(path,params = ''){ return http.get(projectConfig.baseUrl_b + path, {params}); }
-
在
main.js
中引入添加到原型。import {http_a_get,http_b_get} from '..../reuquestConfig.js'; Vue.prototype.$http_a_get = http_a_get; Vue.prototype.$http_b_get = http_b_get;
-
在实际使用中,直接调用封装好的函数。后需要添加新的端口及对应的方法需要在配置文件中添加端口并封装所需要的方法最后在main.js中添加到原型。对于原有端口的修改,只需要在URL的配置文件中更改即可。
export function getId(params) { return http_a_get('/端口后的路径', params); }