本文主要是介绍用上帝视角俯瞰单点登录的前世与今生(内含两种实现方式的源码),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
目录
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的时候,因为还没有登录,会被引导到认证系统中进行登录。
- 根据用户提供的登录信息,认证系统进行身份效验,如果通过效验,应该返回给用户一个认证的凭据Ticket;
- 用户再访问其他相互信任的应用的时候,就会把这个Ticket作为自己认证的凭据,应用系统接受到请求之后会把 Ticket送到认证系统进行效验,检查 Ticket的合法性;
- 如果通过效验,用户就可以在不用再次登录的情况下访问系统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:
- 用户点击http://www.xiaoming.com页面登录按钮,向后台服务器发送登录请求;
- 输入正确的用户名密码,登录认证成功,服务器会把登录信息写入session;
- 服务器为该用户生成一个cookie,并加入到response header中,随着请求返回而写入浏览器中;
- 用户再次访问同域的http://cart.xiaoming.com的时候,浏览器会带上之前的cookie;
- 后台服务器通过该cookie验证当前账户的登陆状态了。
同父域SSO:
- 同父域 SSO 是同域 SSO 的简单升级,唯一的不同在于,服务器在返回 cookie 的时候,要把cookie 的 domain 设置为其父域。
- 比如两个产品的地址分别为 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
- CAS Server:负责完成对用户的认证工作 , 需要独立部署。
- 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实现流程图:
- 用户访问产品 CasClientOne,域名是 http://localhost:8081。
- 由于用户没有携带在CasClientOne上登录的 cookie,所以 CasClientOne重定向到SSO 服务器的地址。
- 由于用户没有携带在 SSO 服务器上登录的 TGC,所以 SSO 服务器判断用户未登录,给用户显示统一登录界面。
- 登录成功后,SSO 服务器构建用户在 SSO 登录的 TGT,同时返回一个 http 重定向(包含 SSO 服务器派发的 ST )。
- 重定向的 http response 中包含写 cookie。这个 cookie 代表用户在 SSO 中的登录状态,它的值是 TGC。
- 浏览器重定向到CasClientOne。此时重定向的 url 中携带着 SSO 服务器生成的 ST。根据 ST,CasClientOne向 SSO 服务器发送请求,SSO 服务器验证票据的有效性。验证成功后,CasClientOne知道用户已经在 SSO服务器登录了,于是CasClientOne构建用户登录 session。
- 用户访问产品CasClientTWO,域名是 http://localhost:8082。
- 由于用户没有携带在CasClientTWO上登录的cookie,所以 CasClientTWO重定向到SSO 服务器,去询问用户在 SSO 中的登录状态。
- 浏览器重定向到 SSO服务器。由于已经向浏览器写入了携带 TGC 的cookie,所以此时 SSO 服务器可以拿到,根据 TGC 去查找 TGT,如果找到,就判断用户已经在SSO服务器登录过了。
- SSO 服务器返回一个重定向,重定向携带 ST。
- 浏览器带 ST 重定向到CasClientTWO。
- 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)实际遇到的一些问题的解决方案。感兴趣的小伙伴可以点点关注。觉得本文对您理解单点登录有一些帮助的小伙伴,麻烦给这篇文章点点赞。如果大佬发现本文理解存在问题,希望可以在下方评论区指点我改正。最后谢谢大家的阅读!小名祝大家每天都开开心心哒~😁
这篇关于用上帝视角俯瞰单点登录的前世与今生(内含两种实现方式的源码)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!