什么是跨域
指从一个域名网页去请求另一域名页面的资源,比如 baidu.com 请求 google.com 中的资源,但是这种情况是不允许的,因为由浏览器的同源策略
,使浏览器对js增加安全限制,即就是阻止不同域下的js进行交互。判断域是否相同是通过判断协议、域名、端口
中有任何一个不同,则为不同的域。
什么是同源策略
1、同源策略(Same origin policy)是一种约定,是由Netscape提出的一个著名的安全策略,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
2、举例:
当浏览器的百度tab页执行一个脚本的时候会检查这个脚本是属于哪个页面的,
即检查是否同源,只有和百度同源的脚本才会被执行。
如果非同源,那么在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。
3、同源策略是浏览器的行为,是为了保护本地数据不被JavaScript代码获取回来的数据污染,因此拦截的是客户端发出的请求回来的数据接收,即请求发送了,服务器响应了,但是无法被浏览器接收。
4、同源指:协议、域名、端口都相同。
5、同源策略是基于安全方面的考虑提出的,这个策略本身没有问题,但是在实际开发中,由于其他原因经常有跨域的需求,所以需要跨域的解决方案。
传统的跨域解决凡是 jsonP,jsonP 虽然能解决跨域问题但缺点就是只支持 get 请求。所以有了 cors 来解决 不同请求下跨域资源共享问题。
6、CORS 是一种允许当前域(domain)的资源(比如html/js/web service)被其他域(domain)的脚本请求访问的机制,它是w3c标准,是一份浏览器技术的规范,提供了web服务从不同网域js交互方法,以避开浏览器的同源策略。
springboot 实现 cors
需要创建两个springboot 项目,一个为 provider提供服务的项目,一个为consumer消费服务的项目。
先看没有 cors 的情况
无 cors 情况
提供服务(provider)
1、创建提供服务的项目,添加依赖(provider)
因为需要模拟网页,所以需要 web 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2、配置端口
server.port=8080
3、提供接口
@RestController
public class HelloController {
@GetMapping("/h")
public String hello(){
return "hello cros";
}
@PutMapping ("/h1")
public String he(){
return "hello put cros";
}
}
消费服务(consumer)
1、添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2、配置端口
server.port=8081
3、页面准备
在resources/static下创建 index.html 文件,用于发送简单的 ajax 请求
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
</head>
<body>
<input type="button" onclick="getData()" value="get"><br>
<input type="button" onclick="putData()" value="put"><br>
<script>
function getData() {
$.get("http://localhost:8080/h",function (msg){
alert(msg);
})
}
function putData() {
$.ajax({
url:"http://localhost:8080/h1",
type:"put",
success:function (msg) {
alert(msg);
}
})
}
</script>
</body>
</html>
启动两个项目,通过消费服务的页面定义的按钮,点击后会发现浏览器控制台输出如下:
可以看出由于同源策略的限制,请求无法获取资源。
而当使用了 cors 可以在前端代码不做任何修改下,实现跨越。
有 cors 情况
只需在 服务提供者(provider)中进行配置即可
方式一:@CrossOrigin()
在提供的接口中添加 @CrossOrigin() 注解,该注解添加类或方法上都可。
@RestController
@CrossOrigin(value = "http://localhost:8081",maxAge = 8000)
public class HelloController {
@GetMapping("/h")
public String hello(){
return "hello cros";
}
@PutMapping ("/h1")
public String he(){
return "hello put cros";
}
}
通过注解的 value 属性,来设置请求的地址。maxAge:设置相应时间
重启项目,再次访问就正常访问了。
上述实现缺点就是,需要手动添加该组注解,如果多个类,多个方法,也得手动添加多个该注解
方式二:WebMvcConfigurer 全局配置
在 springboot 中通过全局配置来进行配置 cors。
全局配置需要在 springmvc 的配置类中重写 addCorsMappings方法即可
@Configuration
public class MyWebMvcConfiguar implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")//允许哪些接口
.allowedMethods("*")
.allowedOrigins("http://localhost:8081");
}
}
/** :表示该应用的所有方法都会处理跨域请求,
allowedMethods:表示允许通过的请求方法
allowedOrigins:表示允许请求的地址
这样配置后就不必在每个类或方法上添单独配置跨域了。
方式三:CorsFilter
通过域过滤器,来进行跨域配置
@Configuration
public class MyWebMvcConfiguar implements WebMvcConfigurer {
@Bean
CorsFilter corsFilter(){
//获取UrlBasedCorsConfigurationSource
//其使用URL路径模式为请求选择CorsConfiguration。模式匹配可以通过PathMatcher或预先解析的PathPatterns来完成
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
//获取CorsConfiguration对象
CorsConfiguration cfg = new CorsConfiguration();
//设置cors
cfg.addAllowedMethod("*");
cfg.addAllowedOrigin("http://localhost:8081");
//将设置cors 注册到CorsConfiguration中
source.registerCorsConfiguration("/**", cfg);//pattern:配置哪些接口可以请求,config:跨域配置
return new CorsFilter(source);
}
}
cors存在的问题
虽然解决跨域请求问题使用户体验提高了,但是也有潜在的威胁,常见的是 CSRF,跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。跟跨网站脚本(XSS)相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。
例如:假如一家银行用以运行转账操作的URL地址如下:http://www.examplebank.com/withdraw?account=AccoutName&amount=1000&for=PayeeName
那么,一个恶意攻击者可以在另一个网站上放置如下代码:
如果有账户名为Alice的用户访问了恶意站点,而她之前刚访问过银行不久,登录信息尚未过期,那么她就会损失1000资金。
这种恶意的网址可以有很多种形式,藏身于网页中的许多地方。此外,攻击者也不需要控制放置恶意网址的网站。例如他可以将这种地址藏在论坛,博客等任何用户生成内容的网站中。这意味着如果服务端没有合适的防御措施的话,用户即使访问熟悉的可信网站也有受攻击的危险。
所以,浏览器在实际操作中,会对请求进行分类,分为简单请求,预先请求,带凭证的请求等,
预先请求会首先发送一个 options 探测请求,和浏览器进行协调请求,默认下跨域请求是不需凭证的,但可通过服务端配置要求客户端提供凭证,这样可以防止攻击。
或者添加校验token也可避免