美文网首页我爱编程
session与cookie的理解(sso、redis)

session与cookie的理解(sso、redis)

作者: 凉拌姨妈好吃 | 来源:发表于2018-04-06 15:34 被阅读0次

    理解session和cookie之间的联系

        当程序需要为客户端创建一个session前,会先在客户端发送过来的消息内查找是否包含cookie信息,cookie信息里一般会报存一个session标识(也就是sessionId),如果没有这个sessionId就创建一个新的session和一个与该session关联的sessionId,将sessionId返回给客户端保存。(sessionId不重复)

        若上面的cookie被人为禁止了,那么我们就采用url重写的方式将标识附在url路径后(两种方式:1.作为url的附加消息;2.作为查询字符串附加在url后)

    为什么session需要设置失效时间?

        要先明白一个概念:只有客户端通知服务器删除session,服务器才会删除session。但是我们关闭浏览器前并不会给服务器发送通知“我要关闭浏览器了”,所以服务器根本不知道我们何时关闭浏览器。所以服务器就要设置一个session失效时间,避免session的长期保留占用存储空间。

    如何避免表单重复提交?(这里只考虑session方法)

        在服务端随机生成一个唯一的标识符,存入session和表单的隐藏字段中。当用户点击提交,把表单信息发送给服务器,服务器先检测标识符字段是否相等,若不等,则说明是重复提交,不再进行处理。

    如何用session共享实现单点登录?

        将sessionID存储在Redis上。

        1.用户访问某页面,该页面需要登录。用户向sso发起登录请求,sso跳转到登录界面。接收用户名、密码,判断用户名密码是否正确,正确就生成一个token,将用户信息保存至Redis上并设置有效期,返回登录成功,将token保存在cookie中,返回到该页面。

        2.在用户已经登录的前提下访问B页面,从cookie中取出token,sso服务根据token查询用户的登录信息,redis根据key获取内容,然后返回给sso,sso判断token值是否存在,存在就调整生存期,不存在则返回登录界面。

        3.若用户退出登录,那么sso将token销毁,并且取出所有由该token注册的地址,将所有注册系统发起注销请求,各子系统收到请求后销毁局部会话。用户完成单点注销

    来源:sso流程图

    sso部署方式:

            sso认证系统(只有认证系统提供登录页面)作为sso-server端,而其它子系统作为sso-client端,它们之间靠着token维持联系。

    来源

    既然存在cookie,那么为什么不直接用cookie直接进行单点登录?

        cookie的域是有限制的。出于安全的考虑,只有与创建cookie的页面处于同一个目录下,或子目录下才可以访问cookie。

        不过cookie的跨域限制是可以解决的。

        解决方法:

                        1.使用nginx进行反向代理,客户端的请求都经过nginx,再发送给服务端,nginx获得返回内容后再转交给客户端

                        2.使用jsonp,在ajax的url里写入需要跳转的网址,并且设置发送cookie(因为默认为不发送cookie)

    $.ajax({

    url:'http://www.wp.com/getData.php' 

    type:'GET', //jsonp 类型下只能使用GET,不能用POST,这里不写默认为GET

    dataType:'jsonp', //指定为jsonp类型

    data:{"name":"Zjmainstay"}, //数据参数

    jsonp:'callback',

    //服务器端获取回调函数名的key,对应后台有$_GET['callback']='‘getName';callback是默值

    jsonpCallback:'getName', //回调函数名

    success:function(result){ //成功执行处理,对应后台返回的getName(data)方法。}

    });

           3.nodejs的superagent

     但是即使cookie共享了,也还是存在着很多问题

    问题一:各子系统的web服务器必须相同,不同的服务器cookie的key值不同

    问题二:共享cookie无法实现跨语言登录

    问题三:cookie存在一定的安全隐患

    什么是Redis?

    redis其实就是一个缓存工具,可以存储大量数据。但是redis又不同于关系型数据库,因为它只能存储key-value,常用于主键查询。

    Redis的主要应用场景:

            秒杀商品、热点新闻、热门商品等(其实也就是需要频繁查询的东西放在redis里,因为相比较mysql,redis查询速度更快,性能更好)、聊天记录缓存(将聊天记录先缓存到redis,等达到一定数量再存到数据库中,这比一条一条直接存入数据库来说性能更佳)

    Redis查询为什么很快?

        虽然redis是单线程程序,但是因为它是纯内存数据库,只操作内存(而我们的mysql是硬盘操作),并且使用了异步非阻塞IO

    Redis的优势(从数据结构层面讨论)

        1.它使用了动态字符串,但是这个动态字符串最大的特点就是它的实际长度远比它的存储长度长。这样在一定范围的字符串存入,就不需要频繁的进行内存的再分配

        2.它使用了dict(也就是字典),类似于java的hashmap插入知识:hashmap的hash算法就是将当前要查找的key值进行hash,然后将得到的hash值与原数组的索引值进行对比,找到对应的value),但是与hashmap不同的一点就是dict包含两个数组。

            两个数组主要是用于扩容的,初始的dict数组长度不会过长,当超过了一定长度后,就会进行扩容。(插入知识:hashmap的扩容就是新生成一个数组,在把旧数组的值向新数组迁移)。而dict的扩容是渐进式的,就是一点点的向另一个数组迁移,不会影响当前的dict的使用。

        3.关系型数据库往往都是采用树型结构(例如mysql的B+树,B+树的搜索非常的快速,但是B+树最大的弱点就在于插入,因为需要各种的左旋右旋,拖了性能),而我们的非关系型Redis采用的是跳跃表

            什么是跳跃表?

            首先先明白数组、链表带来的插入数据的复杂

            比如:当数组里存有 1,2,3,4,6,7,8,9,10,11 时,我们向数组内插入5,使用二分查找法快速查找到与3最相近的数字(O(logN)),然后再将其他数字右移(O(N)),那么数组的插入算法的时间复杂度就是O(N)

                       当链表里存有 1,2,3,4,6,7,8,9,10,11时,往链表中插入5,那么就需要逐一比较,时间复杂度为O(N)

            这样简单的数字看起来插入查询似乎并没有什么性能的影响,但是思考一下,如果此时数据库存在10万条数据,对这个数据进行查询或者插入,是非常耗费性能的。

            所以这个时候提出来跳跃表:

                例子:我们将原数据1,2,3,4,6,7,8,9,10,11提炼出来,变成两层:1,4,8,11(2,3,6,7,9,10),那我们就只需要先对顶层数据进行查询,然后查找到与插入数据最接近的值4,那么就知道数据应该插入4-8之间,那么继续往下查询,知道插入的数据应该在4-6之间,确定了索引位置就返回原链表,迅速定位到该位置进行插入。

                        那么问题又来了!!如果这个时候已经有大量新节点插入,那么原本的索引节点就会不够用了,这个时候要怎么办呢?

              跳跃表的提出者想了一个办法:既然我们无法确定增加和删除的节点,那么我们就采用抛硬币法(概率50%),为正就把节点提到上一层索引,然后再抛硬币,若为正继续提到上一层索引,直至为反。

            好了又逼逼太多了,总结一下跳跃表的插入

            1.将新节点与各层节点逐一比较,确定插入的位置,把索引插入到原链表

            2.采用“抛硬币”法,决定节点是否提为为上一层的索引,硬币为正就往上提,为负就停止。

            逼逼太多甚至忘了自己刚才写到了哪里

        4.上面说的数据结构的优势都没有比下面我们要说的这个牛皮!!就是单线程+多路复用IO模型

            一听到单线程是不是就觉得坑了(什么鬼单线程查询速度能这么快?)

            首先,单线程肯定不可能比多线程快,但是单线程的模式避免了数据并发安全(因为多线程的数据并发安全,所以出现了各种锁)。那么这个时候我们单线程的优点就来了:不需要考虑数据并发的安全,也不用出现频繁的线程调度,切换上下文。

            上下文是啥?

            线程每次执行时都需要将数据从主内存读入到工作内存中。当线程阻塞时,工作内存的数据就需要被放到线程上下文中,逼逼了这么多,其实线程上下文就是一个存储结构,当线程被唤醒的时候,就去读取上下文的内容,就称为切换上下文。

            那么再说一说多路复用IO,其实就是当一个请求访问redis时,redis去取数据给这个请求的时间段内,请求的入口不是阻塞的,也就是说依旧可以接收请求,直到redis取到数据,再一一响应这些请求。(其实我觉得有点像消息队列)

            我们拿redis常常用来做缓存,用它会碰到一个问题“缓存击穿”

            什么叫做缓存击穿?

            我们缓存里存有一定的数据,如果缓存中查找不到数据,就到服务器里查找数据再更新redis缓存。那么如果有人恶意拿请求缓存中没有的数据,造成redis压力过大,redis往往会被整崩溃了。

            如何解决缓存击穿?

            定时更新redis缓存,缓存中查找不到的数据不允许再去服务器里查找,所有的查询以缓存中的数据为主。(虽然这会存在数据延迟的问题,但是缓存容量够大,定时更新时间合理,延迟的时间就微乎其微了)

    相关文章

      网友评论

        本文标题:session与cookie的理解(sso、redis)

        本文链接:https://www.haomeiwen.com/subject/zcmjhftx.html