1. 为什么会有跨域
浏览器出于对于浏览网页安全性问题的考虑,引入的同源策略。
什么是同源策略,听着很官方,简单一幅图说明:
<mark>同源指的是URL地址三要素(协议,主机,端口号)必须不变</mark>
协议 | 主机 | 端口 |
---|---|---|
http | www.baidu.com | 80 |
例如我客户端访问80端口的百度,只要协议,和端口不变访问
http://www.baidu.com:80/aaa/bbb 满足同源策略,访问京东同样如此。
2. 什么是跨域
同样如上图,如果出现了在百度页面可以访问京东的页面,这中间就出现了跨域问题。
这种问题 会引起什么问题呢?
其实在正常的界面访问直接一般不会出现问题,但是如果是涉及到访问一些需要登录后访问的信息时,这样就会存在安全隐患,例如,同样看图
很明显,可以正常访问到界面2的内容,但是如果界面2的内容属于敏感信息呢,我们不需要被访问到!!!
3. DEMO准备
<mark>项目1:端口号8080</mark>
一个页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
<script src="/js/axios.min.js"></script>
<script src="/js/vue.min.js"></script>
</head>
<body>
<button id="btn" @click.prevent = "click">点击按钮,测试跨域</button>
</body>
<script> new Vue({
el:"#btn", methods:{
click(){
axios.get("http://localhost:80/jd/index"); } } }); </script>
</html>
简单按钮请求:但是请求的是 另外一个服务器的资源
URL地址是:http://localhost:80/jd/index
<mark>项目2:端口号 80</mark>
@RestController
@RequestMapping("/jd")
public class IndexController {
@GetMapping("/index")
public String index(){
return "index";
}
}
两个都是基于SpringBoot构建,如果通过项目1的请求,去访问项目2的controller,这个时候就会发生跨域问题。如下:
4. 解决手段
4.1 设置Access-Control-Allow-Origin
修改项目2的controller
@RestController
@RequestMapping("/jd")
public class IndexController {
@GetMapping("/index")
public String index(HttpServletResponse response){
//运行项目1访问:只需要协议 + 主机 + 端口
response.addHeader("Access-Control-Allow-Origin","http://localhost:8080");
//允许所有访问
// response.addHeader("Access-Control-Allow-Origin","*");
//扩展:
// 域间请求的最大等待最时间(单位s)
//1. 表示每10s发送一次域间请求,不足10s就不需要发送域间请求,提高访问效率
// response.addHeader("Access-Control-Max-age","10");
//
// //2.只允许GET请求
// response.addHeader("Access-Control-Allow-Method","GET");
//
// //3.请求头携带xxx参数
// response.addHeader("Access-Control-Allow-Headers","xxx");
return "index";
}
}
同样可以使用SpringBoot自带的注解@CrossOrigin
进行配置
@GetMapping("/index")
@CrossOrigin
public String index(HttpServletResponse response){
return "index";
}
@Target({
ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CrossOrigin {
/** @deprecated */
@Deprecated
String[] DEFAULT_ORIGINS = new String[]{
"*"};
/** @deprecated */
@Deprecated
String[] DEFAULT_ALLOWED_HEADERS = new String[]{
"*"};
/** @deprecated */
@Deprecated
boolean DEFAULT_ALLOW_CREDENTIALS = false;
/** @deprecated */
@Deprecated
long DEFAULT_MAX_AGE = 1800L;
@AliasFor("origins")
String[] value() default {
};
@AliasFor("value")
String[] origins() default {
};
String[] originPatterns() default {
};
String[] allowedHeaders() default {
};
String[] exposedHeaders() default {
};
RequestMethod[] methods() default {
};
String allowCredentials() default "";
long maxAge() default -1L;
}
具体配置细节可网上查询 CrossOrigin介绍
4.2 借助RestTemplate实现远程调用
项目1:安排RestTemplate
@Configuration
public class RPCConfig {
@Autowired
private RestTemplateBuilder restTemplateBuilder;
@Bean
public RestTemplate restTemplate(){
return restTemplateBuilder.build();
}
}
我们转换思路:原先从项目1的index.html访问项目2,现在转换成从项目1的html访问到项目1的controller,再从项目1的controller转而访问项目2的controller,将返回数据再次返给项目1的html,看文字不如看张图
同样可以实现跨域访问
4.3 借助Nginx实现反向代理
将我们的项目1的index.html放入nginx静态服务器中,并和nginx处于同一个监听端口
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
<script src="/js/axios.min.js"></script>
<script src="/js/vue.min.js"></script>
</head>
<body>
<button id="btn" @click.prevent = "click">点击按钮,测试跨域</button>
</body>
<script> new Vue({
el:"#btn", methods:{
click(){
axios.get("http://localhost:81/jd/index").then(response=>{
console.log(response.data); }); } } }); </script>
</html>
nginx配置文件
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 81;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location /jd {
#反向代理项目2的地址,只要包含/jd开头的都转发到http://localhost:80
proxy_pass http://localhost:80;
}
location / {
root html;
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
实现跨域
这里需要注意的是,nginx必须监听的是,要代理的端口,这样才可以实现跨域。
通过正则表达式修改Nginx映射规则
location /jd {
rewrite ^/jd/(.*)$ /$1 break;
proxy_pass http://localhost:80;
}
可以做到代理到80的任意URL,如我们将项目2的controller地址改为
http://localhost/jd/index
也可以正常访问
4.4 jsonp技术
jsonp属于比较老的技术了,运用的是一些天然支持跨域的标签特性,进行函数回调的过程。
举个例子:
在项目1 的index.htm,通过天然支持跨域的标签<script>
来实现跨域,<script>
请求项目2的
@RestController
@RequestMapping("/jd")
public class IndexController {
/** * 方法2:借助restTemplate 远程调用 * @param response * @return */
@GetMapping("/index")
public String index(HttpServletResponse response){
return "cross('es-jd-index')";
}
}
index.html
<script> function cross(data) {
console.log(data); } </script>
<script src = 'http://localhost:80/jd/index'></script>
来进行函数的回调执行,这种方法现在也不常用,比较常用的是Nginx来实现跨域!!