跨域
跨域的三种方法
出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)
1.代理
代理其实没跨域
比如要访问一个域名,user.html,一回车,就会发给web应用的服务器,在项目中就是LiveServer,访问这个静态资源,如果直接访问它,就跨域了
所谓代理就是中间加一个代理服务器,Nigix;Nigix判断路径里带了static,静态资源就是访问静态的服务器,也就是liveServer,返回页面
ajax访问后台代码,也是通过代理服务器,不是静态资源,应该访问后台
前端访问a.com,后端访问b.com;中间加了Nigix,而Nigix是n.com;都先访问n.com,通过代理服务器来规避跨域问题
这种方法的缺点是前后端代码中路径结构必须有严格的规定,不能任意改变,因为要判断是访问前端还是后台,缺乏一定的灵活性
2.jsonp
纯前端的方案
ajax不能跨域,但是浏览器是支持某些元素跨域的,比如超链接<a></a> <img/> <scipt>,这种标签是允许跨域的
但是这种标签是死的,ajax是活的
比如说第一步已经得到了a.com这个网页,要在这个网页上异步的访问tomcat,ajax不允许跨域访问
在script标签内写一个访问b.com的路径,然后服务器正常处理这个请求,查到了这个user对象,把这个user对象转换成json格式,并且和“show({json})”拼接起来,给前台返回一个字符串
但是这个标签内要求的是返回js文件,得到的是字符串。浏览器会认为这是一个js函数,它会尝试去调这个函数,因为刚好写了这个函数,就能够调用成功,正常执行
这种方式是一种欺骗,使浏览器认为这是一个静态资源,实际上是一个动态资源;而服务端又把这个动态资源伪装成一个js给客户端,客户端加一个执行函数处理了
callback是告诉服务端要拼接成这样一种格式
明白这是一种欺骗就可以了,它有明显的局限性:
这个标签只能获取东西,不能post,适用的范围很窄,一般不用
3.跨域资源逻辑(CROS)
后端真正支持的方案
a.com要访问b.com,只要b.com同意,只要后台同意,就可以跨域;这是协议规定的
可以看到在我们代码中的实现:
在前端每个ajax请求里加这么一句话,表示允许带cookie
可以看一下这个跨域:
可以看到这里,服务器响应的时候携带信息,告诉可以跨域;服务器响应的时候告诉可以端,127.0.0.1:5500可以访问,没有问题;这个东西的生成就是基于那个注解的,整个流程是基于http协议关于跨域的一些规定
Cookie 和 Session
目的是保存客户端的状态,使服务端能够记住客户端的状态
设想这么一个场景:
相当于去理发店,要办会员,那么就要存信息;存信息的方式有两种:
第一种是写在卡片上,卡片自己带走,然后下次来了,带着这个卡片理发;这种相当于cookie,不安全,因为能伪造信息,篡改信息
第二种是把信息存到店里,但是给你颁发一个号码,下次来了看系统里有没有这个号;这个相当于session,把信息存到了服务器里,但是每次来了必须找一下,麻烦,但是敏感信息必须存到session里
Cookie:
访问a.com,服务器创建cookie把信息存储下来,存储的是键值对;因为要给客户端,需要调response.addCookie©
response是用来向浏览器响应的对象,响应的时候服务器就会把cookie发给a.com,响应的时候带上cookie的信息
当浏览器再访问a.com的时候,会主动带上cookie;服务器得到这个cookies
cookie劣势很明显,只能是字符串,并且安全性不好;所能传递的数据也比较少
Session:
浏览器访问服务器(首页),session不是我们new的,而是满足http协议规范,是JavaEE的标准规范,浏览器第一次访问,Tomcat会给我们创建一个session,用不用都会创建
这时候需要让浏览器记住这个“卡号”,所以就利用到了cookie;所以紧接着服务器会帮我们创建一个cookie,每有一个session就都会创建一个cookie,用来记session的cookie叫做JSESSIONID,存的值是session的id
然后在响应的时候,自动的返回给浏览器cookie
于是下次访问服务器的时候,比如要登录,这时候将cookie传给服务器,服务器获取session,request根据sessionid得到对应的session,然后往session中存登录的用户信息
如果再次访问商品详情页面,那么同样,得到session,再得到session里面的用户id,就知道了用户是谁
session的好处是安全,依赖于cookie,但是会给服务器造成压力,因为服务端要存session,存到内存里(因为要频繁的获取);并且session存储类型是多样的,Object;session是有时间的,默认30分钟,如果30分钟没有访问,就销毁了
分布式系统状态管理
刚刚说的是单机的情况,要分布式的话就会有问题:
(分布式就是服务器压力过大,部署了多个服务器)
例如下图,部署了两个服务器,此时浏览器访问,不知道访问哪个,这时候需要代理服务器Nigix来控制,去做负载均衡;这时候,浏览器去访问Nigix,Nigix按照一定的策略去分给多个服务器
这时候就有问题了,如果第一次登录访问的是server1,存储了用户信息,第二次分给server2,因为server2中没有对应的session,就不能访问了;
怎么处理,第一种方法是让多个服务器的session共享,同步,但是这个很麻烦,一般不这样做
第二种方法是把session迁移到一个公共的地方,数据库是不行的(数据库是做持久化操作的,是存在硬盘上的,性能不行);可以采用高性能的中间件,例如redis,redis是公用的;
这时候会有疑问,只有一个redis能行吗,redis的吞吐量QPS是10W,通常够用,不够用还能做主备,集群,也能支持大规模数据
redis存储的是jsonid,通过这个id对应到session,session中存储的是对应user
但是这种方法还有问题,比如说多个终端去访问服务器,例如每个网站有多种客户端,通过网页浏览器可以访问,通过手机APP也可以,但是服务器是一套代码支持多个客户端的,而且app的没有cookie的;这种机制还是基于session和cookie,这种方案对于多客户端的场景不适用
所以我们在多客户端的情况下,我们可以用一个广义的session,或者自己造一个session:
此时,首次访问服务器的时候,服务器创建一个能代替session的东西,create token,创建一个字符串;token和用户信息相关联,存储到redis中;并且会给客户端返回token;客户端自己想办法把这token记住,并且下次访问的时候带上,url?token= …,可以拼接url里,也可以放在header里;然后服务器就去查询token,得到对应的用户信息
token是广义的session概念,session依赖于cookie,但是客户端一旦不是浏览器,这个办法不生效;为了让服务器能支持多种客户端统一访问,改成token的方式;存到redis里的东西,会设置有过期时间
单点登录(SSO)的实现机制
单点登录:我们系统太大了,分成多个子系统,那每个子系统都要登录一遍吗?
要实现登录一次,这些子系统都能畅通无阻,这个就叫做单点登录(SSO)
比如jd.com,秒杀是一个子系统miaosha.jd.com,商品是一个子系统item.jd.com
但是我们只需要登录一次,就可以访问多个子系统,这是怎么实现的呢?
根域名相同时
访问两个服务器,也可以用同一个redis去存状态
浏览器访问a.jd.com,login登录,登录凭证session存到redis里,返回给浏览器cookie
这时候浏览器访问b.jd.com,跨域了,按理说cookie带不过来,因为cookie是哪个域给我的,访问哪个域就能带这个cookie;访问另外的域带不过来
但是因为根域名相同, 在a.jd.com中可以做一个设置,意思是cookie生效的范围是根域名;这时候访问b.jd.com就可以携带这个cookie了(因为根域名相同)
根域名不同时
两个服务器域名不同时,需要有个中间的服务器去验证,而不是通过cookie去验证
1.首先浏览器访问jd,要下单,但是京东检测到没登录;没登录肯定要登录,但是登录这件事是交给中间的sso来做,状态统一由sso管理;所以jd给浏览器的响应是,你要登录,但是是在sso里登录,所以发出重定向的响应,到另外一个网站中去,redirect指向的是sso,但是带的url是jd自己的域名,代表来源是jd.com
2.所以浏览器就去访问sso,想要去登录,访问的时候会带上jd.com,带上来源;sso返回一个登录页面login.html,让用户填相关信息
3.填完以后,浏览器再访问sso,提交登录信息login;这时候假设登录成功了,刚才是从jd来的,所以响应的时候要重定向redirect到jd.com,回到下单的环节,并且带上token,是一个登录凭证,会给客户端创建一个cookie,来存客户端的标识(BrowserID)
4.返回浏览器的Cookie里存有用户的身份信息,再访问jd.com的时候,就会传token;这时候jd需要去验证这个token对不对,需要去跟sso验证,如果有效,就说明操作成功了;这时,jd会给浏览器再颁发一个京东的token;sso给的token是一个全局token,而jd给的是局部的token(为什么要这样做,因为如果每次访问jd都带全局的token,需要每次都和sso验证;所以给一个局部的token,避免了这个麻烦)
5.这时候用户要去访问nc.com(奶茶哈哈),这个域名是和jd有关联的,比如说在jd下单,在nc支付;支付肯定也要登录,但是这里显示未登录,这时候返回一个响应,要求浏览器登录,和刚刚一样,重定向到sso,带上来源nc.com
6.浏览器再次访问sso(带上nc.com和cookie),sso看到浏览器有一个cookie(cookie存的是用户身份),此时sso基于cookie判断用户是登录过的,sso给出响应,重定向到nc.com并且带上新的token
7.浏览器得到token以后,就访问nc.com,并且带上token,nc得到token以后去和sso验证,验证有效以后支付成功,返回给客户端一个局部的token_nc,方便后面的再次访问
这时候浏览器会有三个token,访问jd,带token_jd,访问nc,带上token_nc,访问sso,带上全局的token
整个sso核心是基于一个服务器做全局的验证,颁发全局的token,而每一个子服务器会颁发一个局部的token,避免每次都要和sso验证全局token;只登陆一次,后续都是拿一个凭证去验证
服务器和服务器之间的通信,是通过webServers,有专门的服务局调度,
token可以理解为是一个随机的字符串,会带有硬件的指纹信息、时间,不会重复
如果token过期了或者是用户注销了,sso会通过webServers告诉关联的系统,去把局部token销毁掉;全局token一般有过期时间,局部的没有,局部的token销毁时通过全局token通知的
用户信息存到local_stores和session_stores里面会比cookie更好一点,安全