首页 文章 精选 留言 我的

精选列表

搜索[面试],共4915篇文章
优秀的个人博客,低调大师

Redis Cluster 原理详解,应付面试官就看这一篇!

Redis 缓存作为使用最多的缓存工具被各大厂商争相使用。通常我们会使用单体的 Redis 应用作为缓存服务,为了保证其高可用还会使用主从模式(Master-Slave),又或者是读写分离的设计。但是当缓存数据量增加以后,无法用单体服务器承载缓存服务时,就需要对缓存服务进行扩展。将需要缓存的数据切分成不同的分区,将数据分区放到不同的服务器中,用分布式的缓存来承载高并发的缓存访问。恰好 Redis Cluster 方案刚好支持这部分功能。 今天就来一起看看 Redis Cluster 的核心原理和实践: Redis Cluster 实现数据分区 分布式缓存节点之间的通讯 请求分布式缓存的路由 缓存节点的扩展和收缩 故障发现和恢复 Redis Cluster 实现数据分区 正如开篇中提到的,分布式数据库要解决的就是将整块数据,按照规则分配到多个缓存节点,解决的是单个缓存节点处理数量大的问题。如果要将这些数据进行拆分,并且存放必须有一个算法。例如:哈希算法和哈希一致性算法,这些比较经典的算法。 Redis Cluster 则采用的是虚拟槽分区算法。其中提到了槽(Slot)的概念。这个槽是用来存放缓存信息的单位,在 Redis 中将存储空间分成了 16384 个槽,也就是说 Redis Cluster 槽的范围是 0 -16383(2^4 * 2^10)。 缓存信息通常是用 Key-Value 的方式来存放的,在存储信息的时候,集群会对 Key 进行 CRC16 校验并对 16384 取模(slot = CRC16(key)%16383)。 得到的结果就是 Key-Value 所放入的槽,从而实现自动分割数据到不同的节点上。然后再将这些槽分配到不同的缓存节点中保存。图1:Redis 集群中的数据分片 如图 1 所示,假设有三个缓存节点分别是 1、2、3。Redis Cluster 将存放缓存数据的槽(Slot)分别放入这三个节点中:缓存节点 1 存放的是(0-5000)Slot 的数据。缓存节点 2 存放的是(5001-10000)Slot 的数据。缓存节点 3 存放的是(10000-16383)Slot 的数据。 此时 Redis Client 需要根据一个 Key 获取对应的 Value 的数据,首先通过 CRC16(key)%16383 计算出 Slot 的值,假设计算的结果是 5002。 将这个数据传送给 Redis Cluster,集群接受到以后会到一个对照表中查找这个 Slot=5002 属于那个缓存节点。 发现属于“缓存节点 2”,于是顺着红线的方向调用缓存节点 2 中存放的 Key-Value 的内容并且返回给 Redis Client。 分布式缓存节点之间的通讯 如果说 Redis Cluster 的虚拟槽算法解决的是数据拆分和存放的问题,那么存放缓存数据的节点之间是如何通讯的,就是接下来我们要讨论的。 缓存节点中存放着缓存的数据,在 Redis Cluster 的分布式部署下,缓存节点会被分配到一台或者多台服务器上。图 2:新上线的缓存节点 2 和缓存节点 1 进行通讯 缓存节点的数目也有可能根据缓存数据量和支持的并发进行扩展。如图 2 所示,假设 Redis Cluster 中存在“缓存节点 1”,此时由于业务扩展新增了“缓存节点 2”。 新加入的节点会通过 Gossip 协议向老节点,发出一个“Meet 消息”。收到消息以后“缓存节点 1”,会礼貌地回复一个“Pong 消息”。 此后“缓存节点 2”会定期发送给“缓存节点 1” 一个“Ping 消息”,同样的“缓存节点 1”每次都会回复“Pong 消息”。 上面这个例子说明了,在 Redis Cluster 中缓存节点之间是通过 Gossip 协议进行通讯的。 其实节点之间通讯的目的是为了维护节点之间的元数据信息。这个元数据就是每个节点包含哪些数据,是否出现故障。 节点之间通过 Gossip 协议不断相互交互这些信息,就好像一群人在一起八卦一样,没有多久每个节点就知道其他所有节点的情况了,这个情况就是节点的元数据。 整个传输过程大致分为以下几点:Redis Cluster 的每个缓存节点都会开通一个独立的 TCP 通道,用于和其他节点通讯。有一个节点定时任务,每隔一段时间会从系统中选出“发送节点”。这个“发送节点”按照一定频率,例如:每秒 5 次,随机向最久没有通讯的节点发起 Ping 消息。接受到 Ping 消息的节点会使用 Pong 消息向“发送节点”进行回复。 不断重复上面行为,让所有节点保持通讯。他们之间通讯是通过 Gossip 协议实现的。 从类型上来说其分为了四种,分别是:Meet 消息,用于通知新节点加入。就好像上面例子中提到的新节点上线会给老节点发送 Meet 消息,表示有“新成员”加入。Ping 消息,这个消息使用得最为频繁,该消息中封装了自身节点和其他节点的状态数据,有规律地发给其他节点。Pong 消息,在接受到 Meet 和 Ping 消息以后,也将自己的数据状态发给对方。同时也可以对集群中所有的节点发起广播,告知大家的自身状态。Fail 消息,如果一个节点下线或者挂掉了,会向集群中广播这个消息。图 3:Gossip 协议结构 Gossip 协议的结构如图 3 所示,有其中 type 定义了消息的类型,例如:Meet、Ping、Pong、Fail 等消息。 另外有一个 myslots 的数组定义了节点负责的槽信息。每个节点发送 Gossip 协议给其他节点最重要的就是将该信息告诉其他节点。另外,消息体通过 clusterMsgData 对象传递消息征文。 请求分布式缓存的路由 对内,分布式缓存的节点通过 Gossip 协议互相发送消息,为了保证节点之间了解对方的情况。 那么对外来说,一个 Redis 客户端如何通过分布式节点获取缓存数据,就是分布式缓存路由要解决的问题了。 上文提到了 Gossip 协议会将每个节点管理的槽信息发送给其他节点,其中用到了 unsigned char myslots[CLUSTER_SLOTS/8] 这样一个数组存放每个节点的槽信息。 myslots 属性是一个二进制位数组(bit array),其中 CLUSTER_SLOTS 为 16384。 这个数组的长度为 16384/8=2048 个字节,由于每个字节包含 8 个 bit 位(二进制位),所以共包含 16384 个 bit,也就是 16384 个二进制位。 每个节点用 bit 来标识自己是否拥有某个槽的数据。如图 4 所示,假设这个图表示节点 A 所管理槽的情况。图 4:通过二进制数组存放槽信息 0、1、2 三个数组下标就表示 0、1、2 三个槽,如果对应的二进制值是 1,表示该节点负责存放 0、1、2 三个槽的数据。同理,后面的数组下标位 0 就表示该节点不负责存放对应槽的数据。 用二进制存放的优点是,判断的效率高,例如对于编号为 1 的槽,节点只要判断序列的第二位,时间复杂度为 O(1)。图 5:接受节点把节点槽的对应信息保存在本地 如图 5 所示,当收到发送节点的节点槽信息以后,接受节点会将这些信息保存到本地的 clusterState 的结构中,其中 Slots 的数组就是存放每个槽对应哪些节点信息。图 6:ClusterStatus 结构以及槽与节点的对应 如图 6 所示,ClusterState 中保存的 Slots 数组中每个下标对应一个槽,每个槽信息中对应一个 clusterNode 也就是缓存的节点。 这些节点会对应一个实际存在的 Redis 缓存服务,包括 IP 和 Port 的信息。 Redis Cluster 的通讯机制实际上保证了每个节点都有其他节点和槽数据的对应关系。 Redis 的客户端无论访问集群中的哪个节点都可以路由到对应的节点上,因为每个节点都有一份 ClusterState,它记录了所有槽和节点的对应关系。 下面来看看 Redis 客户端是如何通过路由来调用缓存节点的:图 7:MOVED 重定向请求 如图 7 所示,Redis 客户端通过 CRC16(key)%16383 计算出 Slot 的值,发现需要找“缓存节点 1”读/写数据,但是由于缓存数据迁移或者其他原因导致这个对应的 Slot 的数据被迁移到了“缓存节点 2”上面。 那么这个时候 Redis 客户端就无法从“缓存节点 1”中获取数据了。 但是由于“缓存节点 1”中保存了所有集群中缓存节点的信息,因此它知道这个 Slot 的数据在“缓存节点 2”中保存,因此向 Redis 客户端发送了一个 MOVED 的重定向请求。 这个请求告诉其应该访问的“缓存节点 2”的地址。Redis 客户端拿到这个地址,继续访问“缓存节点 2”并且拿到数据。 上面的例子说明了,数据 Slot 从“缓存节点 1”已经迁移到“缓存节点 2”了,那么客户端可以直接找“缓存节点 2”要数据。 那么如果两个缓存节点正在做节点的数据迁移,此时客户端请求会如何处理呢?图 8:ASK 重定向请求 如图 8 所示,Redis 客户端向“缓存节点 1”发出请求,此时“缓存节点 1”正向“缓存节点 2”迁移数据,如果没有命中对应的 Slot,它会返回客户端一个 ASK 重定向请求并且告诉“缓存节点 2”的地址。 客户端向“缓存节点 2”发送 Asking 命令,询问需要的数据是否在“缓存节点 2”上,“缓存节点 2”接到消息以后返回数据是否存在的结果。 缓存节点的扩展和收缩 作为分布式部署的缓存节点总会遇到缓存扩容和缓存故障的问题。这就会导致缓存节点的上线和下线的问题。 由于每个节点中保存着槽数据,因此当缓存节点数出现变动时,这些槽数据会根据对应的虚拟槽算法被迁移到其他的缓存节点上。图 9:分布式缓存扩容 如图 9 所示,集群中本来存在“缓存节点 1”和“缓存节点 2”,此时“缓存节点 3”上线了并且加入到集群中。 此时根据虚拟槽的算法,“缓存节点 1”和“缓存节点 2”中对应槽的数据会应该新节点的加入被迁移到“缓存节点 3”上面。 针对节点扩容,新建立的节点需要运行在集群模式下,因此新建节点的配置最好与集群内其他节点配置保持一致。 新节点加入到集群的时候,作为孤儿节点是没有和其他节点进行通讯的。因此,其会采用 cluster meet 命令加入到集群中。 在集群中任意节点执行 cluster meet 命令让新节点加入进来。假设新节点是 192.168.1.1 5002,老节点是 192.168.1.1 5003,那么运行以下命令将新节点加入到集群中。192.168.1.1 5003> cluster meet 192.168.1.1 5002 这个是由老节点发起的,有点老成员欢迎新成员加入的意思。新节点刚刚建立没有建立槽对应的数据,也就是说没有缓存任何数据。 如果这个节点是主节点,需要对其进行槽数据的扩容;如果这个节点是从节点,就需要同步主节点上的数据。总之就是要同步数据。图 10:节点迁移槽数据的过程 如图 10 所示,由客户端发起节点之间的槽数据迁移,数据从源节点往目标节点迁移:客户端对目标节点发起准备导入槽数据的命令,让目标节点准备好导入槽数据。这里使用 cluster setslot {slot} importing {sourceNodeId} 命令。之后对源节点发起送命令,让源节点准备迁出对应的槽数据。使用命令 cluster setslot {slot} importing {sourceNodeId}。此时源节点准备迁移数据了,在迁移之前把要迁移的数据获取出来。通过命令 cluster getkeysinslot {slot} {count}。Count 表示迁移的 Slot 的个数。然后在源节点上执行,migrate {targetIP} {targetPort} “” 0 {timeout} keys{keys} 命令,把获取的键通过流水线批量迁移到目标节点。重复 3 和 4 两步不断将数据迁移到目标节点。目标节点获取迁移的数据。完成数据迁移以后目标节点,通过 cluster setslot {slot} node {targetNodeId} 命令通知对应的槽被分配到目标节点,并且广播这个信息给全网的其他主节点,更新自身的槽节点对应表。 既然有缓存服务器的上线操作,那么也有下线的操作。下线操作正好和上线操作相反,将要下线缓存节点的槽数据分配到其他的缓存主节点中。 迁移的过程也与上线操作类似,不同的是下线的时候需要通知全网的其他节点忘记自己,此时通过命令 cluster forget{downNodeId} 通知其他的节点。 当节点收到 forget 命令以后会将这个下线节点放到仅用列表中,那么之后就不用再向这个节点发送 Gossip 的 Ping 消息了。 不过这个仅用表的超时时间是 60 秒,超过了这个时间,依旧还会对这个节点发起 Ping 消息。 不过可以使用 redis-trib.rb del-node{host:port} {donwNodeId} 命令帮助我们完成下线操作。 尤其是下线的节点是主节点的情况下,会安排对应的从节点接替主节点的位置。 故障发现和恢复 前面在谈到缓存节点扩展和收缩是提到,缓存节点收缩时会有一个下线的动作。 有些时候是为了节约资源,或者是计划性的下线,但更多时候是节点出现了故障导致下线。 针对下线故障来说有两种下线的确定方式: 主观下线:当节点 1 向节点 2 例行发送 Ping 消息的时候,如果节点 2 正常工作就会返回 Pong 消息,同时会记录节点 1 的相关信息。 同时接受到 Pong 消息以后节点 1 也会更新最近一次与节点 2 通讯的时间。 如果此时两个节点由于某种原因断开连接,过一段时间以后节点 1 还会主动连接节点 2,如果一直通讯失败,节点 1 中就无法更新与节点 2 最后通讯时间了。 此时节点 1 的定时任务检测到与节点 2 最好通讯的时间超过了 cluster-node-timeout 的时候,就会更新本地节点状态,把节点 2 更新为主观下线。 这里的 cluster-node-timeout 是节点挂掉被发现的超时时间,如果超过这个时间还没有获得节点返回的 Pong 消息就认为该节点挂掉了。 这里的主观下线指的是,节点 1 主观的认为节点 2 没有返回 Pong 消息,因此认为节点 2 下线。 只是节点 1 的主观认为,有可能是节点 1 与节点 2 之间的网络断开了,但是其他的节点依旧可以和节点 2 进行通讯,因此主观下线并不能代表某个节点真的下线了。 客观下线:由于 Redis Cluster 的节点不断地与集群内的节点进行通讯,下线信息也会通过 Gossip 消息传遍所有节点。 因此集群内的节点会不断收到下线报告,当半数以上持有槽的主节点标记了某个节点是主观下线时,便会触发客观下线的流程。 也就是说当集群内的半数以上的主节点,认为某个节点主观下线了,才会启动这个流程。 这个流程有一个前提,就是直针对主节点,如果是从节点就会忽略。也就是说集群中的节点每次接受到其他节点的主观下线是都会做以下的事情。 将主观下线的报告保存到本地的 ClusterNode 的结构中,并且针对主观下线报告的时效性进行检查,如果超过 cluster-node-timeout*2 的时间,就忽略这个报告。 否则就记录报告内容,并且比较被标记下线的主观节点的报告数量大于等于持有槽的主节点数量的时候,将其标记为客观下线。 同时向集群中广播一条 Fail 消息,通知所有的节点将故障节点标记为客观下线,这个消息指包含故障节点的 ID。 此后,群内所有的节点都会标记这个节点为客观下线,通知故障节点的从节点出发故障转移的流程,也就是故障的恢复。 说白了,客观下线就是整个集群中有一半的节点都认为某节点主观下线了,那么这个节点就被标记为客观下线了。 如果某个主节点被认为客观下线了,那么需要从它的从节点中选出一个节点替代主节点的位置。 此时下线主节点的所有从节点都担负着恢复义务,这些从节点会定时监测主节点是否下线。 一旦发现下线会走如下的恢复流程: ①资格检查,每个节点都会检查与主节点断开的时间。如果这个时间超过了 cluster-node-timeout*cluster-slave-validity-factor(从节点有效因子,默认为 10),那么就没有故障转移的资格。 也就是说这个从节点和主节点断开的太久了,很久没有同步主节点的数据了,不适合成为新的主节点,因为成为主节点以后其他的从节点回同步自己的数据。 ②触发选举,通过了上面资格的从节点都可以触发选举。但是出发选举是有先后顺序的,这里按照复制偏移量的大小来判断。 这个偏移量记录了执行命令的字节数。主服务器每次向从服务器传播 N 个字节时就会将自己的复制偏移量+N,从服务在接收到主服务器传送来的 N 个字节的命令时,就将自己的复制偏移量+N。 复制偏移量越大说明从节点延迟越低,也就是该从节点和主节点沟通更加频繁,该从节点上面的数据也会更新一些,因此复制偏移量大的从节点会率先发起选举。 ③发起选举,首先每个主节点会去更新配置纪元(clusterNode.configEpoch),这个值是不断增加的整数。 在节点进行 Ping/Pong 消息交互式也会更新这个值,它们都会将最大的值更新到自己的配置纪元中。 这个值记录了每个节点的版本和整个集群的版本。每当发生重要事情的时候,例如:出现新节点,从节点精选。都会增加全局的配置纪元并且赋给相关的主节点,用来记录这个事件。 说白了更新这个值目的是,保证所有主节点对这件“大事”保持一致。大家都统一成一个配置纪元(一个整数),表示大家都知道这个“大事”了。 更新完配置纪元以后,会想群内发起广播选举的消息(FAILOVER_AUTH_REQUEST)。并且保证每个从节点在一次配置纪元中只能发起一次选举。 ④投票选举,参与投票的只有主节点,从节点没有投票权,超过半数的主节点通过某一个节点成为新的主节点时投票完成。 如果在 cluster-node-timeout*2 的时间内从节点没有获得足够数量的票数,本次选举作废,进行第二轮选举。 这里每个候选的从节点会收到其他主节点投的票。在第2步领先的从节点通常此时会获得更多的票,因为它触发选举的时间更早一些。 获得票的机会更大,也是由于它和原主节点延迟少,理论上数据会更加新一点。 ⑤当满足投票条件的从节点被选出来以后,会触发替换主节点的操作。新的主节点别选出以后,删除原主节点负责的槽数据,把这些槽数据添加到自己节点上。 并且广播让其他的节点都知道这件事情,新的主节点诞生了。 总结 本文通过 Redis Cluster 提供了分布式缓存的方案为出发点,针对此方案中缓存节点的分区方式进行了描述。 虚拟槽的分区算法,将整块数据分配到了不同的缓存节点,通过 Slot 和 Node 的对应关系让数据找到节点的位置。 对于分布式部署的节点,需要通过 Gossip 协议进行 Ping、Pong、Meet、Fail 的通讯,达到互通有无的目的。 当客户端调用缓存节点数据的时候通过 MOVED 和 ASKED 重定向请求找到正确的缓存节点。 并且介绍了在缓存扩容和收缩时需要注意的处理流程,以及数据迁移的方式。 最后,讲述如何发现故障(主观下线和客观下线)以及如何恢复故障(选举节点)的处理流程。

优秀的个人博客,低调大师

这些喜闻乐见的Java面试知识点,你都掌握了吗?

最近分享了一些有关学习方法和经验的文章,得到了很多读者的反馈,恰巧大家在昨天推文中的投票里一直选择了“Java基础的复习方法”这一项,那么今天我们就谈谈这方面的内容吧。 其实对于Java基础的学习,我觉得最好的方法就是理论结合实践,先通过书籍,博客等内容理解基本原理,再通过写一些demo进行实践,做到知其然又知其所以然。 前言以下内容出自我的CSDN技术博客专栏,由于内容较多,我在这里只贴一个大概的目录。 本系列文章是我在秋招复习过程中创作和整理的内容,当时的初衷也是希望能够通过写博客把这些知识点吃透,当然我也是通过阅读博客+代码实践的方式来学习的,所以也有部分文章是转载的,毕竟大牛的博客真的可以把知识点讲的很透彻。 大家可以根据需要进行阅读。即使看的不是我博客的文章,也可以按照这个顺序进行阅读学习,我个人觉得效果还是不错的。 专栏:深入浅出Java核心技术 本专栏主要介绍Java基础,并且会结合实现原理以及具体实例来讲解。同时还介绍了Java集合类,设计模式以及Java8的相关知识。 专栏地址:https://blog.csdn.net/a724888/column/info/21930 ONE Java基础知识 Java基础1:深入理解Java面向对象三大特性Java基础2:基本数据类型与常量池Java基础3:深入理解String及包装类java基础4:深入理解final关键字Java基础5:抽象类和接口Java基础6:代码块与代码加载顺序Java基础7:关于Java类和包的那些事Java基础8:深入理解内部类Java基础9:解读Java回调机制Java基础10:全面解读Java异常Java基础11:Java泛型详解Java基础12:深入理解Class类和Object类Java基础13:反射详解Java基础14:离开IDE,使用java和javac构建项目Java基础15:深入剖析Java枚举类Java基础16:Java多线程基础最全总结Java基础17:Java IO流总结Java基础18:Java序列化与反序列化Java基础19:Java集合框架梳理Java基础20:Java8新特性终极指南 TWO Java集合类 Java集合详解1:ArrayList,Vector与Stack Java集合详解2:LinkedList和Queue Java集合详解3:Iterator,fail-fast机制与比较器 Java集合详解4:HashMap和HashTable Java集合详解5:深入理解LinkedHashMap和LRU缓存 Java集合详解6:TreeMap和红黑树 Java集合详解7:HashSet,TreeSet与LinkedHashSet Java集合详解8:Java集合类细节精讲 THREE 设计模式 初探Java设计模式1:创建型模式(工厂,单例等) 初探Java设计模式2:结构型模式(代理模式,适配器模式等) 初探Java设计模式3:行为型模式(策略,观察者等)初探设计模式 初探Java设计模式4:JDK中的设计模式 初探Java设计模式5:Spring涉及到的9种设计模式

优秀的个人博客,低调大师

面试必问的4种单点登录的实现方式,你知道几个?

前言 单点登录在现在的系统架构中广泛存在,他将多个子系统的认证体系打通,实现了一个入口多处使用,而在架构单点登录时,也会遇到一些小问题,在不同的应用环境中可以采用不同的单点登录实现方案来满足需求。 一、共享Session 共享Session可谓是实现单点登录最直接、最简单的方式。将用户认证信息保存于Session中,即以Session内存储的值为用户凭证,这在单个站点内使用是很正常也很容易实现的,而在用户验证、用户信息管理与业务应用分离的场景下即会遇到单点登录的问题,在应用体系简单,子系统很少的情况下,可以考虑采用Session共享的方法来处理这个问题。 这个架构我使用了基于Redis的Session共享方案。将Session存储于Redis上,然后将整个系统的全局Cookie Domain设置于顶级域名上,这样SessionID就能在各个子系统间共享。分布式Session共享解决方案,这篇推荐大家看下。这个方案存在着严重的扩展性问题,首先,ASP.NET的Session存储必须为SessionStateItemCollection对象,而存储的结构是经过序列化后经过加密存储的。并且当用户访问应用时,他首先做的就是将存储容器里的所有内容全部取出,并且反序列化为SessionStateItemCollection对象。这就决定了他具有以下约束:1.Session中所涉及的类型必须是子系统中共同拥有的(即程序集、类型都需要一致),这导致Session的使用受到诸多限制;2.跨顶级域名的情况完全无法处理; 二、基于OpenId的单点登录 这种单点登录将用户的身份标识信息简化为OpenId存放于客户端,当用户登录某个子系统时,将OpenId传送到服务端,服务端根据OpenId构造用户验证信息,多用于C/S与B/S相结合的系统,流程如下: 由上图可以看到,这套单点登录依赖于OpenId的传递,其验证的基础在于OpenId的存储以及发送。1.当用户第一次登录时,将用户名密码发送给验证服务;2.验证服务将用户标识OpenId返回到客户端;3.客户端进行存储;4.访问子系统时,将OpenId发送到子系统;5.子系统将OpenId转发到验证服务;6.验证服务将用户认证信息返回给子系统;7.子系统构建用户验证信息后将授权后的内容返回给客户端。这套单点登录验证机制的主要问题在于他基于C/S架构下将用户的OpenId存储于客户端,在子系统之间发送OpenId,而B/S模式下要做到这一点就显得较为困难。为了处理这个问题我们将引出下一种方式,这种方式将解决B/S模式下的OpenId的存储、传递问题。 三、基于Cookie的OpenId存储方案 我们知道,Cookie的作用在于充当一个信息载体在Server端和Browser端进行信息传递,而Cookie一般是以域名为分割的,例如a.xxx.com与b.xxx.com的Cookie是不能互相访问的,但是子域名是可以访问上级域名的Cookie的。即a.xxx.com和b.xxx.com是可以访问xxx.com下的Cookie的,于是就能将顶级域名的Cookie作为OpenId的载体。 验证步骤和上第二个方法非常相似:1、 在提供验证服务的站点里登录;2、 将OpenId写入顶级域名Cookie里;3、 访问子系统(Cookie里带有OpenId)4、 子系统取出OpenId通过并向验证服务发送OpenId5、 返回用户认证信息6、 返回授权后的内容在以上两种方法中我们都可以看到通过OpenId解耦了Session共享方案中的类型等问题,并且构造用户验证信息将更灵活,子系统间的验证是相互独立的,但是在第三种方案里,我们基于所有子系统都是同一个顶级域名的假设,而在实际生产环境里有多个域名是很正常的事情,那么就不得不考虑跨域问题究竟如何解决。 四、B/S多域名环境下的单点登录处理 在多个顶级域名的情况下,我们将无法让各个子系统的OpenId共享。处理B/S环境下的跨域问题,我们首先就应该想到JSONP的方案。 验证步骤如下:1、 用户通过登录子系统进行用户登录;2、 用户登录子系统记录了用户的登录状态、OpenId等信息;3、 用户使用业务子系统;4、 若用户未登录业务子系统则将用户跳转至用户登录子系统;5、 用户子系统通过JSONP接口将用户OpenId传给业务子系统;6、 业务子系统通过OpenId调用验证服务;7、 验证服务返回认证信息、业务子系统构造用户登录凭证;(此时用户客户端已经与子业务系统的验证信息已经一一对应)8、 将用户登录结果返回用户登录子系统,若成功登录则将用户跳转回业务子系统;9、 将授权后的内容返回客户端; 五、安全问题 经过以上步骤,跨域情况下的单点登录问题已经可以得到解决。而在整个开发过程初期,我们采用用户表中纪录一个OpenId字段来保存用户OpenId,而这个机制下很明显存在一些安全性、扩展性问题。这个扩展性问题主要体现在一个方面:OpenId的安全性和用户体验的矛盾。 整个单点登录的机制决定了OpenId是会出现在客户端的,所以OpenId需要有过期机制,假如用户在一个终端登录的话可以选择在用户每次登录或者每次退出时刷新OpenId,而在多终端登录的情况下就会出现矛盾:当一个终端刷新了OpenId之后其他终端将无法正常授权。欢迎大家关注我的公种浩【程序员追风】,文章都会在里面更新,整理的资料也会放在里面。 而最终,我采用了单用户多OpenId的解决方案。每次用户通过用户名/密码登录时,产生一个OpenId保存在Redis里,并且设定过期时间,这样多个终端登录就会有多个OpenId与之对应,不再会存在一个OpenId失效所有终端验证都失效的情况。 最后 欢迎大家一起交流,喜欢文章记得点个赞哟,感谢支持!

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。