文章目录
- 浏览器的同源安全策略
- 跨域报错
- 跨域解决方案
- CORS
- Response支持跨域
- springboot支持跨域
- Java中设置多个Access-Control-Allow-Origin跨域访问
- 基于nginx配置请求的CORS
- JSONP方案
- 后端接口返回
- js原生实现jsonp
- jQuery实现jsonp
- vue.js实现jsonp
- JSONP的优缺点
- 其它方式支持跨域
浏览器的同源安全策略 同源策略,它是由Netscape提出的一个著名的安全策略。现在所有支持JavaScript的浏览器都会使用这个策略。所谓同源是指,域名,协议,端口相同。同源策略是浏览器的行为,是为了保护本地数据不被JavaScript代码获取回来的数据污染,因此拦截的是客户端发出的请求回来的数据接收,即请求发送了,服务器响应了,但是无法被浏览器接收。
同源:协议 + 域名 + 端口。所以,怎么才算跨域呢?
- 请求协议http,https的不同
- 域domain的不同
- 端口port的不同
跨域报错 【后端和服务器|跨域(Access-Control-Allow-Origin)解决方案详解】
文章图片
上面也说了这个限制是浏览器做的,看看接口,其实已经请求成功了,后端是执行了相关代码的。
跨域解决方案 CORS 这是W3C的标准,全称是"跨域资源共享"(Cross-origin resource sharing)。
//指定允许其他域名访问
‘Access-Control-Allow-Origin:http://172.20.0.206’//一般用法(,指定域,动态设置),3是因为不允许携带认证头和cookies
//是否允许后续请求携带认证信息(cookies),该值只能是true,否则不返回
‘Access-Control-Allow-Credentials:true’
上面第一行说到的Access-Control-Allow-Origin有多种设置方法:
- 设置是最简单粗暴的,但是服务器出于安全考虑,肯定不会这么干,而且,如果是的话,游览器将不会发送cookies,即使你的XHR设置了withCredentials
- 指定域,如上图中的http://172.20.0.206,一般的系统中间都有一个nginx,所以推荐这种
- 动态设置为请求域,多人协作时,多个前端对接一个后台,这样很方便。
从上面控制台的输出可以看到,错误原因是请求的资源(接口)的header中没有”Access-Control-Allow-Origin“,那我们可以给它加上。在哪加?既然说是请求的资源没有,那当然是在请求的资源上加,也就是服务端。
@SpringBootApplication
@Configuration
@RestController
public class ApplicationA {public static void main(String[] args) {
SpringApplication.run(ApplicationA.class, args);
}@RequestMapping("/test")
public Object test(HttpServletRequest request, HttpServletResponse response) {
// 跨域支持
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST,GET,PUT,DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
Map map = new HashMap<>();
map.put("success", true);
map.put("msg", "我来自服务端");
return map;
}
}
springboot支持跨域
测试用例是一个springboot项目,可以用更简单的方式。通过一个继承了WebMvcConfigurerAdapter的bean,重写addCorsMappings方法,在方法里配置。
@SpringBootApplication
@Configuration
@RestController
public class ApplicationA extends WebMvcConfigurerAdapter {public static void main(String[] args) {
SpringApplication.run(ApplicationA.class, args);
}@RequestMapping("/test")
public Object test(HttpServletRequest request, HttpServletResponse response) {
Map map = new HashMap<>();
map.put("success", true);
map.put("msg", "我来自服务端");
return map;
}// 跨域支持
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowCredentials(true)
.allowedMethods("GET", "POST", "DELETE", "PUT")
.maxAge(3600);
}
Java中设置多个Access-Control-Allow-Origin跨域访问
1、如果服务端是Java开发的,添加如下设置允许跨域即可,但是这样做是允许所有域名都可以访问,不够安全。
response.setHeader(“Access-Control-Allow-Origin”,"*");
2、为保证安全性,可以只添加部分域名允许访问,添加位置可以在下面三处任选一个。
(1)可以在过滤器的filter的dofilter()方法种设置。
(2)可以在servlet的get或者post方法里面设置。
(3)可以放在访问的jsp页面第一行。
3、在此用第一种方法,注意web.xml配置过滤器(filter)。
public void doFilter(ServletRequest req, ServletResponse res,FilterChain chain) throws IOException, ServletException {
// 将ServletResponse转换为HttpServletResponse
HttpServletResponse httpResponse = (HttpServletResponse) res;
// 如果不是80端口,需要将端口加上,如果是集群,则用Nginx的地址,同理不是80端口要加上端口
String []allowDomain= {"http://www.baidu.com","http://123.456.789.10","http://123.16.12.23:8080"};
Set allowedOrigins= new HashSet(Arrays.asList(allowDomain));
String originHeader=((HttpServletRequest) req).getHeader("Origin");
if (allowedOrigins.contains(originHeader)){
httpResponse.setHeader("Access-Control-Allow-Origin", originHeader);
httpResponse.setContentType("application/json;
charset=UTF-8");
httpResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
httpResponse.setHeader("Access-Control-Max-Age", "3600");
httpResponse.setHeader("Access-Control-Allow-Headers", "Content-Type,Access-Token");
// 如果要把Cookie发到服务器,需要指定Access-Control-Allow-Credentials字段为true
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Expose-Headers", "*");
}
chain.doFilter(req, res);
}
基于nginx配置请求的CORS
目前很多请求都不是直接暴露的,很多通过nginx做反向代理,因此可以使用nginx配置固定请求的Access-Control-Allow-Origin,实现跨域访问。方式是在被请求的接口,配置location代理,添加header实现。
#活动访问接口跨域配置
location /promotion/activityPro {
proxy_pass http://frontHost/promotion/activityPro;
add_header 'Access-Control-Allow-Origin' '*';
#add_header 'Access-Control-Allow-Methods' 'GET, POST';
#add_header 'Access-Control-Allow-Credentials' "true";
#add_header 'Access-Control-Max-Age' 86400;
#add_header 'Access-Control-Allow-Header' 'Content-Type,*';
}
JSONP方案 有前端经验的童鞋知道,有时我们会在自己的代码里直接引入其它域名的js、css等静态文件。为啥这些静态文件没被浏览器限制呢?通常为了减轻web服务器的压力,我们会把js、css,img等静态资源分离到另一台独立域名的服务器上,使其和前端分离开。基于这个原因,浏览器并没有限制这类静态资源的跨域访问。
我们可以动态地创建一个script,让浏览器以为我们要获取静态资源,从而网开一面。而服务器端也需要做一点改变,不能直接返回json,而是返回一个立即执行的函数,而前端请求的结果就作为函数的参数。
后端接口返回
@SpringBootApplication
@Configuration
@RestController
public class ApplicationA {public static void main(String[] args) {
SpringApplication.run(ApplicationA.class, args);
}@RequestMapping("/test")
public String test(HttpServletRequest request, HttpServletResponse response, String callback)
throws IOException {
Map map = new HashMap<>();
map.put("success", true);
map.put("msg", "我来自服务端");
// 返回值如下:
// callback({"msg":"我来自服务端","success":true});
return String.format("%s(%s);
", callback, JsonUtil.toJson(map));
}
js原生实现jsonp
function test() {
// 外部域名,参数是和后端接口约定的callback指定接口返回后的回调函数
url = "http://localhost:8882/test?callback=_ajax_callback";
// 创建一个script元素
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = https://www.it610.com/article/url;
document.head.appendChild(script);
}// 接口回调
function _ajax_callback(res) {
console.log("被回调了");
console.log(res);
}
接口返回:
文章图片
jQuery实现jsonp
// 没测,懒得测
$.ajax({
url: 'http://localhost:8882/test',
type: 'get',
dataType: 'jsonp',// 请求方式
jsonpCallback: "_ajax_callback",// 回调函数名
data: {}
});
vue.js实现jsonp
// 没测,懒得测
this.$http.jsonp(‘http://localhost:8882/test’, {
params: {},
jsonp: ‘_ajax_callback’
}).then((res) => {
console.log(res);
})
JSONP的优缺点
优点:它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制;它的兼容性更好,在更加古老的浏览器中都可以运行,不需要XMLHttpRequest或ActiveX的支持;并且在请求完毕后可以通过调用callback的方式回传结果。
缺点:它只支持GET请求而不支持POST等其它类型的HTTP请求;它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。
其它方式支持跨域
- nginx反向代理:前端访问相同域名,nginx再根据需要把请求转发到外部域名;
- 后端代理:在后端接口里先请求外部资源(比如用HttpClient),然后把结果返回给前端,这样就不是跨域了;
- 其它:借助iframe、postMessage等也可实现跨域。
- document.domain,window.name,web sockets
臭味相投的朋友们,我在这里:
猿in小站:http://www.yuanin.net
csdn博客:https://blog.csdn.net/jiabeis
简书:https://www.jianshu.com/u/4cb7d664ec4b
微信免费订阅号“猿in”
文章图片
参考:
https://cloud.tencent.com/developer/article/1493346
https://www.cnblogs.com/hofmann/p/10750983.html
https://blog.csdn.net/qq_32625839/article/details/81001125
推荐阅读
- web网页模板|如此优秀的JS轮播图,写完老师都沉默了
- 接口|axios接口报错-参数类型错误解决
- JavaScript|vue 基于axios封装request接口请求——request.js文件
- JavaScript|JavaScript — 初识数组、数组字面量和方法、forEach、数组的遍历
- JavaScript|JavaScript — call()和apply()、Date对象、Math、包装类、字符串的方法
- 前端|web前端dya07--ES6高级语法的转化&render&vue与webpack&export
- vue|Vue面试常用详细总结
- javascript|vue使用js-xlsx导出excel,可修改格子样式,例如背景颜色、字体大小、列宽等
- css|我用css精灵图拼接了自己的英文名字,不会还有人不知道精灵图技术吧()
- css|css三角的做法及其案例