上一篇文章带大家了解了SSO的相关内容,这篇文章针对我在实现SSO的demo中额外学的同源策略以及CORS做一个记录。
同源策略和CORS
一:同源策略
什么是同源策略?
同源策略(same-orgin policy)是一种约定,web是构建在同源策略之上的,浏览器是针对同源策略的一种实现。同源策略最初是指不同源的A网页的cookie不能被B网页的获得,后来扩展对dom操作,XmlHttpRequest也做了限制
何为同源?
协议相同
域名相同
端口相同
只有同时满足以上三个条件才谓之同源
同源策略的三个方面
Cookie同源策略:不同源cookie不共享
DOM同源策略:禁止对不同源的页面dom操作,主要针对的是iframe跨域
XmlHttpRequest同源策略:禁止使用XmlHttpRequet向不同域发起请求,例如ajax请求
下面看一下ajax跨域请求,浏览器的显示
<html><head> <title>AJAX跨域测试title> <script src="js/jquery.min.js">script>head><body><button class="btn">请求baidubutton><script> $(".btn").click(function(){ $.ajax({ url:"http://www.baidu.com", type:"get" }) })script>body>html>
点击button,ajax发起跨域请求
看一下Chrome显示
火狐
为何要有同源策略
同源策略的目的就是保障用户的信息安全,防止恶意网站窃取数据,下面通过DOM和XmlHttpRequest来说明
如果没有Dom同源策略
现在有一个假网站,通过iframe嵌入了一个银行网站
通过设置将银行网站的宽高设置到合适比例,让这个假网站在浏览器上面除了url不同,其他都和银行网站一模一样
用户在iframe嵌套的银行网站中输入了密码和账户名称
在iframe的外层通过dom操作获得用户的密码和账户
这样用户的密码和账户信息就被窃取了
如果没有XmlHttpRequest同源策略
用户登录银行网站,提交登录表单,银行服务器通过验证,为了维持对话返回Cookie
这时用户又浏览了另一个网站,而这个网站就是一个恶意网站
恶意网站后台向银行网站发起ajax请求,请求携带第一步银行的cookie,银行服务器通过cookie验证了用户身份,响应这个假冒用户的请求
这样用户的信息又泄漏了,而且是后台发起ajax请求,所以用户不会感知到这个过程
所以在同源策略的保护下,我们可以安全的上网
CORS
跨域限制
因为同源策略,所以浏览器的跨域请求被限制,但是有些时候跨域操作又是必须的;例如在CAS实现SSO的过程中,就需要在访问子系统的时候将浏览器重定向到CAS Server。
解决方案之CORS
CORS(cross orign resource sharing)即跨域资源共享,是一个W3C标准,它允许浏览器向不同源的服务器发起XmlHttpRequest请求,从而克服了AJAX,重定向只能同源使用的限制
CORS的实现需要浏览器和服务器的同时支持,整个CORS的通信过程是在后台进行的,不需要用户的参与,浏览器会根据跨域请求,自动为请求的request header额外添加一些字段,对于部分跨域请求还会在真正发出跨域请求之前发送一个preflight请求。
目前所有的浏览器都支持CORS,IE浏览器版本不能低于IE10,所以实现CORS的关键在于服务器,我认为这也正常,因为你是跨域访问我,不是我的人,还想要的资源,肯定得征得我同意呀。
俩种请求
浏览器将CORS请求分为俩类:简单请求和非简单请求
简单请求需要同时满足以下俩个条件
请求方法是 HEAD,GET,POST之一
HTTP的头字段不能超出以下几个
AcceptAccept-LanguageContent-LanguageLast-Event-IDContent-Type: 只限于三个值application/x-www.form-urlencded,multipart/form-data,text/plain
凡不是简单请求的都是非简单请求(废话!!!)
简单请求跨域流程
对于简单跨域请求,浏览器会直接向服务器发起请求,并且在请求头中添加Origin字段来说明源信息例如协议端口域名。
服务器可以通过这个字段来决定是否同意这次请求。Origin的源在服务器允许的范围内,那么服务器就会在response中添加一个Access-Control-Origin :true,如果服务器不允许,那么在浏览器端发现响应中没有Access-Control-Origin字段就会抛出一个错误。
默认浏览器不会跨域携带Cookie,如果需要,ajax请求需要设置属性withCredentials属性为true,服务器需要设置相应头部Access-Control-Allow-Credentials:true
非简单请求跨域流程
对于非简单跨域请求,浏览器会在真正发起请求前先发起一个Preflight请求给服务器,请求method使用OPTIONS,头部会加入下列信息
Origin : 源信息Access-Control-Request-Method : 列出请求使用的方法Access-Control-Request-Headers:自定义的头部信息
看一个例子,实践出真知
$(".btn").click(function(){ $.ajax({ url:"https://www.baidu.com", type:"get", headers:{ "CONTENT":"TEST" }//添加了自定义headers,所以是非简单请求 })})
浏览器F12查看network,果然浏览器提前发送了preflight请求,并且添加了相关字段
发送preflight后,服务器端可以根据Origin,Access-Control-Request-Method和Access-Control-Request-Headers决定是否允许请求,在允许的响应中会添加如下字段
Access-Control-Allow-Origin:与简单的请求相同。*代表同意任何跨域请求Access-Control-Allow-Methods: 允许的方法,多个方法以逗号分隔。Access-Control-Allow-Headers: 允许的头部,多个方法以逗号分隔。Access-Control-Max-Age: 应该将这个 Preflight 请求缓存多长时间(以秒表示)。Access-Control-Allow-Credentials: 允许携带Cookie
如果不允许,服务器返回正常的HTTP响应,但是没有任何的CORS相关的头信息,浏览器就会认定,服务器不同意预检请求,因此触发一个错误
如果允许,以后每次浏览器正常的CORS请求,就和普通请求一样了。
具体服务器可以设置一个拦截器
@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String originHeader = request.getHeader("Origin"); response.setHeader("Access-Control-Allow-Origin", originHeader); response.addHeader("Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With,accessToken"); response.addHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH"); return true;}
参考文章
http://www.ruanyifeng.com/blog/2016/04/cors.html
https://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html
https://juejin.im/post/6844903681683357710#comment
------------------
最后
我是不想当码农的小白,平时会写写一些技术博客,推荐优秀的程序员博主给大家还有自己遇到的优秀的java学习资源,希望和大家一起进步,共同成长。
以上内容如有错误,还望指出,感谢
公众号点击交流,添加我的微信,一起交流编程呗!
公众号: 小白不想当码农