- 流程说明
- 导入依赖
<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());
//用户名,比如此处我们需要签发张三的Token
String 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服务器,获取正式Token
SsoServer 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分钟说明有效,可以给客户端签发正式Token
if (!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="微信";
}