- 流程说明
- 导入依赖
<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.16</version></dependency>
- 模拟代码
客户端
import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.DES;/*** @Author wf* @Date 2024/9/13 16:29* @Description: 客户端服务,模拟单点登录*/
public class ClientServer {public static void main(String[] args) {// 创建 DES 对象DES des = SecureUtil.des(ExternalAPP.APP_ID.getBytes());//用户名,比如此处我们需要签发张三的TokenString userName="张三";// 创建临时token需要携带的信息String tokenData =userName+"#"+System.currentTimeMillis();// 加密byte[] encryptedData = des.encrypt(tokenData.getBytes());String encodedEncryptedData = Base64.encode(encryptedData);//模拟请求sso服务器requestAuth(encodedEncryptedData);}/*** 模拟请求sso服务器获取用户Token** @param token 代币*/public static void requestAuth(String token){//此处模拟,通过客户端生成的Token,去请求sso服务器,获取正式TokenSsoServer ssoServer=new SsoServer();String tokenData = ssoServer.getToken(token, ExternalAPP.APP_NAME);System.out.println("获取到的正式Token为:"+tokenData);}
}
服务端
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.DES;
import cn.hutool.jwt.JWTUtil;
import com.google.common.collect.Maps;import java.util.HashMap;
import java.util.Map;/*** @Author wf* @Date 2024/9/13 16:03* @Description: token签发认证*/
public class SsoServer {/*** 模拟数据库中存储的客户端列表*/private static final Map<String, String> APP_MAP = new HashMap<String, String>() {{put(ExternalAPP.APP_NAME, ExternalAPP.APP_ID);}};/*** 获取指定用户的正式Token** @param token 临时token* @param appName 应用名称* @return {@link String}*/public String getToken(String token, String appName){String userName = authTemporaryToken(token, appName);if(StrUtil.isNotBlank(userName)){return issuanceToken(userName);}return "Token无效,或用户不存在";}/*** 认证临时Token** @param token 临时token* @param appName 应用名称* @return {@link String} 需要签发Token的用户名*/private String authTemporaryToken(String token, String appName) {// 对临时token进行解密if (!APP_MAP.containsKey(appName)) {throw new RuntimeException("当前应用不存在,或无效");}// 创建 DES 对象DES des = SecureUtil.des(APP_MAP.get(appName).getBytes());// 解密byte[] decodedEncryptedData = Base64.decode(token);byte[] decryptedData = des.decrypt(decodedEncryptedData);String inscription = new String(decryptedData);if (StrUtil.isNotBlank(inscription)) {// 解析token中的信息String[] tokenArr = inscription.split("#");if (tokenArr.length == 2) {// 需要签发token的用户名String userName = tokenArr[0];// 临时token签发的时间戳String timeStamp = tokenArr[1];// 检查是否超过一分钟(60,000毫秒)boolean isOverOneMinute = (System.currentTimeMillis() - Long.parseLong(timeStamp)) > 60000;// 临时token没超过1分钟说明有效,可以给客户端签发正式Tokenif (!isOverOneMinute) {return userName;}}}throw new RuntimeException("临时Token无效");}/*** 签发Token** @param userName 用户名* @return {@link String}*/private String issuanceToken(String userName){// token过期时间,1分钟Map<String, Object> tokenInfo = Maps.newHashMapWithExpectedSize(3);tokenInfo.put("userName", userName);tokenInfo.put("expireTime", System.currentTimeMillis() + 1000L * 60L );return JWTUtil.createToken(tokenInfo, "e14fb232-4c8d-4f20-bcd6-0b077f9131f4".getBytes());}}
外部APP配置
/*** @Author wf* @Date 2024/9/13 15:56* @Description: 外部服务常量,模拟在服务中配置需要单点的应用,正常情况应该是存储在数据库中*/
public class ExternalAPP {/*** 需要单点登录的应用标识,配置好之后暴露给对应客户端,固定8字节长度作为加密的key*/public static final String APP_ID="weixin01";/*** 需要单点登录的应用名称*/public static final String APP_NAME="微信";}