Sa-token SSO单点登录机制【源码】

最近没事想自己写一套SSO的单点登录机制,于是再Sa-token上面学习了一下,Sa-token很优秀的实现了一套完整的单点登录机制,供用户进行使用。我选择其中的模式二,即客户端不同源,Redis同源的认证策略,进行了源码阅读和学习,这也是最常用的单点登录模型。

客户端不同源,认证中心Redis同源

首先讲一下,什么叫做同源和不同源,这个其实很容易理解,通俗来说就是是否在同一个域名下。
举个例子吧
客户端1:client1-sso.com
客户端2:client2-sso.com
认证中心:server-sso.com
我们知道客户端的是否登录这个机制,通常是通过cookie来完成的,现在如果client1跳转到认证中心登录,那么认证中心就会向client1中写入一个cookie,但是因为client1和client2不同源,这就带来了比较大的麻烦,cookie不共享啊!这咋办呢,sa-token是这样解决的。

  1. 用户访问客户端1,进入client1-sso.com,提示用户进行登录
  2. 用户点击登录,跳转到http://client1-sso.com/sso/login?back=http://client1-sso.com/,客户端对该路由进行检查,重定向到认证中心
  3. 用户进入认证中心http://server-sso.com/sso/auth?redirect=http://client1-sso.com/sso/login?back=http://client1-sso.com/,在这里就会有对应操作了,如果用户没有登录,那么认证中心应用程序的cookie里面必然就没有记录token字段,那么此时,就会跳转到认证中心登录界面
  4. 用户输入用户名和密码进行登录,登录成功后,就会往当前会话的认证中心中写入cookie。到这里为止,其实用户就已经完成了认证中心的单点登录了。
  5. 那么接下来,我们再次回到之前的client1,此时同样,我们还是要点击登录按钮,重复上述的操作。注意到第三步时,此时认证中心已经登录,就有了cookie,那么直接拿到了用户信息,同时认证中心生成一个ticket码,将这个ticket放到redis里面去。然后进行重定向,跳转到redirect记录的客户端登录地址http://client1-sso.com/sso/login?back=http://client1-sso.com/&ticket=xxxxxxxxx
  6. 客户端发现登录地址里面有ticket,就会立马连接到远程的redis上面,进行ticket对比,如果无误,就可以再进行检查拿到ticket里面的登录信息。客户端就会进行登录操作,将token写入客户端应用程序的cookie中。
  7. 自动登录完成后,cookie中也记录登录信息,用户返回到back记录的主页面。

那么到此为止,单点登录就已经完成了,从第5步到第7步,这些操作,实际上都是自动完成的。
SSO

再说一下,sa-token是如何实现注销的吧,其实明白了登录如何实现的,注销的原理就非常简单了。目的是客户端选择注销之后,用户需要认证中心重新登录。
1、用户在客户端点击注销,首先在客户端应用程序上删除token,这样就无法通过认证了
2、最关键的第二步,是要让认证中心的token也失效,sa-token中认证中心和客户端共用一个token存储库,实现的方式是将这个token从存储库中移除,就无法识别了,需要重新认证。

代码操作

讲了原理部分,那就看看sa-token是如何实现吧。看源码其实对于很多原理的理解会有很多帮助,实际上,通过这种图的形式来讲解原理是抽象的,当你阅读了源码之后,就会有很深刻的理解。更重要的地方在于,你可以按照别人的思路实现自己的一套SSO单点登录系统。

  • 客户端登录接口
    首先根据客户端登录接口,http://client1-sso.com/login,一般会有可能出现两个参数,一个是back,这个是必须要有的,相当于客户端登录成功后返回的主页url。另一个是ticket,这个可有可无。
    如果没有ticket,说明这个接口客户点击过来的,那么就需要重定向到认证中心去。
    如果有ticket就说明这个是认证中心登录成功后返回的,此时就可以拿着ticket去redis中拿用户信息loginId,然后直接用这个loginId进行登录,生成token,存到客户端的cookie中。
    在这里注意的是,ticket的校验机制,客户端应该与认证中心生成ticket的机制相对应。
	/**
	 * SSO-Client端:登录地址 
	 * @return 处理结果 
	 */
	public static Object ssoLogin() {
		// 获取对象 
		SaRequest req = SaHolder.getRequest();
		SaResponse res = SaHolder.getResponse();
		SaSsoConfig cfg = SaSsoManager.getConfig();
		StpLogic stpLogic = SaSsoUtil.saSsoTemplate.stpLogic;
		
		// 获取参数 
		String back = req.getParam(ParamName.back, "/");
		String ticket = req.getParam(ParamName.ticket);
		
		// 如果当前Client端已经登录,则无需访问SSO认证中心,可以直接返回 
		if(stpLogic.isLogin()) {
			return res.redirect(back);
		}
		/*
		 * 此时有两种情况: 
		 * 情况1:ticket无值,说明此请求是Client端访问,需要重定向至SSO认证中心 
		 * 情况2:ticket有值,说明此请求从SSO认证中心重定向而来,需要根据ticket进行登录 
		 */
		if(ticket == null) {
			String serverAuthUrl = SaSsoUtil.buildServerAuthUrl(SaHolder.getRequest().getUrl(), back);
			return res.redirect(serverAuthUrl);
		} else {
			// ------- 1、校验ticket,获取 loginId 
			Object loginId = checkTicket(ticket, Api.ssoLogin);
			
			// Be: 如果开发者自定义了处理逻辑 
			if(cfg.getTicketResultHandle() != null) {
				return cfg.getTicketResultHandle().apply(loginId, back);
			}
			
			// ------- 2、如果 loginId 无值,说明 ticket 无效
			if(SaFoxUtil.isEmpty(loginId)) {
				throw new SaSsoException("无效ticket:" + ticket).setCode(SaSsoExceptionCode.CODE_20004);
			} else {
				// 3、如果 loginId 有值,说明 ticket 有效,此时进行登录并重定向至back地址 
				stpLogic.login(loginId); 
				return res.redirect(back);
			}
		}
	}
  • 认证中心授权地址
    接下来,我们看看认证中心的授权地址http://server-sso.com/sso/auth,通常会带着一个redirect参数,表示登录成功会跳转回的客户端地址,所谓授权地址,就是检查用户有没有登录的。认证中心会查找当前会话中有没有cookie,然后获取其中token信息,拿到对应的loginId。
    如果没有token,或者并没有获取到loginId,那么说明并没有登录,那么自然而然就会跳转到认证中心的登录地址进行登录。
    如果获取到了loginId,那就说明登录成功了,那么认证中心,就用这个loginId作为标志,生成一个ticket存到redis中。然后重定向到redirect参数中的客户端地址,并再带上一个参数ticket,这个ticket会在客户端用于去redis中获取loginId。
	/**
	 * SSO-Server端:授权地址
	 * @return 处理结果 
	 */
	public static Object ssoAuth() {
		// 获取对象 
		SaRequest req = SaHolder.getRequest();
		SaResponse res = SaHolder.getResponse();
		SaSsoConfig cfg = SaSsoManager.getConfig();
		StpLogic stpLogic = SaSsoUtil.saSsoTemplate.stpLogic;
		
		// ---------- 此处有两种情况分开处理:
		// ---- 情况1:在SSO认证中心尚未登录,需要先去登录 
		if(stpLogic.isLogin() == false) {
			return cfg.getNotLoginView().get();
		}
		// ---- 情况2:在SSO认证中心已经登录,需要重定向回 Client 端,而这又分为两种方式:
		String mode = req.getParam(ParamName.mode, "");
		
		// 方式1:直接重定向回Client端 (mode=simple)
		if(mode.equals(SaSsoConsts.MODE_SIMPLE)) {
			String redirect = req.getParam(ParamName.redirect);
			SaSsoUtil.checkRedirectUrl(redirect);
			return res.redirect(redirect);
		} else {
			// 方式2:带着ticket参数重定向回Client端 (mode=ticket)  
			String redirectUrl = SaSsoUtil.buildRedirectUrl(stpLogic.getLoginId(), req.getParam(ParamName.redirect));
			return res.redirect(redirectUrl);
		}
	}
  • 认证中心登录地址
    这个接口http://server-sso.com/sso/doLogin相对于来说,就比较简单了。用户输入用户名和密码在认证中心进行登录,登录成功后,将token放到当前应用程序的cookie中就可以了。cookie中有了token信息,后面再跳转到认证中心授权地址的时候,就不用再重复登录。
	/**
	 * SSO-Server端:RestAPI 登录接口 
	 * @return 处理结果 
	 */
	public static Object ssoDoLogin() {
		// 获取对象 
		SaRequest req = SaHolder.getRequest();
		SaSsoConfig cfg = SaSsoManager.getConfig();
		
		// 处理 
		return cfg.getDoLoginHandle().apply(req.getParam(ParamName.name), req.getParam(ParamName.pwd));
	}

文章的最后,真的很佩服有些大佬,Sa-token的作者真的很牛逼啊,这是一套非常完整的认证鉴权框架,实现的非常完美。不过使用上太过简单了,对于使用者而言,理解原理只能阅读源码了。

  • 34
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
前后端分离是一种开发架构,将前端和后端的开发过程分开进行,前端主要负责用户界面展示和交互逻辑,后端负责数据处理和业务逻辑。而sa-token是一种单点登录的解决方案。 sa-token是基于Token的一个轻量级权限认证和申请令牌的工具,可以实现前后端分离项目的单点登录功能。它通过生成和验证Token令牌来实现用户身份认证。 在前后端分离的架构中,前端发送登录请求到后端,后端通过验证用户的账号和密码,如果验证成功,则生成一个Token令牌,并将该Token返回给前端。前端保存该Token,每次向后端发送请求时,需要在请求的Header中添加Token。后端通过获取请求Header中的Token,并进行验证,如果验证通过,则表示用户已登录。 sa-token提供了一些便捷的API,用于生成Token、验证Token、获取用户信息等操作。通过这些API,我们可以简便地实现单点登录的功能。sa-token还提供了一些定制化的配置选项,可以根据实际需求进行调整。 前后端分离的sa-token单点登录方案具有以下优点:简单易用、安全性高、扩展性强等。同时,由于前后端分离,使得前端和后端可以独立开发和部署,提高了开发效率和项目的可维护性。 总之,前后端分离sa-token单点登录是一种解决方案,通过Token令牌验证实现用户身份认证,适用于前后端分离的项目,具有诸多优点,可轻松实现单点登录功能。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值