单点登录的三种实现方式

前言

在 B/S 系统中,登录功能通常都是基于 Cookie 来实现的。当用户登录成功后,一般会将登录状态记录到 Session 中,或者是给用户签发一个 Token,无论哪一种方式,都需要在客户端保存一些信息(Session ID 或 Token ),并要求客户端在之后的每次请求中携带它们。在这样的场景下,使用 Cookie 无疑是最方便的,因此我们一般都会将 Session 的 ID 或 Token 保存到 Cookie 中,当服务端收到请求后,通过验证 Cookie 中的信息来判断用户是否登录 。

单点登录(Single Sign On, SSO)是指在同一帐号平台下的多个应用系统中,用户只需登录一次,即可访问所有相互信任的应用系统。举例来说,百度贴吧和百度地图是百度公司旗下的两个不同的应用系统,如果用户在百度贴吧登录过之后,当他访问百度地图时无需再次登录,那么就说明百度贴吧和百度地图之间实现了单点登录。

单点登录的本质就是在多个应用系统中共享登录状态。如果用户的登录状态是记录在 Session 中的,要实现共享登录状态,就要先共享 Session,比如可以将 Session 序列化到 Redis 中,让多个应用系统共享同一个 Redis,直接读取 Redis 来获取 Session。当然仅此是不够的,因为不同的应用系统有着不同的域名,尽管 Session 共享了,但是由于 Session ID 是往往保存在浏览器 Cookie 中的,因此存在作用域的限制,无法跨域名传递,也就是说当用户在 app1.com 中登录后,Session ID 仅在浏览器访问 app1.com 时才会自动在请求头中携带,而当浏览器访问 app2.com 时,Session ID 是不会被带过去的。实现单点登录的关键在于,如何让 Session ID(或 Token)在多个域中共享。

实现方式一:父域 Cookie

在将具体实现之前,我们先来聊一聊 Cookie 的作用域。

Cookie 的作用域由 domain 属性和 path 属性共同决定。domain 属性的有效值为当前域或其父域的域名/IP地址,在 Tomcat 中,domain 属性默认为当前域的域名/IP地址。path 属性的有效值是以“/”开头的路径,在 Tomcat 中,path 属性默认为当前 Web 应用的上下文路径。

如果将 Cookie 的 domain 属性设置为当前域的父域,那么就认为它是父域 Cookie。Cookie 有一个特点,即父域中的 Cookie 被子域所共享,换言之,子域会自动继承父域中的Cookie。

利用 Cookie 的这个特点,不难想到,将 Session ID(或 Token)保存到父域中不就行了。没错,我们只需要将 Cookie 的 domain 属性设置为父域的域名(主域名),同时将 Cookie 的 path 属性设置为根路径,这样所有的子域应用就都可以访问到这个 Cookie 了。不过这要求应用系统的域名需建立在一个共同的主域名之下,如 tieba.baidu.com 和 map.baidu.com,它们都建立在 baidu.com 这个主域名之下,那么它们就可以通过这种方式来实现单点登录。

总结:此种实现方式比较简单,但不支持跨主域名。

实现方式二:认证中心

我们可以部署一个认证中心,认证中心就是一个专门负责处理登录请求的独立的 Web 服务。

用户统一在认证中心进行登录,登录成功后,认证中心记录用户的登录状态,并将 Token 写入 Cookie。(注意这个 Cookie 是认证中心的,应用系统是访问不到的。)

应用系统检查当前请求有没有 Token,如果没有,说明用户在当前系统中尚未登录,那么就将页面跳转至认证中心。由于这个操作会将认证中心的 Cookie 自动带过去,因此,认证中心能够根据 Cookie 知道用户是否已经登录过了。如果认证中心发现用户尚未登录,则返回登录页面,等待用户登录,如果发现用户已经登录过了,就不会让用户再次登录了,而是会跳转回目标 URL ,并在跳转前生成一个 Token,拼接在目标 URL 的后面,回传给目标应用系统。

应用系统拿到 Token 之后,还需要向认证中心确认下 Token 的合法性,防止用户伪造。确认无误后,应用系统记录用户的登录状态,并将 Token 写入 Cookie,然后给本次访问放行。(注意这个 Cookie 是当前应用系统的,其他应用系统是访问不到的。)当用户再次访问当前应用系统时,就会自动带上这个 Token,应用系统验证 Token 发现用户已登录,于是就不会有认证中心什么事了。

这里顺便介绍两款认证中心的开源实现:

  • Apereo CAS 是一个企业级单点登录系统,其中 CAS 的意思是”Central Authentication Service“。它最初是耶鲁大学实验室的项目,后来转让给了 JASIG 组织,项目更名为 JASIG CAS,后来该组织并入了Apereo 基金会,项目也随之更名为 Apereo CAS。
  • XXL-SSO 是一个简易的单点登录系统,由大众点评工程师许雪里个人开发,代码比较简单,没有做安全控制,因而不推荐直接应用在项目中,这里列出来仅供参考。

总结:此种实现方式相对复杂,支持跨域,扩展性好,是单点登录的标准做法。

实现方式三:LocalStorage 跨域

前面,我们说实现单点登录的关键在于,如何让 Session ID(或 Token)在多个域中共享。

父域 Cookie 确实是一种不错的解决方案,但是不支持跨域。那么有没有什么奇淫技巧能够让 Cookie 跨域传递呢?

很遗憾,浏览器对 Cookie 的跨域限制越来越严格。Chrome 浏览器还给 Cookie 新增了一个 SameSite 属性,此举几乎禁止了一切跨域请求的 Cookie 传递(超链接除外),并且只有当使用 HTTPs 协议时,才有可能被允许在 AJAX 跨域请求中接受服务器传来的 Cookie。

不过,在前后端分离的情况下,完全可以不使用 Cookie,我们可以选择将 Session ID (或 Token )保存到浏览器的 LocalStorage 中,让前端在每次向后端发送请求时,主动将 LocalStorage 的数据传递给服务端。这些都是由前端来控制的,后端需要做的仅仅是在用户登录成功后,将 Session ID (或 Token )放在响应体中传递给前端。

在这样的场景下,单点登录完全可以在前端实现。前端拿到 Session ID (或 Token )后,除了将它写入自己的 LocalStorage 中之外,还可以通过特殊手段将它写入多个其他域下的 LocalStorage 中。

关键代码如下:

// 获取 token
var token = result.data.token;

// 动态创建一个不可见的iframe,在iframe中加载一个跨域HTML
var iframe = document.createElement("iframe");
iframe.src = "http://app1.com/localstorage.html";
document.body.append(iframe);
// 使用postMessage()方法将token传递给iframe
setTimeout(function () {
 iframe.contentWindow.postMessage(token, "http://app1.com");
}, 4000);
setTimeout(function () {
 iframe.remove();
}, 6000);

// 在这个iframe所加载的HTML中绑定一个事件监听器,当事件被触发时,把接收到的token数据写入localStorage
window.addEventListener('message', function (event) {
 localStorage.setItem('token', event.data)
}, false);

前端通过 iframe+postMessage() 方式,将同一份 Token 写入到了多个域下的 LocalStorage 中,前端每次在向后端发送请求之前,都会主动从 LocalStorage 中读取 Token 并在请求中携带,这样就实现了同一份 Token 被多个域所共享。

总结:此种实现方式完全由前端控制,几乎不需要后端参与,同样支持跨域。

补充:域名分级

从专业的角度来说(根据《计算机网络》中的定义),.com、.cn 为一级域名(也称顶级域名),.com.cn、baidu.com 为二级域名,sina.com.cn、tieba.baidu.com 为三级域名,以此类推,N 级域名就是 N-1 级域名的直接子域名。

从使用者的角度来说,一般把可支持独立备案的主域名称作一级域名,如 baidu.com、sina.com.cn 皆可称作一级域名,在主域名下建立的直接子域名称作二级域名,如 tieba.baidu.com 为二级域名。

为了避免歧义,本人将使用“主域名“替代”一级域名“的说法。

以上就是单点登录的三种实现方式的详细内容,更多关于单点登录实现的资料请关注我们其它相关文章!

(0)

相关推荐

  • JavaScript实现单点登录的示例

    项目中遇到单点登录这个问题,当点击链接的时候跳转到另一个系统中并实现自动登录进去,直接进去系统的页面 因为不同的系统涉及到跨域的问题,所以使用nginx来解决跨域的问题 先跳转到另一个系统的一个页面,在这个页面里实现登录操作再跳转到系统中我们需要的页面 还有一个问题就是登录的时候需要用户名和密码,用户名和密码不是固定的,需要动态获取,所以跳转到过渡页面的时候需要携带参数 携带参数是通过url传递的,这里用户名和密码使用了简单的base64加密 过渡页面接受参数 var params = wind

  • SpringBoot整合SSO(single sign on)单点登录

    1.单点登录三种常见的方式 (1)Session广播机制(Session复制) (2)使用Cookie+Redis实现 (3)使用token实现 2.单点登录介绍 举例: (1)引入jwt依赖 <!-- JWT--> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> </dependency> (2)创建JWTUtil

  • 使用springboot结合vue实现sso单点登录

    本文实例为大家分享了springboot vue实现sso单点登录的具体代码,供大家参考,具体内容如下 项目结构: 开发工具:idea, maven3 静态文件下载地址 1.pom文件: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.or

  • springboot集成CAS实现单点登录的示例代码

    最近新参与的项目用到了cas单点登录,我还不会,这怎么能容忍!空了学习并搭建了一个spring-boot 集成CAS 的demo.实现了单点登录与登出. 单点登录英文全称是:Single Sign On,简称SSO. 含义:在多个相互信任的系统中,只要登录一个系统其他系统均可访问. CAS 是一种使用广泛的单点登录实现,分为客户端CAS Client和服务端 CAS Service,客户端就是我们的系统,服务端是认证中心,由CAS提供,我们需要稍作修改,启动起来就可以用.~~~~ 效果演示 ht

  • 详解使用JWT实现单点登录(完全跨域方案)

    首先介绍一下什么是JSON Web Token(JWT)? 官方文档是这样解释的:JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且独立的方式,可以在各方之间作为JSON对象安全地传输信息.此信息可以通过数字签名进行验证和信任.JWT可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名. 虽然JWT可以加密以在各方之间提供保密,但只将专注于签名令牌.签名令牌可以验证其中包含的声明的完整性,而加密令牌则隐藏其他方的声明.当使用公钥

  • 基于JWT实现SSO单点登录流程图解

    一.基于JWT实现SSO单点登录原理 1.什么是单点登录 所谓单点登录就是有多个应用部署在不同的服务器上,只需登录一次就可以互相访问不同服务器上的资源. 2.单点登录流程 当一个访问请求发给应用A,如果这个请求需要登录以后才能访问,那么应用A就会向认证服务器请求授权,这时候就把用户引导到认证服务器上.用户在认证服务器上完成认证并授权.认证授权完成后,认证服务器返回给应用A一个授权码,应用A携带授权码到认证服务器请求令牌,认证服务器返回应用A一个JWT,应用A解析JWT里面的信息,完成登录.这是一

  • 基于springboot和redis实现单点登录

    本文实例为大家分享了基于springboot和redis实现单点登录的具体代码,供大家参考,具体内容如下 1.具体的加密和解密方法 package com.example.demo.util; import com.google.common.base.Strings; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; import javax.crypto.Cipher; import javax.crypto.KeyG

  • 基于SpringBoot+Redis的Session共享与单点登录详解

    前言 使用Redis来实现Session共享,其实网上已经有很多例子了,这是确保在集群部署中最典型的redis使用场景.在SpringBoot项目中,其实可以一行运行代码都不用写,只需要简单添加添加依赖和一行注解就可以实现(当然配置信息还是需要的). 然后简单地把该项目部署到不同的tomcat下,比如不同的端口(A.B),但项目访问路径是相同的.此时在A中使用set方法,然后在B中使用get方法,就可以发现B中可以获取A中设置的内容. 但如果就把这样的一个项目在多个tomcat中的部署说实现了单

  • 单点登录的三种实现方式

    前言 在 B/S 系统中,登录功能通常都是基于 Cookie 来实现的.当用户登录成功后,一般会将登录状态记录到 Session 中,或者是给用户签发一个 Token,无论哪一种方式,都需要在客户端保存一些信息(Session ID 或 Token ),并要求客户端在之后的每次请求中携带它们.在这样的场景下,使用 Cookie 无疑是最方便的,因此我们一般都会将 Session 的 ID 或 Token 保存到 Cookie 中,当服务端收到请求后,通过验证 Cookie 中的信息来判断用户是否

  • python中requests模拟登录的三种方式(携带cookie/session进行请求网站)

    一,cookie和session的区别 cookie在客户的浏览器上,session存在服务器上 cookie是不安全的,且有失效时间 session是在cookie的基础上,服务端设置session时会向浏览器发送设置一个设置cookie的请求,这个cookie包括session的id当访问服务端时带上这个session_id就可以获取到用户保存在服务端对应的session 二,爬虫处理cookie和session 带上cookie和session的好处: 能够请求到登录后的界面 带上cook

  • 详细解读分布式锁原理及三种实现方式

    目前几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题.分布式的CAP理论告诉我们"任何一个分布式系统都无法同时满足一致性(Consistency).可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项."所以,很多系统在设计之初就要对这三者做出取舍.在互联网领域的绝大多数的场景中,都需要牺牲强一致性来换取系统的高可用性,系统往往只需要保证"最终一致性",只要这个最终

  • JS 验证码功能的三种实现方式

    摘要: 我们在做一些后台系统的登录验证的时候.难免会用到验证码功能,来辅助进行验证,提高安全性,在我们日常生活中,验证方式无处不在,最多的是短信验证码的方式,通过点击发送验证码,然后手机接收短信,填写验证码才能登陆成功,但是也有一些其他验证码功能也在使用,所以,我就想着来集中记录一下,希望对大家有所帮助... 一.数字短信验证码 思路: a.两个文本框+一个获取验证码按钮,文本框用来输入手机号和获取到的验证码,按钮负责点击和记录倒计时: b.js书写定时器setTimeout,进行60秒验证码失

  • Git工作流演示及三种工作方式

    目录 集中式工作流(不常用) Forking 工作流(偶尔使用) GitFlow 工作流(最流行) Git工作流演示 集中式工作流(不常用) 集中式工作流像SVN一样,以中央仓库作为项目所有修改的单点实体.所有修改都提交到 Master分支上.这种方式与 SVN 的主要区别就是开发人员有本地库,但是Git 很多特性并没有用到. Forking 工作流(偶尔使用) Forking 工作流是在 GitFlow 基础上,充分利用了 Git 的 Fork 和 pull request 的功能以达到代码审

  • Vue网络请求的三种实现方式介绍

    目录 1.XMLHttpRequest发送请求 2.fetch发送请求 3.axios请求库(Vue中推荐写法) 模拟发送get和post请求 网络请求时发送用户认证信息 请求拦截器 响应拦截器 用户管理 在进行 Vue 的网络请求之前,我们先写一些假数据: users.json: [ { "id": 1, "name": "张三" }, { "id": 2, "name": "李四"

  • oracle中if/else的三种实现方式详解

    1.标准sql规范 1.单个IF IF v=... THEN END IF; 2.IF ... ELSE IF v=... THEN ELSE t....; END IF; 3.多个IF IF v=... THEN ELSIF v=... THEN t...; END IFL 注意: 多个IF的是'ELSIF' 不是 ' ELSE IF' 2.decode函数 DECODE(VALUE,IF1,THEN1,IF2,THEN2,IF2,THEN2,..,ELSE) 表示如果value等于if1时,

  • 浅谈js中的三种继承方式及其优缺点

    第一种,prototype的方式: //父类 function person(){ this.hair = 'black'; this.eye = 'black'; this.skin = 'yellow'; this.view = function(){ return this.hair + ',' + this.eye + ',' + this.skin; } } //子类 function man(){ this.feature = ['beard','strong']; } man.pr

  • IIS下PHP的三种配置方式对比

    在Windows IIS 6.0下配置PHP,通常有CGI.ISAPI和FastCGI三种配置方式,这三种模式都可以在IIS 6.0下成功运行,下面我就讲一下这三种方式配置的区别和性能上的差异.   1.CGI(通用网关接口/Common Gateway Interface)一般是可执行程序,例如EXE文件,和WEB服务器各自占据着不同的进程,而且一般一个CGI程序只能处理一个用户请求.这样,当用户请求数量非常多时,会大量占用系统的资源,如内存.CPU时间等,造成效能低下.   2.ISAPI(

  • Python selenium 三种等待方式详解(必会)

    很多人在群里问,这个下拉框定位不到.那个弹出框定位不到-各种定位不到,其实大多数情况下就是两种问题:1 有frame,2 没有加等待.殊不知,你的代码运行速度是什么量级的,而浏览器加载渲染速度又是什么量级的,就好比闪电侠和凹凸曼约好去打怪兽,然后闪电侠打完回来之后问凹凸曼你为啥还在穿鞋没出门?凹凸曼分分中内心一万只羊驼飞过,欺负哥速度慢,哥不跟你玩了,抛个异常撂挑子了. 那么怎么才能照顾到凹凸曼缓慢的加载速度呢?只有一个办法,那就是等喽.说到等,又有三种等法,且听博主一一道来: 1. 强制等待

随机推荐