1.什么是跨域
跨域是指a页面想获取b页面资源,
如果a、b页面的协议、域名、端口、子域名不同,
或是a页面为ip地址,b页面为域名地址,
所进行的访问行动都是跨域的,
而浏览器为了安全问题一般都限制了跨域访问,也就是不允许跨域请求资源。
常见场景:
URL 说明 是否允许通信
http://www.domain.com/a.js
http://www.domain.com/b.js 同一域名,不同文件或路径 允许
http://www.domain.com/lab/c.js
http://www.domain.com:8000/a.js
http://www.domain.com/b.js 同一域名,不同端口 不允许
http://www.domain.com/a.js
https://www.domain.com/b.js 同一域名,不同协议 不允许
http://www.domain.com/a.js
http://192.168.4.12/b.js 域名和域名对应相同ip 不允许
http://www.domain.com/a.js
http://x.domain.com/b.js 主域相同,子域不同 不允许
http://domain.com/c.js
http://www.domain1.com/a.js
http://www.domain2.com/b.js 不同域名 不允许
2.跨域解决方案
1、 通过jsonp跨域
2、 document.domain + iframe跨域
3、 location.hash + iframe
4、 window.name + iframe跨域
5、 postMessage跨域
6、 跨域资源共享(CORS)
7、 nginx代理跨域
8、 nodejs中间件代理跨域
9、 WebSocket协议跨域
2.1通过jsonp跨域
jsonp缺点:只能实现get一种请求。
原生实现
<script>
var script = document.createElement('script');
script.type = 'text/javascript';
// 传参并指定回调执行函数为onBack
script.src = 'http://www.domain2.com:8080/login?user=admin&callback=onBack';
document.head.appendChild(script);
// 回调执行函数
function onBack(res) {
alert(JSON.stringify(res));
}
</script>
服务端返回如下(返回时即执行全局函数):
onBack({"status": true, "user": "admin"})
jquery ajax:
$.ajax({
url: 'http://www.domain2.com:8080/login',
type: 'get',
dataType: 'jsonp', // 请求方式为jsonp
jsonpCallback: "onBack", // 自定义回调函数名
data: {}
});
某sso.js如下:
$(function () {
function getCookie(name) {
var arr, reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)");
if (arr = document.cookie.match(reg))
return decodeURI(arr[2]);
else
return null;
}
var token = getCookie("USER_TOKEN");
//alert(token)
$.ajax({
type: "get",
dataType: "jsonp",
url: "http://localhost:8084/user/token/" + token,
data: {},
success: function (data) {
console.info(data)
$("#userInfo").html("用户:<em>" + data.data.userName + "</em>");
}
});
})
function loginOut() {
$.ajax({
type: "get",
dataType: "jsonp",
url: "http://localhost:8082/user/loginOut",
data: {},
success: function (data) {
console.info(data)
if(data.status==200){
window.location.href="http://localhost:8087/portal/toMain"
}
}
});
}
vue.js:
首先,安装vue-resource
npm install vue-resource -S
其次,在<main.js>中引入
import VueResource from 'vue-redource'
Vue.use(VueResource)
再次,在代码调用即可
this.$http.jsonp('http://www.domain2.com:8080/login', {
params: {"username": "tom", "password": "123456"},
jsonp: 'onBack'
}).then((res) => {
console.log(res);
})
#该方法无需后台代码过多设置即可实现跨域
2.2跨域资源共享(CORS)Cross-Origin Resource Sharing
a.普通跨域请求:
只服务端设置Access-Control-Allow-Origin即可,前端无须设置。
b.带cookie请求:
前后端都需要设置字段,另外需注意:所带cookie为跨域请求接口所在域的cookie,而非当前页。
目前,所有浏览器都支持该功能(IE8+:IE8/9需要使用XDomainRequest对象来支持CORS)),CORS也已经成为主流的跨域解决方案。
2.2.1前端跨域方案:
jQuery ajax
$.ajax({
...
xhrFields: {
withCredentials: true // 前端设置是否带cookie
},
crossDomain: true, // 会让请求头中包含跨域的额外信息,但不会含cookie
...
});
vue框架在vue-resource封装的ajax组件中加入以下代码:
Vue.http.options.credentials = true
2.2.2服务端设置:
springMVC实现跨域
http://spring.io/blog/2015/06/08/cors-support-in-spring-framework
a.为controller的某个路径跨域
@RestController
@RequestMapping("/account")
public class AccountController {
@CrossOrigin
@GetMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
@DeleteMapping("/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}
b.为整个controller跨域
origins: 允许可访问的域列表
maxAge:飞行前响应的缓存持续时间的最大年龄(以秒为单位)
@CrossOrigin(maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {
@CrossOrigin(origins = "http://domain2.com")
@GetMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
@DeleteMapping("/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}
c.spring中跨域的全局配置:
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
}
d.spring-boot中跨域的全局配置
@Configuration
public class MyConfiguration {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://domain2.com")
.allowedMethods("PUT", "DELETE")
.allowedHeaders("header1", "header2", "header3")
.exposedHeaders("header1", "header2")
.allowCredentials(false).maxAge(3600);
}
};
}
}
e.spring-boot中基于filter实现跨域
@Configuration
public class MyConfiguration {
@Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("http://domain1.com");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(0);
return bean;
}
}
2.3通过nginx的反向代理(proxy_pass)
server {
listen 9090;
server_name localhost;
location / {
root html; index index.html index.htm;
}
location / apis {
rewrite ^.+apis / ? (. *)$ / $1 break;
include uwsgi_params;
proxy_pass http://localhost:8089;
}
}