1.什么是跨域资源请求?
https://www.cnblogs.com/niuli1987/p/10252214.html
同源: 如果两个页面的协议,端口(如果有指定)和域名都相同,则两个页面具有相同的源。
1.1 同源策略 : 浏览器的一个安全功能,不同源的客户端js脚本在没有明确授权的情况下,不能读写对方资源。用于隔离潜在恶意文件的重要安全机制。
同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。
使用js脚本读写非同源的资源会被拒绝的(跨域资源的引入是可以的,使用js读写则受限制),因此 XMLHttpRequest 受同源策略限制。
1.2 不受同源策略限制的(跨域资源的引入是允许的)
页面中的链接,重定向以及表单提交是不会受到同源策略限制的。
如嵌入到页面中的<script src="..."></script>,<img>,<link>,<iframe>等。
ps: 跨域限制都是对浏览器端来说的,服务器端是不存在跨域安全限制。
2.如何解决 跨域资源请求 限制
- JSONP https://blog.csdn.net/DFF1993/article/details/79925874
- CORS http://www.ruanyifeng.com/blog/2016/04/cors.html
https://www.cnblogs.com/niuli1987/p/10252214.html
- 利用nginx 反向代理解决跨域问题 https://www.cnblogs.com/bninp/p/5694277.html
2.1 JSONP (不推荐)
例子:跨域资源位于 http://localhost:8066/file/jsonp
springboot 工程
@RequestMapping(value="/jsonp", method=RequestMethod.GET ) public String jsonp(@RequestParam("callback") String callback, HttpServletRequest request) { // 处理正确的jsonp请求, 返回: callback方法名(json字符串) if(callback != null && !callback.equals("")) { return callback + "(" + "{"key": "hello"}" + ")"; } //不是jsonp请求 return "hello"; }
2.1.1 使用ajax出现 跨域请求限制
XMLHttpRequest发起了请求,但是响应中获取不到值
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>ajax 跨域请求(不能成功)</title> </head> <body> <div id="mydiv"> <button id="btn">点击</button> </div> </body> <script type="text/javascript"> window.onload = function() { var oBtn = document.getElementById('btn'); oBtn.onclick = function() { var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if (xhr.readyState == 4 && xhr.status == 200) { // 处理响应 alert( xhr.responseText ); } }; // 跨域请求 xhr.open('get', 'http://localhost:8066/file/jsonp?callback', true); xhr.send(); }; }; </script> </html>
2.1.2 使用JSONP 避免跨域请求限制
原理:利用 <script src="..."></script> 中src 引入跨域资源(不受同源策略限制),浏览器收到响应后,通知回调函数处理该跨域资源。
缺点:只能通过是get请求引入跨域资源。
在页面插入带有src 属性的 <script>标签,src 地址即跨域资源地址;
服务端对于 JSONP请求的 响应格式是: callback函数名(JSON字符串) 。 (非标准协议)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>JSONP实现跨域(只支持get请求)</title> </head> <body> <div id="mydiv"> <button id="btn">点击</button> </div> </body> <script type="text/javascript"> // 回调函数,处理响应 function handleResponse(response){ console.log(response); alert(JSON.stringify(response)); //将json对象转为 字符串 } </script> <script type="text/javascript"> window.onload = function() { var oBtn = document.getElementById('btn'); oBtn.onclick = function() { // 创建一个script标签 var script = document.createElement("script"); //设置script标签的src script.src = "http://localhost:8066/file/jsonp?callback=handleResponse"; //在页面插入一个script标签,将会发起src请求 document.body.insertBefore(script, document.body.firstChild); }; }; </script> </html>
#####
2.2 CORS (推荐)
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出XMLHttpRequest
请求,从而克服了AJAX只能同源使用的限制。
CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信(包括客户端跨域读取cookie,跨域访问服务端资源)。
例如1: https://api.github.com/ 支持跨域资源请求
响应头中含有 Access-Control-Allow-Origin ; 它的值要么是请求时Origin
字段的值,要么是一个*
,表示接受任意域名的请求。
例2 :服务端配置响应头,客户端可跨域访问cookie
resp.setHeader("Access-Control-Allow-Origin", origin); // 允许指定域访问跨域资源
resp.setHeader("Access-Control-Allow-Credentials", "true"); // 允许客户端携带跨域cookie,此时origin值不能为“*”,只能为指定单一域名
2.2.1 springboot 后台服务 配置 支持 CORS
配置 WebMvcConfigurerAdapter, 可提支持 站外Ajax请求访问的跨域资源
1)配置后台服务
import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; @Configuration public class CORSConfiguration extends WebMvcConfigurerAdapter { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedMethods("*") .allowedOrigins("*") .allowedHeaders("*"); } }
addMapping:配置可以被跨域的路径,可以任意配置,可以具体到直接请求路径。
allowedOrigins:允许所有的请求域名访问我们的跨域资源,可以固定单条或者多条内容,如:"http://www.baidu.com",只有百度可以访问我们的跨域资源。
allowedHeaders:允许所有的请求header访问,可以自定义设置任意请求头信息,如:"X-YAUTH-TOKEN"
2)可以直接使用 XMLHttpRequest 访问跨域资源
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>ajax 跨域请求</title> </head> <body> <div id="mydiv"> <button id="btn">点击</button> </div> </body> <script type="text/javascript"> window.onload = function() { var oBtn = document.getElementById('btn'); oBtn.onclick = function() { var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if (xhr.readyState == 4 && xhr.status == 200) { // 处理响应 alert( xhr.responseText ); } }; // 跨域请求 xhr.open('get', 'http://localhost:8066/file/jsonp?callback', true); xhr.send(); }; }; </script> </html>
2.3 利用nginx 反向代理解决跨域问题
location /apis {
rewrite ^.+apis/?(.*)$ /$1 break;
include uwsgi_params;
proxy_pass http://www.baidu.com/;
}
对于浏览器来说,访问的就是同源服务器上的一个url。而nginx通过检测url前缀,把http请求转发到后面真实的物理服务器。并通过rewrite命令把前缀再去掉。这样真实的服务器就可以正确处理请求,并且并不知道这个请求是来自代理服务器的。
3. 跨域请求伪造 CSRF 防御
CORS - Cross Origin Resourse-Sharing - 跨站资源共享
CSRF - Cross-Site Request Forgery - 跨站请求伪造
3.1 如何防止 CSRF 攻击
https://www.bilibili.com/video/av33502871/?spm_id_from=333.788.videocard.0
CSRF 攻击:
当用户不小心在本机访问 fuck.com
黑客页面的时候,黑客页面上放了一个按钮或者一个表单(URL/action 为 http://you.com/delete-myself,当前用户登录过的网站),
当用户触发这个按钮或表单的,浏览器发出 GET 或 POST 请求的时候,会带上 you.com
的 cookie;如果you.com网站没有做 CSRF 防御措施,那么这次请求在 you.com
看来会是完全合法的,但是实际上是黑客伪造的请求。
CSRF 防御:
CSRF攻击之所以能够成功,是因为攻击者可以伪造用户的请求。(该请求中所有的用户验证信息都存在于Cookie中,攻击者可以在不知道这些验证信息的情况下直接利用用户自己的Cookie来通过安全验证。)
CSRF 主流防御方式是,用户每次发起请求之前,先从后端获取随机 token(后端同时将此 token 保存到缓存如redis中);
用户发起请求时携带该token,如果后端检查到没有 token或者提交的token和后端缓存的不一致,则请求失败;当token校验通过后,此toiken在缓存中被删除以防止token被冒用。
*** 如何确保获取token的请求不是伪造的??