sso简单记录

首先我们再次复习一下,多个系统之间为什么无法同步登录状态?

  1. 前端的Token无法在多个系统下共享。
  2. 后端的Session无法在多个系统间共享。

关于第二点,我们已在 "SSO模式一" 章节中阐述,使用 Alone独立Redis插件 做到权限缓存直连 SSO-Redis 数据中心,在此不再赘述。

而第一点,才是我们解决问题的关键所在,在跨域模式下,意味着 "共享Cookie方案" 的失效,我们必须采用一种新的方案来传递Token。

  1. 用户在 子系统 点击 [登录] 按钮。
  2. 用户跳转到子系统登录接口 /sso/login,并携带 back参数 记录初始页面URL。
    • 形如:http://{sso-client}/sso/login?back=xxx
  3. 子系统检测到此用户尚未登录,再次将其重定向至SSO认证中心,并携带redirect参数记录子系统的登录页URL。
    • 形如:http://{sso-server}/sso/auth?redirect=xxx?back=xxx
  4. 用户进入了 SSO认证中心 的登录页面,开始登录。
  5. 用户 输入账号密码 并 登录成功,SSO认证中心再次将用户重定向至子系统的登录接口/sso/login,并携带ticket码参数。
    • 形如:http://{sso-client}/sso/login?back=xxx&ticket=xxxxxxxxx
  6. 子系统根据 ticket码 从 SSO-Redis 中获取账号id,并在子系统登录此账号会话。
  7. 子系统将用户再次重定向至最初始的 back 页面。

整个过程,除了第四步用户在SSO认证中心登录时会被打断,其余过程均是自动化的,当用户在另一个子系统再次点击[登录]按钮,由于此用户在SSO认证中心已有会话存在, 所以第四步也将自动化,也就是单点登录的最终目的 —— 一次登录,处处通行。

第6步中,子系统根据 ticket码 从 SSO-Redis 中获取账号id,并在子系统登录此账号会话。详细如下:

public Object checkTicket(String ticket, String currUri) {
		SaSsoConfig cfg = SaSsoManager.getConfig();
		ApiName apiName = ssoTemplate.apiName;
		
		// --------- 两种模式 
		if(cfg.getIsHttp()) {
			// q1、使用模式三:使用 http 请求从认证中心校验ticket 
			
			// 计算当前 sso-client 的单点注销回调地址 
			String ssoLogoutCall = null; 
			if(cfg.getIsSlo()) {
				// 如果配置了回调地址,就使用配置的值:
				if(SaFoxUtil.isNotEmpty(cfg.getSsoLogoutCall())) {
					ssoLogoutCall = cfg.getSsoLogoutCall();
				}
				// 如果提供了当前 uri,则根据此值来计算:
				else if(SaFoxUtil.isNotEmpty(currUri)) {
					ssoLogoutCall = SaHolder.getRequest().getUrl().replace(currUri, apiName.ssoLogoutCall); 
				}
				// 否则视为不注册单点注销回调地址 
				else {
				}
			}
			
			// 发起请求 
			String checkUrl = ssoTemplate.buildCheckTicketUrl(ticket, ssoLogoutCall);
			SaResult result = ssoTemplate.request(checkUrl);
			
			// 校验 
			if(result.getCode() == SaResult.CODE_SUCCESS) {
				return result.getData();
			} else {
				// 将 sso-server 回应的消息作为异常抛出 
				throw new SaSsoException(result.getMsg()).setCode(SaSsoErrorCode.CODE_30005);
			}
		} else {
			// q2、使用模式二:直连Redis校验ticket 
			return ssoTemplate.checkTicket(ticket);
		}
	}

子系统拿到ticket校验的时候,需要传个client参数,标记自己是谁,避免ticket被中间人拿走用。

public Object checkTicket(String ticket, String client) {
		// 读取 loginId
		String loginId = SaManager.getSaTokenDao().get(splicingTicketSaveKey(ticket));
		
		if(loginId != null) {

			// 如果是 "a,b" 的格式,则解析出对应的 Client
			String ticketClient = null;
			if(loginId.indexOf(",") > -1) {
				String[] arr = loginId.split(",");
				loginId = arr[0];
				ticketClient = arr[1];
			}
			
			// 如果指定了 client 标识,则校验一下 client 标识是否一致 
			if(SaFoxUtil.isNotEmpty(client) && SaFoxUtil.notEquals(client, ticketClient)) {
				throw new SaSsoException("该 ticket 不属于 client=" + client + ", ticket 值: " + ticket)
					.setCode(SaSsoErrorCode.CODE_30011);
			}
			
			// 删除 ticket 信息,使其只有一次性有效
			deleteTicket(ticket);
			deleteTicketIndex(loginId);
		}
		
		// 
		return loginId;
	}

此外,一般ssoServer要记录回调的host,也就是上文的{sso-client},做个防钓鱼操作。

在SSO(Single Sign-On)认证中心回调子系统时,URL中直接携带token的确存在中间人攻击的风险。中间人攻击是指黑客截获用户与认证中心之间的通信,并伪装成用户与子系统之间的通信,从而获取用户的敏感信息或篡改数据。

为了减少中间人攻击的风险,可以采取以下措施:

  1. 使用HTTPS协议:通过使用HTTPS协议,可以确保通信过程中的数据加密,从而防止中间人窃取token等敏感信息。

  2. 对token进行签名或加密:在生成token时,可以使用签名或加密算法对其进行处理,确保token在传输过程中不被篡改或窃取。

  3. 设置token的有效期限:为token设置一个较短的有效期限,以降低被攻击的风险。在token过期后,用户需要重新进行认证。

  4. 使用防重放攻击措施:在子系统中对token进行校验时,可以使用防重放攻击的措施,如使用随机数或时间戳等方式防止token被重放。

需要注意的是,以上措施可以提高安全性,但无法完全消除中间人攻击的风险。因此,在实际应用中,还需要综合考虑其他安全措施,如使用双因素认证、IP限制等来增加系统的安全性。

回调后,子系统拿着ssoToken和app标识等去ssoServer校验的时候,比如走http需要验签?。校验后返回用户详细信息,token,签名,时间戳等,

验签:通过传入公钥、签名、用户信息原始数据,可以验证签名是否正确,以确定数据是否被篡改。Signature.verify。

每次请求是判断是否过期,如果过期重新请求token并更新cookie。

你可能感兴趣的:(java)