一、介绍
1、什么是跨域?构成跨域的条件是什么?
当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域。
当前页面url | 被请求页面url | 是否跨域 原因 |
---|---|---|
http://www.test.com/ | http://www.test.com/index.html | 否 同源(协议、域名、端口号相同) |
http://www.test.com/ | https://www.test.com/index.html | 跨域 协议不同(http/https) |
http://www.test.com/ | http://www.baidu.com/ | 跨域 主域名不同(test/baidu) |
http://www.test.com/ | http://blog.test.com/ | 跨域 子域名不同(www/blog) |
http://www.test.com:8080/ | http://www.test.com:7001/ | 跨域 端口号不同(8080/7001) |
那为什么跨域就不能访问呢?这是因为浏览器的同源策略。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,简单来说就是一种安全机制,它规定了只有同源下才能进行访问。
而所谓同源(即指在同一个域)就是两个页面具有相同的协议,主机、端口号。在非同源情况下的请求会受到以下限制:
【1】无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB
【2】无法接触非同源网页的 DOM
【3】无法向非同源地址发送 AJAX 请求
但是有三个标签是允许跨域加载资源:
【1】<img/>
【2】<link/>
【3】<script/>
但我们首先要明确以下几点:
跨域只存在于浏览器端,不存在于安卓/ios/Node.js/python/ java等其它环境
跨域请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。
2、没有同源策略的危险场景
举两个例子
1、没有同源策略限制的接口请求
有一个小小的东西叫cookie大家应该知道,一般用来处理登录等场景,目的是让服务端知道谁发出的这次请求。如果你请求了接口进行登录,服务端验证通过后会在响应头加入Set-Cookie字段,然后下次再发请求的时候,浏览器会自动将cookie附加在HTTP请求的头字段Cookie中,服务端就能知道这个用户已经登录过了。知道这个之后,我们来看场景: 1.你准备去清空你的购物车,于是打开了买买买网站www.maimaimai.com,然后登录成功,一看,购物车东西这么少,不行,还得买多点。 2.你在看有什么东西买的过程中,你的好基友发给你一个链接www.nidongde.com,一脸坏笑地跟你说:“你懂的”,你毫不犹豫打开了。 3.你饶有兴致地浏览着www.nidongde.com,谁知这个网站暗地里做了些不可描述的事情!由于没有同源策略的限制,它向www.maimaimai.com发起了请求!聪明的你一定想到上面的话“服务端验证通过后会在响应头加入Set-Cookie字段,然后下次再发请求的时候,浏览器会自动将cookie附加在HTTP请求的头字段Cookie中”,这样一来,这个不法网站就相当于登录了你的账号,可以为所欲为了!如果这不是一个买买买账号,而是你的银行账号,那……。这就是传说中的CSRF攻击。但及时有同源策略的保护,cookie是明文的可以通过XSS攻击并可以轻松获得你的cookie。其实没有刺不穿的盾,只是攻击的成本和攻击成功后获得的利益成不成正比。攻击者就不会想着攻击了。可以攻击,但没必要。
2、没有同源策略限制的Dom查询
1.有一天你刚睡醒,收到一封邮件,说是你的银行账号有风险,赶紧点进www.yinghang.com改密码。你吓尿了,赶紧点进去,还是熟悉的银行登录界面,你果断输入你的账号密码,登录进去看看钱有没有少了。 2.睡眼朦胧的你没看清楚,平时访问的银行网站是www.yinhang.com,而现在访问的是www.yinghang.com,这个钓鱼网站直接引用了www.yinhang.com的页面,而你没有任何察觉的输入了你的银行密码。。。。。
总结
由此我们知道,同源策略确实能规避一些危险,不是说有了同源策略就安全,只是说同源策略是一种浏览器最基本的安全机制,毕竟能提高一点攻击的成本。
参考网站:https://segmentfault.com/a/1190000015597029?utm_source=tag-newest#articleHeader6
二、解决方案
核心逻辑在于:给响应头设置Access-Control-Allow-xxxxx
字段,表示服务端支持某些域名可以访问。如:
httpResponse.setHeader("Access-Control-Allow-Headers","准许的请求头字段);
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Allow-Origin", '访问的ip');
httpResponse.setHeader("Access-Control-Allow-Methods", '准许的请求方式');
httpResponse.setHeader("Access-Control-Max-Age", "验证的时间");
1、基于Spring4.2以上的解决方案
参考网站:https://segmentfault.com/a/1190000018554528?utm_source=tag-newest
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/greeting-javaconfig").allowedOrigins("http://localhost:9000");
}
};
}
2、filter解决方案
yml配置
#cors 防跨域攻击
cors:
#允许跨域域名
corsAllowedOrigins: http://127.0.0.1:9090,http://localhost:9090
#允许跨域方法
corsAllowedMethod: OPTIONS,GET,POST,DELETE,PUT
java代码
package guhong.core.api.filter;
import cn.hutool.core.util.StrUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author : 李双凯
* @date : 2019-11-20 22:32
**/
@Component
@WebFilter(urlPatterns = "/*")
public class CorsFilter implements Filter {
@Value("${cors.corsAllowedOrigins}")
private String[] corsAllowedOrigins;
@Value("${cors.corsAllowedMethod}")
private String corsAllowedMethod;
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest)request;
if (httpRequest.getRequestURL().toString().matches(".+.ico$")) {
chain.doFilter(request, response);
} else {
String origin = httpRequest.getHeader("origin");
boolean isValid = false;
if (null != corsAllowedOrigins) {
for (String corsAllowedOrigin : corsAllowedOrigins) {
if (StrUtil.isNotBlank(corsAllowedOrigin) && corsAllowedOrigin.equals(origin)) {
// 如果当前域名准许跨域访问,则为true,将其添加到Access-Control-Allow-Origin中去
isValid = true;
break;
}
}
}
HttpServletResponse httpResponse = (HttpServletResponse)response;
httpResponse.setHeader("Access-Control-Allow-Headers","Origin, X-Requested-With, Content-Type, Accept, token");
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Allow-Origin", isValid ? origin : null);
httpResponse.setHeader("Access-Control-Allow-Methods", corsAllowedMethod);
httpResponse.setHeader("Access-Control-Max-Age", "3600");
chain.doFilter(request, httpResponse);
}
}
}
三、相关知识
1、CSRF攻击
简单来说,就是通过获取你未登出的网站的cookie、认证,从而以你的身份操作你的信息。
2、XSS攻击
简单来说,就是在表单提交的时,输入的数据变成了JavaScript,而这些嵌入的JavaScript代码将会被执行,从而获取你的信息。