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

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

目录

 

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

相关文章

使用Sentinel自定义返回和实现区分来源方式

《使用Sentinel自定义返回和实现区分来源方式》:本文主要介绍使用Sentinel自定义返回和实现区分来源方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Sentinel自定义返回和实现区分来源1. 自定义错误返回2. 实现区分来源总结Sentinel自定

Java实现时间与字符串互相转换详解

《Java实现时间与字符串互相转换详解》这篇文章主要为大家详细介绍了Java中实现时间与字符串互相转换的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、日期格式化为字符串(一)使用预定义格式(二)自定义格式二、字符串解析为日期(一)解析ISO格式字符串(二)解析自定义

opencv图像处理之指纹验证的实现

《opencv图像处理之指纹验证的实现》本文主要介绍了opencv图像处理之指纹验证的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录一、简介二、具体案例实现1. 图像显示函数2. 指纹验证函数3. 主函数4、运行结果三、总结一、

Springboot处理跨域的实现方式(附Demo)

《Springboot处理跨域的实现方式(附Demo)》:本文主要介绍Springboot处理跨域的实现方式(附Demo),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录Springboot处理跨域的方式1. 基本知识2. @CrossOrigin3. 全局跨域设置4.

springboot security使用jwt认证方式

《springbootsecurity使用jwt认证方式》:本文主要介绍springbootsecurity使用jwt认证方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录前言代码示例依赖定义mapper定义用户信息的实体beansecurity相关的类提供登录接口测试提供一

Spring Boot 3.4.3 基于 Spring WebFlux 实现 SSE 功能(代码示例)

《SpringBoot3.4.3基于SpringWebFlux实现SSE功能(代码示例)》SpringBoot3.4.3结合SpringWebFlux实现SSE功能,为实时数据推送提供... 目录1. SSE 简介1.1 什么是 SSE?1.2 SSE 的优点1.3 适用场景2. Spring WebFlu

基于SpringBoot实现文件秒传功能

《基于SpringBoot实现文件秒传功能》在开发Web应用时,文件上传是一个常见需求,然而,当用户需要上传大文件或相同文件多次时,会造成带宽浪费和服务器存储冗余,此时可以使用文件秒传技术通过识别重复... 目录前言文件秒传原理代码实现1. 创建项目基础结构2. 创建上传存储代码3. 创建Result类4.

springboot security验证码的登录实例

《springbootsecurity验证码的登录实例》:本文主要介绍springbootsecurity验证码的登录实例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录前言代码示例引入依赖定义验证码生成器定义获取验证码及认证接口测试获取验证码登录总结前言在spring

SpringBoot日志配置SLF4J和Logback的方法实现

《SpringBoot日志配置SLF4J和Logback的方法实现》日志记录是不可或缺的一部分,本文主要介绍了SpringBoot日志配置SLF4J和Logback的方法实现,文中通过示例代码介绍的非... 目录一、前言二、案例一:初识日志三、案例二:使用Lombok输出日志四、案例三:配置Logback一

Python如何使用__slots__实现节省内存和性能优化

《Python如何使用__slots__实现节省内存和性能优化》你有想过,一个小小的__slots__能让你的Python类内存消耗直接减半吗,没错,今天咱们要聊的就是这个让人眼前一亮的技巧,感兴趣的... 目录背景:内存吃得满满的类__slots__:你的内存管理小助手举个大概的例子:看看效果如何?1.