用上帝视角俯瞰单点登录的前世与今生(内含两种实现方式的源码)

本文主要是介绍用上帝视角俯瞰单点登录的前世与今生(内含两种实现方式的源码),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

 

1.什么是单点登录?

2.单点登录的前世与今生——前世篇(SSO)

2.1.单点登录的定义是:

2.2.总结成简单一句话说就是:

2.3.单点登录的优点:

2.4.拿去游乐园买票来讲一下单点登录的简单实现机制:

2.5.单点登录的实现机制:

2.6.单点登录的分类:

2.7.同域SSO:

2.7.1.同域SSO概念:

2.7.2.同父域SSO概念:

2.7.3.同域SSO实现流程图:

2.7.4.代码实现同域SSO:

3.单点登录的前世与今生——今生篇(CAS)

3.1.同域SSO

3.1.1.CAS

3.1.2.CAS包含两个部分:

3.1.3.在说跨域SSO流程之前要提前说几个CAS知识点:

3.1.4.理解几种票根:

3.1.5.跨域SSO实现流程图:

3.1.6.代码实现跨域SSO(基于CAS-Client):


1.什么是单点登录?

        大家都知道百度不仅仅只有搜索引擎这一个业务,它还有百度贴吧,百度云盘等众所周知的业务。而且有没有发现?你只需要在百度任意一个业务中登陆过,其他业务都默认自动登录了你这个账号呢?比如你在百度搜索中登录了你的账号,你再进入百度贴吧或者百度网盘,显示你已经登录了。

百度首页登录:

在登录这个“进阶的小名”账号的情况下,分别打开百度网盘和百度地图的效果:

       百度网盘和百度地图都默认登录了“进阶的小名”这个账号了。相对的,如果你在任何一个业务中退出登录,其他的业务也会自动退出当前账号的登录。

2.单点登录的前世与今生——前世篇(SSO)

2.1.单点登录的定义是:

       单点登录(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自独立的软件系统,提供访问控制的属性。当拥有这项属性时,当用户登录时,就可以获取所有系统的访问权限,不用对每个单一系统都逐一登录。这项功能通常是以轻型目录访问协议(LDAP)来实现,在服务器上会将用户信息存储到LDAP数据库中。相同的,单一退出(single sign-off)就是指,只需要单一的退出动作,就可以结束对于多个系统的访问权限。

2.2.总结成简单一句话说就是:

在多个应用系统中,用户只需要登录一次就可以访问相互信任的应用系统。

2.3.单点登录的优点:

       不管是百度还是我们自己公司的项目,随着时间的推移,我们都需要根据市场需求或是客户新的需求退出新的功能,不可能在每一个新加的模块中都写一遍登录的业务,于是单点登录解决了这个问题,将登录模块从其他业务模块中抽离出来。

2.4.拿去游乐园买票来讲一下单点登录的简单实现机制:

       我们95后的天津孩子应该都去过一个叫“乐园”的游乐场,这个游乐场里面有:“碰碰车”、“旋转木马”、“丛林鼠”、“摩天轮”等游乐设施,这么多的游乐设施,如果家长想带着熊孩子挨个儿玩一遍是不太可能了,为啥啊?还不是因为排队买票浪费时间吗!人多的时候,玩儿俩设施就家走了😂。排一个小时“碰碰车”的队,孩子进去玩10分钟出来,孩子是美了,一看,家大人又去旁边“激流勇进”排队去了,针对这种熊孩子,“乐园”就推出了通票这一制度。通票就是:“乐园”大门口买一张通票(阿姨给你手腕戴上一个印着通票盖着戳的这么一个纸圈),进到里面家大人就不用单独给熊孩子排队挨个设施买票了,家大人找个树荫下一呆,看着熊孩子带着那个纸圈满园子跑就完了😂。

2.5.单点登录的实现机制:

如下图所示,当用户第一次访问系统1的时候,因为还没有登录,会被引导到认证系统中进行登录。

  1. 根据用户提供的登录信息,认证系统进行身份效验,如果通过效验,应该返回给用户一个认证的凭据Ticket;
  2. 用户再访问其他相互信任的应用的时候,就会把这个Ticket作为自己认证的凭据,应用系统接受到请求之后会把 Ticket送到认证系统进行效验,检查 Ticket的合法性;
  3. 如果通过效验,用户就可以在不用再次登录的情况下访问系统2和系统3了。

2.6.单点登录的分类:

我将会把单点登录分为“同域SSO”和“跨域SSO”两部分说:(“跨域SSO” 会在后文 单点登录的前世与今生——今生篇(CAS)中提到)

2.7.同域SSO:

同域SSO又分为 : “同域SSO” 和 “同父域SSO”

2.7.1.同域SSO概念:

没有设置独立的 SSO 服务器,因为业务后台服务器本身就足以承担 SSO 的职能。

2.7.2.同父域SSO概念:

和同域SSO不同在于,服务器在返回 cookie 的时候,要把cookie 的 domain 设置为其父域。

2.7.3.同域SSO实现流程图:

 

同域SSO:

  1. 用户点击http://www.xiaoming.com页面登录按钮,向后台服务器发送登录请求;
  2. 输入正确的用户名密码,登录认证成功,服务器会把登录信息写入session;
  3. 服务器为该用户生成一个cookie,并加入到response header中,随着请求返回而写入浏览器中;
  4. 用户再次访问同域的http://cart.xiaoming.com的时候,浏览器会带上之前的cookie;
  5. 后台服务器通过该cookie验证当前账户的登陆状态了。

同父域SSO:

  1. 同父域 SSO 是同域 SSO 的简单升级,唯一的不同在于,服务器在返回 cookie 的时候,要把cookie 的 domain 设置为其父域。
  2. 比如两个产品的地址分别为 http://www.xiaoming.com 和 http://cart.xiaoming.com,那么 cookie 的域设置为 xiaoming.com 即可。在访问两个资源的时候,这个 cookie 都能发送到服务器,本质上和同域 SSO 没有区别。

2.7.4.代码实现同域SSO:

       接下来我通过一个Demo来演示同域SSO的要整过程。这个Demo模拟的是利用cookie实现的同域单点登录:(因为我的前后端打包部署在一个Server里,而且浏览器用同一个Origin请求前端和服务器端,即使端口号不同,也不存在跨域问题。)

      先上效果:

 

我在Hosts文件里面进行了如下配置(模拟):

127.0.0.1    www.xiaoming.com

127.0.0.1    vip.xiaoming.com

127.0.0.1    cart.xiaoming.com

127.0.0.1    login.xiaoming.com

本文只展示部分重要代码(点击获取同域SSO源码(导入项目时注意:本项目是Gradle构建的多模块项目))

登录模块后端处理登录业务代码:

@Controller
@RequestMapping("/login")
public class LoginController {//    模拟数据库用户数据public static Set<User> dbUser;static {dbUser = new HashSet<>();dbUser.add(new User(0, "zhangsan", "1"));dbUser.add(new User(1, "lisi", "2"));dbUser.add(new User(2, "wangwu", "3"));}//处理“/login”下的post请求@PostMappingpublic String doLogin(User user, HttpSession session, HttpServletResponse response) {System.out.println(dbUser);String target = (String) session.getAttribute("target");System.out.println(target);/*** 此处为lambda表达式(源码中又完整解释)*/Optional<User> first = dbUser.stream().filter(dbUser -> dbUser.getUsername().equals(user.getUsername()) &&dbUser.getPassword().equals(user.getPassword())).findFirst();//判断用户是否登录if (first.isPresent()) {//保存用户登录信息String token = UUID.randomUUID().toString();//随机生成一个token,UUID(全局唯一标识符)Cookie cookie = new Cookie("TOKEN", token);//cookie要在子系统之间互相访问,要在同一个域下cookie.setDomain("xiaoming.com");response.addCookie(cookie);//模拟缓存,把User和随机生成的cookie存(put)到“loginUser”方法中的mapLoginCacheUtil.loginUser.put(token, first.get());} else {//登陆失败session.setAttribute("msg", "用户名或密码错误");return "login";}//重定向到target地址return "redirect:" + target;}/*** 给其他子系统开放一个接口,通过token获取登录用户的信息* @param token* @return*/@GetMapping("/info")@ResponseBody//ResponseEntity <T>  ,泛型T 表示要设置的返回的 响应体public ResponseEntity<User> getUserInfo(String token) {if (!StringUtils.isEmpty(token)) {User user = LoginCacheUtil.loginUser.get(token);return ResponseEntity.ok(user);} else {//错误请求(我要token,你却没给我)return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);}}
}

 登录模块后端跳转页面控制器:

/*** 页面跳转逻辑*/
@Controller
@RequestMapping("/view")
public class ViewController {/*** 跳转到登录页面* @return* @RequestParam中 value表示参数名字  required表示是否为必需,defaultValue表示默认值* @CookieValue中 有cookie就获取“TOKEN”没有就不获取,cookie不是必须的*/@GetMapping("/login")public String tologin(@RequestParam(required = false, defaultValue = "") String target,HttpSession session,@CookieValue(required = false, value = "TOKEN") Cookie cookie) {/*** 判断target是否为空* 为空:主页面* 不为空:存到session*/if (StringUtils.isEmpty(target)) {target = "http://www.xiaoming.com:9010";}if (cookie != null) {/*** 如果是登录的用户再次访问登陆系统时,就重定向主页面(target)*/String value = cookie.getValue();//获取模拟缓存类LoginCacheUtil中拿出对应token的用户信息User user = LoginCacheUtil.loginUser.get(value);if (user != null) {return "redirect:" + target;}}
//        else {//重定向地址session.setAttribute("target", target);return "login";
//        }}
}

 首页后端跳转页面控制器:

@Controller
@RequestMapping("/view")
public class ViewController {@Autowiredprivate RestTemplate restTemplate;private final String LOGIN_INFO_ADDRESS = "http://login.xiaoming.com:9000/login/info?token=";@GetMapping("/index")public String toIndex(@CookieValue(required = false, value = "TOKEN") Cookie cookie, HttpSession session) {if (cookie != null) {String token = cookie.getValue();if (!StringUtils.isEmpty(token)) {Map result = restTemplate.getForObject(LOGIN_INFO_ADDRESS + token, Map.class);System.out.println("hahha" + result);session.setAttribute("loginUser", result);}}return "/index";}
}

文章篇幅有限,只展示部分代码,可以 点击获取同域SSO源码 clone到本地自行测试。

3.单点登录的前世与今生——今生篇(CAS)

3.1.跨域SSO

3.1.1.CAS概念:

集中式认证服务(英语:Central Authentication Service,缩写CAS)是一种针对万维网的单点登录协议。它的目的是允许一个用户访问多个应用程序,而只需向认证服务器提供一次凭证(如用户名和密码)。这样用户不仅不需在登陆web应用程序时重复认证,而且这些应用程序也无法获得密码等敏感信息。CAS 是 Yale 大学发起的一个企业级的、开源的项目,旨在为 Web 应用系统提供一种可靠的单点登录解决方法。

3.1.2.CAS包含两个部分:

CAS Server 和 CAS Client

  1. CAS Server:负责完成对用户的认证工作 , 需要独立部署。
  2. CAS Client:负责处理对客户端受保护资源的访问请求,需要对请求方进行身份认证时,重定向到 CAS Server 进行认证。

3.1.3.在说跨域SSO流程之前要提前说几个CAS知识点:

Cas-Client接口方面:

/login:登录接口,用于登录到中心服务器。

/logout:登出接口,用于从中心服务器登出。

3.1.4.理解几种票根:

1.TGT (Ticket Grangting Ticket) :

    TGT 是 CAS 为用户签发的登录票据,拥有了 TGT,用户就可以证明自己在 CAS 成功登录过。TGT 封装了 Cookie 值以及此 Cookie 值对应的用户信息。

2.TGC(Ticket Granting Cookie) :

    CAS Server 生成TGT放入自己的 Session 中,而 TGC 就是这个 Session 的唯一标识(SessionId),以 Cookie 形式放到浏览器端。

3.ST(Service Ticket) :

    ST 是 CAS 为用户签发的访问某一 service 的票据。用户访问 service 时,service 发现用户没有 ST,则要求用户去 CAS 获取 ST。

票据关系:用户信息签发TGT,TGT签发ST,PGT签发PT。

3.1.5.跨域SSO实现流程图:

 

  1. 用户访问产品 CasClientOne,域名是 http://localhost:8081
  2. 由于用户没有携带在CasClientOne上登录的 cookie,所以 CasClientOne重定向到SSO 服务器的地址。
  3. 由于用户没有携带在 SSO 服务器上登录的 TGC,所以 SSO 服务器判断用户未登录,给用户显示统一登录界面。
  4. 登录成功后,SSO 服务器构建用户在 SSO 登录的 TGT,同时返回一个 http 重定向(包含 SSO 服务器派发的 ST )。
  5. 重定向的 http response 中包含写 cookie。这个 cookie 代表用户在 SSO 中的登录状态,它的值是 TGC。
  6. 浏览器重定向到CasClientOne。此时重定向的 url 中携带着 SSO 服务器生成的 ST。根据 ST,CasClientOne向 SSO 服务器发送请求,SSO 服务器验证票据的有效性。验证成功后,CasClientOne知道用户已经在 SSO服务器登录了,于是CasClientOne构建用户登录 session。
  7. 用户访问产品CasClientTWO,域名是  http://localhost:8082
  8. 由于用户没有携带在CasClientTWO上登录的cookie,所以 CasClientTWO重定向到SSO 服务器,去询问用户在 SSO 中的登录状态。
  9. 浏览器重定向到 SSO服务器。由于已经向浏览器写入了携带 TGC 的cookie,所以此时 SSO 服务器可以拿到,根据 TGC 去查找 TGT,如果找到,就判断用户已经在SSO服务器登录过了。
  10. SSO 服务器返回一个重定向,重定向携带 ST。
  11. 浏览器带 ST 重定向到CasClientTWO。
  12. CasClientTWO根据票据向 SSO 服务器发送请求,票据验证通过后,CasClientTWO知道用户已经在SSO服务器登录了,于是生成 session,向浏览器写入CasClientTWO的 cookie。

3.1.6.代码实现跨域SSO(基于CAS-Client):

先上效果:

本文只展示部分重要代码(点击获取CAS-Demo源码(导入项目时注意:本项目是Maven构建的多模块项目))

客户端One的部分实现:

public class CasClientOne {@RequestMapping("/CasClientOne")public String CasClientOne(HttpSession session, HttpServletRequest request){Principal userPrincipal = request.getUserPrincipal();String name = userPrincipal.getName();session.setAttribute("msg",name+"登录了CasClientOne。。。");return "CasClientOne";}}

代码使用Springboot集成Cas5.3实现,点击获取配置好的Cas-Server,篇幅有限,本文中只展示了一个客户端的实现,可以 点击获取CAS-Demo源码 Clone到本地自行测试。

       关于Cas,后期还会更新一些我在公司项目中使用的(SpringBoot CAS-Client)实际遇到的一些问题的解决方案。感兴趣的小伙伴可以点点关注。觉得本文对您理解单点登录有一些帮助的小伙伴,麻烦给这篇文章点点赞。如果大佬发现本文理解存在问题,希望可以在下方评论区指点我改正。最后谢谢大家的阅读!小名祝大家每天都开开心心哒~😁

这篇关于用上帝视角俯瞰单点登录的前世与今生(内含两种实现方式的源码)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/871119

相关文章

pandas中位数填充空值的实现示例

《pandas中位数填充空值的实现示例》中位数填充是一种简单而有效的方法,用于填充数据集中缺失的值,本文就来介绍一下pandas中位数填充空值的实现,具有一定的参考价值,感兴趣的可以了解一下... 目录什么是中位数填充?为什么选择中位数填充?示例数据结果分析完整代码总结在数据分析和机器学习过程中,处理缺失数

Golang HashMap实现原理解析

《GolangHashMap实现原理解析》HashMap是一种基于哈希表实现的键值对存储结构,它通过哈希函数将键映射到数组的索引位置,支持高效的插入、查找和删除操作,:本文主要介绍GolangH... 目录HashMap是一种基于哈希表实现的键值对存储结构,它通过哈希函数将键映射到数组的索引位置,支持

Pandas使用AdaBoost进行分类的实现

《Pandas使用AdaBoost进行分类的实现》Pandas和AdaBoost分类算法,可以高效地进行数据预处理和分类任务,本文主要介绍了Pandas使用AdaBoost进行分类的实现,具有一定的参... 目录什么是 AdaBoost?使用 AdaBoost 的步骤安装必要的库步骤一:数据准备步骤二:模型

使用Pandas进行均值填充的实现

《使用Pandas进行均值填充的实现》缺失数据(NaN值)是一个常见的问题,我们可以通过多种方法来处理缺失数据,其中一种常用的方法是均值填充,本文主要介绍了使用Pandas进行均值填充的实现,感兴趣的... 目录什么是均值填充?为什么选择均值填充?均值填充的步骤实际代码示例总结在数据分析和处理过程中,缺失数

Java对象转换的实现方式汇总

《Java对象转换的实现方式汇总》:本文主要介绍Java对象转换的多种实现方式,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录Java对象转换的多种实现方式1. 手动映射(Manual Mapping)2. Builder模式3. 工具类辅助映

Go语言开发实现查询IP信息的MCP服务器

《Go语言开发实现查询IP信息的MCP服务器》随着MCP的快速普及和广泛应用,MCP服务器也层出不穷,本文将详细介绍如何在Go语言中使用go-mcp库来开发一个查询IP信息的MCP... 目录前言mcp-ip-geo 服务器目录结构说明查询 IP 信息功能实现工具实现工具管理查询单个 IP 信息工具的实现服

SpringBoot基于配置实现短信服务策略的动态切换

《SpringBoot基于配置实现短信服务策略的动态切换》这篇文章主要为大家详细介绍了SpringBoot在接入多个短信服务商(如阿里云、腾讯云、华为云)后,如何根据配置或环境切换使用不同的服务商,需... 目录目标功能示例配置(application.yml)配置类绑定短信发送策略接口示例:阿里云 & 腾

Windows 上如果忘记了 MySQL 密码 重置密码的两种方法

《Windows上如果忘记了MySQL密码重置密码的两种方法》:本文主要介绍Windows上如果忘记了MySQL密码重置密码的两种方法,本文通过两种方法结合实例代码给大家介绍的非常详细,感... 目录方法 1:以跳过权限验证模式启动 mysql 并重置密码方法 2:使用 my.ini 文件的临时配置在 Wi

Spring Boot读取配置文件的五种方式小结

《SpringBoot读取配置文件的五种方式小结》SpringBoot提供了灵活多样的方式来读取配置文件,这篇文章为大家介绍了5种常见的读取方式,文中的示例代码简洁易懂,大家可以根据自己的需要进... 目录1. 配置文件位置与加载顺序2. 读取配置文件的方式汇总方式一:使用 @Value 注解读取配置方式二

JAVA保证HashMap线程安全的几种方式

《JAVA保证HashMap线程安全的几种方式》HashMap是线程不安全的,这意味着如果多个线程并发地访问和修改同一个HashMap实例,可能会导致数据不一致和其他线程安全问题,本文主要介绍了JAV... 目录1. 使用 Collections.synchronizedMap2. 使用 Concurren