个人主页:阿Q说代码
‍♂️作者简介:大众号阿Q说代码作者(等待你的关注)、infoQ签约作者、CSDN后端范畴新星创作者
技术方向:专注于后端技术栈分享:JVM、数据库、中间件、微服务、Spring全家桶

哈喽咱们好,我是阿Q!

最近工作有点忙,好久没更新文章了,正好这两天在整理单点登陆相关的文档,今日趁着小孩睡着了,赶紧码一篇实战文交差。

概念

单点登录(Single Sign-OnSSO)是一种身份验证服务,答运用户运用单个标识来登录多个运用程序或体系。如下图所示,用户只需求用户名/暗码登陆一次就能够拜访体系A、体系B和体系C。

实战:单点登录的两种实现方式,附源码

在传统的登录办法中,用户必须为每个运用程序或体系供给不同的凭据和暗码。如下图所示,用户拜访体系A、体系B和体系C都必须用用户名/暗码登陆。

实战:单点登录的两种实现方式,附源码

这种办法既不便利也容易被攻击者利用,而 SSO 解决了这个问题,使得用户只需经过一次身份验证就能够无缝地拜访多个运用程序或体系,然后提高了用户体会的便利性和安全性。

单点登陆的优点

  • 用户体会改善:用户只需求登录一次,就能够拜访多个体系或运用程序,不需求重复输入用户名和暗码。这能够大大提高用户的工作效率。
  • 安全性增强:单点登陆能够供给更高级别的安全性,由于用户只需求在一个体系中进行身份验证,其他体系就能够同享这个身份验证信息。这能够有效地避免黑客入侵多个体系。
  • 管理更便利:单点登陆能够简化管理员的工作,由于它能够会集管理用户和权限。管理员能够在一个体系中管理多个体系的用户和权限,这样能够更便利地进行管理和维护。

单点登陆的完成办法

  • 同享身份验证:多个体系同享一个身份验证体系,用户只需求在一个体系中进行身份验证,就能够拜访一切体系。这种办法需求树立一个同享的身份验证体系,这样能够确保用户信息的安全性。
  • 署理身份验证:一个体系代表其他体系进行身份验证,用户在登录时输入用户名和暗码,然后其他体系会代表用户进行身份验证。这种办法需求树立一个署理体系,这样能够确保用户信息的安全性。
  • 依据令牌的身份验证:用户在登录后,会获得一个令牌,这个令牌能够在多个体系上进行身份验证。这种办法需求树立一个令牌管理机制,这样能够确保用户信息的安全性。

实战一

架构图

实战:单点登录的两种实现方式,附源码

  1. 用户输入用户名/暗码登陆 ServiceA 体系;
  2. 用户点击 ServiceA 体系中的某个按钮跳转到 ServiceB 体系,在跳转时需求带上 ServiceA 体系颁布的 ticket 收据;
  3. ServiceB 体系拿 ServiceA 体系的 ticket 去获取 ServiceA 体系的用户信息;
  4. ServiceA 体系会校验该 ticket 收据,然后将用户信息回来给 ServiceB 体系;
  5. ServiceB 体系依据用户信息生成 token 并顺便重定向地址回来给 ServiceA 体系;
  6. ServiceA 体系就能够拿着获取的 token 去拜访 ServiceB 体系的资源信息了。

代码完成

数据库

首先是初始化数据库,用户、公司等表依据自己的具体业务而定,此处不再赘述。供给公共的单点登陆信息表。

CREATE TABLE `sso_client_detail` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
  `platform_name` varchar(64) DEFAULT NULL COMMENT '运用名称',
  `platform_id` varchar(64) NOT NULL COMMENT '运用标识',
  `platform_secret` varchar(64) NOT NULL COMMENT '运用秘钥',
  `encrypt_type` varchar(32) NOT NULL DEFAULT 'RSA' COMMENT '加密办法:AES或者RSA',
  `public_key` varchar(1024) DEFAULT NULL COMMENT 'RSA加密的运用公钥',
  `sso_url` varchar(128) DEFAULT NULL COMMENT '单点登录地址',
  `remark` varchar(1024) DEFAULT NULL COMMENT '备注',
  `create_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
  `update_date` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `update_by` varchar(64) DEFAULT NULL COMMENT '更新人',
  `del_flag` bit(1) NOT NULL DEFAULT b'0' COMMENT '删去标志,0:正常;1:已删去',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='单点登陆信息表'

插入测验数据

INSERT INTO cheetah.sso_client_detail
(id, platform_name, platform_id, platform_secret, encrypt_type, public_key, sso_url, remark, create_date, create_by, update_date, update_by, del_flag)
VALUES(1, 'serviceA', 'A9mQUjun', 'Y6at4LexY5tguevJcKuaIioZ1vS3SDaULwOtXW63buBK4w2e1UEgrKmscjEq', 'RSA', NULL, 'http://127.0.0.1:8081/sso/url', NULL, '2023-05-23 16:55:26', 'system', '2023-05-30 13:16:16', NULL, 0);
  • platform_idplatform_secret,阿Q是运用 apache 的 commons-lang3 包下的RandomStringUtils.randomAlphanumeric()办法生成的。
  • sso_url就是上边说到的 ServiceB 体系的地址。
  • encrypt_typepublic_key在此办法中未运用,能够疏忽。

仔细的阿Q还给咱们预备了一个接口,只需求传入 platformName 和 ssoUrl 就能够主动生成单点登陆信息。

实战:单点登录的两种实现方式,附源码

接下来咱们就进入真正的代码部分了,回复“sso”即可获取实战源码。

A跳转B

/**
 * com.itaq.cheetah.serviceA.controller.PortalController#jump
 * title:跳转 ServiceB
 * <pre>
 * 1. 前端点击Jump链接触发此接口调用
 * 2. 此接口生成ticket并携带着恳求 ServiceB
 *      2.1 ServiceB拿着ticket恳求我方服务获取用户信息
 *      2.2 ServiceB获取到我方用户信息并进行数据同步
 *      2.3 ServiceB回来一个链接,衔接中带 token
 * 3. 重定向到回来的链接完成登录
 * </pre>
 *
 * @param req 单点跳转恳求体
 * @return ServiceB单点登录地址
 */
@PostMapping("/jumpB")
public WrapperResult<String> jump(@RequestBody @Validated SsoJumpReq req) {
	log.debug("单点登录:{}", req.getPlatformName());
	//1、判别该渠道名称是否存在
	SsoClientDetail one = iSsoClientDetailService.getOne(
			new LambdaQueryWrapper<SsoClientDetail>().eq(SsoClientDetail::getPlatformName, req.getPlatformName())
	);
	if (Objects.isNull(one)) {
		return WrapperResult.faild("不存在的app");
	}
	//2、校验本体系的 token,并从中获取用户信息
	/*
	* 示例
	* Result<Token> result = authorizationApi.checkToken(req.getToken());
	*/
	//3、生成ticket,并将用户信息与其绑定存入redis
	String ticket = UUID.randomUUID().toString().replaceAll("-", "");
	UserInfo userInfo = new UserInfo();
	userInfo.setId(1L);
	userInfo.setUsername("阿Q");
	redisTemplate.opsForValue().set(RedisConstants.TICKET_PREFIX + ticket, userInfo, 5, TimeUnit.MINUTES);
	String ssoUrl = one.getSsoUrl();
	Map<String, Object> data = new HashMap<>(1);
	data.put("ticket", ticket);
	//4、发送http恳求,把ticket经过设置好的ssoUrl传给ServiceB
	WrapperResult<SsoRespDto> ssoRespDto = HttpRequest
			.get(ssoUrl)
			.queryMap(data)
			.connectTimeout(Duration.ofSeconds(120))
			.readTimeout(Duration.ofSeconds(120))
			.execute()
			.asValue(new TypeReference<WrapperResult<SsoRespDto>>() {
			});
	log.info("恳求ServiceB 成果:{}", JsonUtils.toPrettyString(ssoRespDto));
	return WrapperResult.success(ssoRespDto.getData().getRedirectUrl());
}

B获取收据,并恳求A获取用户信息

/**
 * com.itaq.cheetah.serviceB.controller.SsoController#sso
 * 获取收据,并恳求ServiceA 获取用户信息
 * @param ticket 收据
 * @return 回来地址供sso跳转
 * @throws JsonProcessingException 反常
 */
@GetMapping("/url")
public WrapperResult<SsoRespDto> sso(@RequestParam("ticket") String ticket) throws JsonProcessingException {
	log.info("收到收据:{}", ticket);
	//1.依据ticket交换ServiceA用户信息
	Map<String, Object> param = new HashMap<>(1);
	param.put("ticket", ticket);
	String ssoUrl = "http://localhost:8081/getUser";
	String s = HttpRequest
			.get(ssoUrl)
			.queryMap(param)
			.connectTimeout(Duration.ofSeconds(120))
			.readTimeout(Duration.ofSeconds(120))
			.execute()
			.asString();
	ObjectMapper objectMapper = new ObjectMapper();
	objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
	WrapperResult<SsoUserInfo> ssoUserInfoWrapperResult = objectMapper.readValue(s, new TypeReference<WrapperResult<SsoUserInfo>>() {
	});
	log.info("ticket登录成果:{}", new ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(ssoUserInfoWrapperResult));
	//2.获取到用户信息之后同步到本地数据库
	log.info("获取用户信息同步数据库");
	//3.生成token
	log.info("生成token");
	SsoRespDto respDto = new SsoRespDto();
	//4、将ServiceA要跳转的地址返给ServiceA并携带 ServiceB 的token
	respDto.setRedirectUrl("http://localhost:8082/index?token=123456");
	WrapperResult<SsoRespDto> success = WrapperResult.success(respDto);
	log.info(new ObjectMapper().writeValueAsString(success));
	return success;
}

A供给的获取用户信息接口

/**
 * com.itaq.cheetah.serviceA.controller.PortalController#loginByTicket
 * 依据收据获取用户信息
 * @param ticket 收据信息
 * @return 用户信息
 */
@ApiOperation("依据ticket获取用户信息")
@GetMapping("/getUser")
public WrapperResult<SsoUserInfo> loginByTicket(@RequestParam("ticket") String ticket) {
	log.info("收到收据:{}", ticket);
	UserInfo userInfo = (UserInfo) redisTemplate.opsForValue().get(RedisConstants.TICKET_PREFIX + ticket);
	if (Objects.isNull(userInfo)) {
		return WrapperResult.faild("无法辨认的收据信息");
	}
	//可能 userInfo 中只有少数的用户信息,此处省掉了依据用户id查询用户和企业信息的过程,自行编写逻辑代码即可
	SsoUserInfo ssoUserInfo = new SsoUserInfo();
	BeanUtil.copyProperties(userInfo,ssoUserInfo);
	return WrapperResult.success(ssoUserInfo);
}

测验成果

实战:单点登录的两种实现方式,附源码

实战二

架构图

实战:单点登录的两种实现方式,附源码

这次咱们用 ServiceB 体系单点登陆 ServiceA 办法:

  • 用户输入用户名/暗码登陆 ServiceB 体系;
  • 用户点击 ServiceB 体系中的某个按钮跳转到 ServiceA 体系,在跳转时需求带上 ServiceB 体系加密后的用户信息;
  • ServiceA 体系拿到 ServiceB 体系加密后的用户信息后进行验签和解密操作;
  • ServiceA 体系将用户信息保存到本地并生成 token 回来给 ServiceB 体系;
  • ServiceB 体系拿到 ServiceA 体系回来的 token 就能够拜访 ServiceA 体系的资源信息了;

代码完成

此种办法就用到了上边说到的数据库中的encrypt_typepublic_key字段,其中 public_key 是 ServiceA 给 ServiceB 供给的。为了演示便利直接在application.yml中进行装备。

B的装备

#本服务的appId和appSecret信息,该装备由serviceA供给
appId: A9mQUjun
appSecret: Y6at4LexY5tguevJcKuaIioZ1vS3SDaULwOtXW63buBK4w2e1UEgrKmscjEq
encrypt:
  #加密办法 RSA | AES
  type: RSA
  #该装备是serviceA单点登陆serviceB用到的,此处是serviceB单点serviceA,所以用不到
  #假如挑选非对称加密,需求运用该装备;本服务的公私钥信息,该装备由serviceB自己生成,并将publicKey给serviceA
  rsa:
    publicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0KLYE2Tv4qx/duxu8Qvq5ZN58yEjj/uwsxfs96pj+9iOOAUKLur8IIKjR/bi54GICUy0BHO6dzpWc0xqGK170F9NTv0bHe0qbh7jHgzq9MJrfcVD+XZAH17ho5tCGIo+z7CiC+rMWGTqmRopd/EQuzfx4Op4/85hoPlpKxdcxAfys0jpZ9tBMtROPsYKhCz01iDnHV2K95s4UwaQLbbx0VALVaXv1/4Yjw/PW4xK0syW/nqUtVqpfwPuX+fHf+bJ2s4kLnFBNwYAKFSU6znGmtJuq6aoxCunu2PbzI8xc7SYxHEfDqG8Zp29wtZcTJecWSDMBmywlaXjkXLzapvE7QIDAQAB
    privateKey: MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDQotgTZO/irH927G7xC+rlk3nzISOP+7CzF+z3qmP72I44BQou6vwggqNH9uLngYgJTLQEc7p3OlZzTGoYrXvQX01O/Rsd7SpuHuMeDOr0wmt9xUP5dkAfXuGjm0IYij7PsKIL6sxYZOqZGil38RC7N/Hg6nj/zmGg+WkrF1zEB/KzSOln20Ey1E4+xgqELPTWIOcdXYr3mzhTBpAttvHRUAtVpe/X/hiPD89bjErSzJb+epS1Wql/A+5f58d/5snaziQucUE3BgAoVJTrOcaa0m6rpqjEK6e7Y9vMjzFztJjEcR8Oobxmnb3C1lxMl5xZIMwGbLCVpeORcvNqm8TtAgMBAAECggEBAKMhoQfRFYxMSkIHbluFcP5eyKylDbRoHOp726pvDUx/L/x3XFYBIHCfFOKRFSvk6SQ0WFFe176f27a9Wfu/sh7kVYNcflZw+YsvFXCKsy/70KZ/lr24izy8KHuPSyf6+E/WkW32Ah9fkNtzTFdfIzDv9m1hiIijq0x9l5C87KjNELnbvC0I6vwFOx0ak+JBbpaJ7IRjZxKZup7UIPvt9nbLzcbKelI83An2JUe8HNhrfWxH9UIyMOBoAY+bKCuAbUtHqSlImPiWyiCwE2/Fh7dmPSOAYYp9aZelnhd25jlR+eh4yaUoIID9ubmYVYbjcPW5SSNdfSZMfQ3oa79QeRUCgYEA6K4L+VLRiX8Dg7NCO1fM2+FTv2csTkPX6n7z/uu7kh0+wQDws+/C6Q906OtizvJBIJqFm2jPACNQCvnRixY1srgMJJlH/Rpeb4LtZGwdM1k0jAZIYQcBlGfaq3RaRI/+6+T0xdsh+7VF5A/smp/VXdK2xI3+JbLQ2wm9uN+3yZcCgYEA5Yvly7veDJYf2+8HIQkRhjWrWm1y5lCSe+HG+1ktfqnhN8YEOiPa71u0TXealL0T8EoKsqhWEjomxZ7n0jLigogz7OxxsGAE6HXAiKX0REINNYrq+1qNaqmkfLrhAJyg3JNgTSlb0xd56w7FSqOBttVL9INawGb1P98kYc5OzhsCgYBEfIY1urTGPcZxC2BhSzSXO7mEyv91ge6ZrQhwbj5lgYopEPfIXrgGFXCZ5j7NHu0ghZrx5WWYasxyjpmo0L65fgbE9wEDdLF7LRRmzJPDu2wGEwtW09MZNYBdmv++0ot8L4YEfr1/8xlBSZag5I7O8Oiu7gRyYDGtZy6are7QvQKBgQCaUZnUhOF7/rU+a4yUZf9VBeHD8k7LjaFdDWVzdvmB7P1PPJ185Lv8LN+jMORIWHD+GxjkEQ2ERXnpY7If+zuSW7Tk8/Reib7i9L7SXxc/iFRPCax9/NuTuKavgAdiHOp8P8v/M+3alS7OmuiCDDhZTT46DNDHBrCcFwzjgAo0vwKBgECBs6hEUVsYU74Uc64he8Zgkvj7wZ/yxnFlWmRRERprfBsuiY/y+DAf5ehezSRFpHXUrAkpeVXq2ydnr9BKTs6TV3AxlDMBNSndXsUYHENncR7tEHCSGRFTTu5jxdYA+k47R865Jh+2vQvPaPaXsEKSkDegvcFeUVR/yi5AsDub
#假如挑选非对称加密,则需求运用该装备,该装备由serviceA供给
serviceA:
  rsa:
    publicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2BF9EZCscKNXYADtulIDNHaMnoxV5Yu91jpv+LiWabW2EO51b8Sx8+Ei59EebM4r+SMal0k4L2Z+cNagQSP4Wvpss82/MkGO8bnAFSxS2SOKw+a+c2PxByWUxvHo4pbyYGFVWAGDXLiI+IqiO/fEFfpy6rYQzMLDnfgMFngdS4AZmRyTdMKbQs8mWqBE5nC0PoU39o/lFowfgelEjHE9vhjtTha67KhYY3n+ueuxsYdRQ40Mg7aQ0+Kt/qKoSn9yRWyx09DheFAkYl4ZCQfd0sMotLQ4BZtk0YWMNHOc1w+fL1bOumaj7AaJi6nM/VvwylLJyia2GjJIDrdTfHiOnwIDAQAB

A的装备

serviceA:
  rsa:
    privateKey: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDYEX0RkKxwo1dgAO26UgM0doyejFXli73WOm/4uJZptbYQ7nVvxLHz4SLn0R5sziv5IxqXSTgvZn5w1qBBI/ha+myzzb8yQY7xucAVLFLZI4rD5r5zY/EHJZTG8ejilvJgYVVYAYNcuIj4iqI798QV+nLqthDMwsOd+AwWeB1LgBmZHJN0wptCzyZaoETmcLQ+hTf2j+UWjB+B6USMcT2+GO1OFrrsqFhjef6567Gxh1FDjQyDtpDT4q3+oqhKf3JFbLHT0OF4UCRiXhkJB93Swyi0tDgFm2TRhYw0c5zXD58vVs66ZqPsBomLqcz9W/DKUsnKJrYaMkgOt1N8eI6fAgMBAAECggEAA5f23o3rcEwnLd+WFJ08lGjMWe63lwPF+oQqTJa1Wbi9+HYe2ecJlqbN79EYknKzZIdi79U17APmYnYPYEX64Xh8yljHr0xL1lVijneYQShILI3v6PdmkNndKZnoZ6xfB59WzgnoZ2hiTs/vdtPeHQd3VdQFX4J1wnDXsp/4zMKi1fDPt7rhqWrP5W6PXcoGGKIkN9zBlqrd1RBdnKXcwfFoHcFf2ikk6g3Kn50YMRe324eiHMm8z7W34Y3iSvZYHcKBMgsDklFerw1WOGHTN61oMr+8/NTtCsy1AnCH4PrwX/ryO17mh5xNzo/ZSZRRezR92/hmwUIuOO+3FWIE4QKBgQD05wYMVlGKn1fm+sn4hn+ErC6NifXj3MkNdjs8oSHzLrYr6ea6xIvbxesZvqzqz1Fh68bHjpJPOBKwgFnl7+dLXYLNmKjry1iK0o/MMZTtrGUwMEnWHRrpmxXH6B0cnBecZUReuJ9XfKZIfd9ksHHsUY7IGv1CHcblVP/IhrpnxwKBgQDh2/n0cAh1jygGevlXGK/rxuRSlbVgtxJWLAtY8Yolf2BklSiTwmqtp7nzNn8sxRvgfQCZaLqpjC/o/wtC3Ba5b4StJQejoXkCNhVmRdLbIQ2tUxwAElPjFhWf3C5/4B6uBeLyC9izp4wTSYbNbPKxcUGkkfpPbWdHsFZOG4gSaQKBgA/me/cLF6o3ZD6j478WBGt5vmAEKAnOSONt3LS4BXtDeiJpwkg4AJiZRgVa4uEv6qm/5B0KvacVDemVu8B5DfxPqvFsSvNcNXh16U4pnfC8c6loSTL0ms21+vkKsfEslT/bN1ArDnVgq28jdQCVkB/2v51wWycSxdoX5a+AR9P7AoGAMvTwZefI4M0VmLCyBKZ7OlS7Oq6wJ0vmhS6WuNB1/JPKaacFaqDYdKl82JSZCL7H1VQeiH4KbypDvOud3M3PCrNQWcga+x35MTiGh3aFZg8FCO/RR2rbJkbbRh/lFdC420ZUt4tYrt/ESK20DjDgaIxG5RxSPw1N2ey87A5mGtECgYEAlA12yuxBb6qmG3OUSlacSfcKnxZIC3L1IMqxlXL8eG3MB4dI6QYesc3odmaxmy9csgHs+pTyLfM3yB9Ocl572OW5WcEnod5o1EIup9hxB4IG/xSECYVFHlGKfIgbd/JhWtqloYZrwx+kVX/Iw02z18R32DRqBtK4MQ3klOYH86s=

B跳转A并加密用户信息

/**
 * com.itaq.cheetah.serviceB.controller.ToServiceAController#redirectToServiceA
 * 跳转 ServiceA 服务
 *
 * @return ServiceA回来的重定向链接
 */
@GetMapping
public WrapperResult<String> redirectToServiceA() {
	//1、构建用户信息
	SsoUserInfo data = buildSsoUserInfo();
	Long timestamp = System.currentTimeMillis();
	String flowId = UUID.randomUUID().toString();
	String businessId = "sso";
	String dataEncrypt;
	String encryptType = configProperties.getEncryptType();
	//2、依据装备挑选哪种办法加密
	switch (encryptType) {
		case "AES":
			AES aes = new AES(configProperties.getAppSecret().getBytes(StandardCharsets.UTF_8));
			dataEncrypt = aes.encryptBase64(JsonUtils.toString(data), StandardCharsets.UTF_8);
			break;
		case "RSA":
			RSA rsa = new RSA(AsymmetricAlgorithm.RSA_ECB_PKCS1.getValue(), null, configProperties.getServiceAPublicKey());
			dataEncrypt = rsa.encryptBase64(JsonUtils.toString(data), StandardCharsets.UTF_8, KeyType.PublicKey);
			break;
		default:
			return WrapperResult.faild("未装备加密办法");
	}
	//3、将以下信息进行签名
	SsoSignSource build = SsoSignSource.builder()
			.platformId(configProperties.getAppId())
			.platformSecret(configProperties.getAppSecret())
			.businessId(businessId)
			.data(dataEncrypt)
			.flowId(flowId)
			.timestamp(timestamp)
			.build();
	String sign = build.sign();
	log.info("sign source={}", JsonUtils.toPrettyString(build));
	//4、构建恳求体
	ToServiceAReq req = ToServiceAReq.builder()
			.platformId(configProperties.getAppId())
			.businessId("sso")
			.flowId(flowId)
			.timestamp(timestamp)
			.sign(sign)
			.data(dataEncrypt)
			.build();
    //5、跳转A的操作
	String s = HttpRequest.post("http://localhost:8081/serviceA")
			.bodyString(JsonUtils.toString(req))
			.execute()
			.asString();
	log.info("成果:{}", s);
	return WrapperResult.success(s);
}

A获取用户信息后续操作

/**
 * com.itaq.cheetah.serviceA.controller.ServiceAController#sso
 *
 * @return
 */
@PostMapping
public WrapperResult<SsoRespDto> sso(@VerifySign ToServiceAReq req) {
	log.info("收到单点登录ServiceA的恳求:{}", JsonUtils.toPrettyString(req));
	//同步用户信息
	//模仿登陆生成token
	//回来拼接的url?token=xxx
	//回来拼接的url?token=xxx
    String url ="127.0.0.1:8081/index?token=xxx";
    SsoRespDto ssoRespDto = new SsoRespDto();
    ssoRespDto.setRedirectUrl(url);
    return WrapperResult.success(ssoRespDto);
}

你可能会猎奇,验签解密的逻辑去哪了?

此处咱们经过注解的办法完成主动验签和解密的逻辑,至于具体的逻辑,咱们能够回复“sso”获取源码自行解读,当然后续阿Q还会推出新的文章进行具体的讲解,点击关注【阿Q说代码】进行预定吧!

测验

实战:单点登录的两种实现方式,附源码

弥补知识

本文中用到的 RSA 的密钥是经过在线网站https://www.bchrt.com/tools/rsa/生成的,当然咱们也能够运用 hutool 中的 RSA 类来生成,也能够运用 java 自带的 security 来生成。

回复“sso”获取实战源码!

今日的内容到这里就完毕了,跪求一键三连,希望靓仔在评论区打出老铁666,鼓励一下阿Q。

好看的皮囊千篇一律,有趣的灵魂万里挑一,让咱们在冷酷的城市里相互温暖,我是阿Q,咱们下期再见!

引荐阅读

实战:画了几张图,终于把OAuth2搞清楚了

重磅出击,20张图带你彻底了解ReentrantLock加锁解锁的原理

看了同事写的代码,我居然开端默默的模仿了。。。

实战篇:断点续传?文件秒传?手撸大文件上传