From 0229459d8d5721a6e2ca0323a6ccf6fcfd2fc8f7 Mon Sep 17 00:00:00 2001 From: click33 <2393584716@qq.com> Date: Wed, 30 Apr 2025 11:27:14 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E6=96=B0=E5=A2=9E=20TicketModel=20?= =?UTF-8?q?=E5=AF=B9=E8=B1=A1=EF=BC=8C=E4=BB=A5=E4=BE=BF=E5=9C=A8=20ticket?= =?UTF-8?q?=20=E4=B8=AD=E5=82=A8=E5=AD=98=E6=9B=B4=E5=A4=9A=E4=BF=A1?= =?UTF-8?q?=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dev33/satoken/sso/model/TicketModel.java | 191 +++++++++++++++++ .../sso/template/SaSsoServerTemplate.java | 192 ++++++++++-------- .../dev33/satoken/sso/template/SaSsoUtil.java | 7 +- .../dev33/satoken/sso/util/SaSsoConsts.java | 3 + 4 files changed, 309 insertions(+), 84 deletions(-) create mode 100644 sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/model/TicketModel.java diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/model/TicketModel.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/model/TicketModel.java new file mode 100644 index 00000000..5f1191bb --- /dev/null +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/model/TicketModel.java @@ -0,0 +1,191 @@ +/* + * Copyright 2020-2099 sa-token.cc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package cn.dev33.satoken.sso.model; + +import java.io.Serializable; + +/** + * Model: Ticket 码 + * + * @author click33 + * @since 1.43.0 + */ +public class TicketModel implements Serializable { + + private static final long serialVersionUID = -6541180061782004705L; + + /** + * ticket 码 + */ + public String ticket; + + /** + * 应用标识 + */ + public String client; + + /** + * 设备 id + */ + public String deviceId; + + /** + * 对应 loginId + */ + public Object loginId; + + /** + * 创建时间,13位时间戳 + */ + public long createTime; + + /** + * 构建一个 + */ + public TicketModel() { + this.createTime = System.currentTimeMillis(); + } + + /** + * 构建一个 + * @param ticket 授权码 + * @param client 应用id + * @param loginId 对应的账号id + * @param deviceId 重定向地址 + */ + public TicketModel(String ticket, String client, String deviceId, Object loginId) { + this(); + this.ticket = ticket; + this.client = client; + this.deviceId = deviceId; + this.loginId = loginId; + } + + + // get set + + /** + * 获取 ticket 码 + * + * @return / + */ + public String getTicket() { + return this.ticket; + } + + /** + * 设置 ticket 码 + * + * @param ticket / + * @return 对象自身 + */ + public TicketModel setTicket(String ticket) { + this.ticket = ticket; + return this; + } + + /** + * 获取 应用标识 + * + * @return / + */ + public String getClient() { + return this.client; + } + + /** + * 设置 应用标识 + * + * @param client / + * @return 对象自身 + */ + public TicketModel setClient(String client) { + this.client = client; + return this; + } + + /** + * 获取 设备 id + * + * @return / + */ + public String getDeviceId() { + return this.deviceId; + } + + /** + * 设置 设备 id + * + * @param deviceId / + * @return 对象自身 + */ + public TicketModel setDeviceId(String deviceId) { + this.deviceId = deviceId; + return this; + } + + /** + * 获取 对应 loginId + * + * @return / + */ + public Object getLoginId() { + return this.loginId; + } + + /** + * 设置 对应 loginId + * + * @param loginId / + * @return 对象自身 + */ + public TicketModel setLoginId(Object loginId) { + this.loginId = loginId; + return this; + } + + /** + * 获取 创建时间,13位时间戳 + * + * @return / + */ + public long getCreateTime() { + return this.createTime; + } + + /** + * 设置 创建时间,13位时间戳 + * + * @param createTime / + * @return 对象自身 + */ + public TicketModel setCreateTime(long createTime) { + this.createTime = createTime; + return this; + } + + @Override + public String toString() { + return "TicketModel{" + + "ticket='" + ticket + '\'' + + ", client='" + client + '\'' + + ", deviceId='" + deviceId + '\'' + + ", loginId=" + loginId + + ", createTime=" + createTime + + '}'; + } + +} diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoServerTemplate.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoServerTemplate.java index b85e2c61..b9ca8b0c 100644 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoServerTemplate.java +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoServerTemplate.java @@ -28,6 +28,7 @@ import cn.dev33.satoken.sso.message.SaSsoMessage; import cn.dev33.satoken.sso.message.handle.server.SaSsoMessageCheckTicketHandle; import cn.dev33.satoken.sso.message.handle.server.SaSsoMessageSignoutHandle; import cn.dev33.satoken.sso.model.SaSsoClientInfo; +import cn.dev33.satoken.sso.model.TicketModel; import cn.dev33.satoken.sso.util.SaSsoConsts; import cn.dev33.satoken.strategy.SaStrategy; import cn.dev33.satoken.util.SaFoxUtil; @@ -51,38 +52,38 @@ public class SaSsoServerTemplate extends SaSsoTemplate { // ---------------------- Ticket 操作 ---------------------- /** - * 保存 Ticket 关联的 loginId - * @param ticket ticket码 - * @param loginId 账号id + * 保存 Ticket + * @param ticketModel / */ - public void saveTicket(String ticket, Object loginId) { - // 保存 ticket -> loginId 的关系 + public void saveTicket(TicketModel ticketModel) { long ticketTimeout = getServerConfig().getTicketTimeout(); - SaManager.getSaTokenDao().set(splicingTicketSaveKey(ticket), String.valueOf(loginId), ticketTimeout); + SaManager.getSaTokenDao().setObject(splicingTicketSaveKey(ticketModel.getTicket()), ticketModel, ticketTimeout); } /** * 保存 Ticket 索引 (id 反查 ticket) + * + * @param client 应用端 * @param ticket ticket码 * @param loginId 账号id */ - public void saveTicketIndex(String ticket, Object loginId) { + public void saveTicketIndex(String client, String ticket, Object loginId) { long ticketTimeout = getServerConfig().getTicketTimeout(); - SaManager.getSaTokenDao().set(splicingTicketIndexKey(loginId), String.valueOf(ticket), ticketTimeout); + SaManager.getSaTokenDao().set(splicingTicketIndexKey(client, loginId), String.valueOf(ticket), ticketTimeout); } - /** - * 保存 Ticket 关联的 client - * @param ticket ticket码 - * @param client 客户端标识 - */ - public void saveTicketToClient(String ticket, String client) { - if(SaFoxUtil.isEmpty(client)) { - return; - } - long ticketTimeout = getServerConfig().getTicketTimeout(); - SaManager.getSaTokenDao().set(splicingTicketToClientSaveKey(ticket), client, ticketTimeout); - } +// /** +// * 保存 Ticket 关联的 client +// * @param ticket ticket码 +// * @param client 客户端标识 +// */ +// public void saveTicketToClient(String ticket, String client) { +// if(SaFoxUtil.isEmpty(client)) { +// return; +// } +// long ticketTimeout = getServerConfig().getTicketTimeout(); +// SaManager.getSaTokenDao().set(splicingTicketToClientSaveKey(ticket), client, ticketTimeout); +// } /** * 删除 Ticket @@ -92,29 +93,45 @@ public class SaSsoServerTemplate extends SaSsoTemplate { if(ticket == null) { return; } - SaManager.getSaTokenDao().delete(splicingTicketSaveKey(ticket)); + SaManager.getSaTokenDao().deleteObject(splicingTicketSaveKey(ticket)); } /** * 删除 Ticket索引 + * + * @param client 应用标识 * @param loginId 账号id */ - public void deleteTicketIndex(Object loginId) { + public void deleteTicketIndex(String client, Object loginId) { if(loginId == null) { return; } - SaManager.getSaTokenDao().delete(splicingTicketIndexKey(loginId)); + SaManager.getSaTokenDao().delete(splicingTicketIndexKey(client, loginId)); } +// /** +// * 删除 Ticket 关联的 client +// * +// * @param ticket Ticket码 +// */ +// public void deleteTicketToClient(String ticket) { +// if(ticket == null) { +// return; +// } +// SaManager.getSaTokenDao().delete(splicingTicketToClientSaveKey(ticket)); +// } + /** - * 删除 Ticket 关联的 client + * 查询 ticket ,如果 ticket 码无效则返回 null + * * @param ticket Ticket码 + * @return 账号id */ - public void deleteTicketToClient(String ticket) { - if(ticket == null) { - return; + public TicketModel getTicket(String ticket) { + if(SaFoxUtil.isEmpty(ticket)) { + return null; } - SaManager.getSaTokenDao().delete(splicingTicketToClientSaveKey(ticket)); + return SaManager.getSaTokenDao().getObject(splicingTicketSaveKey(ticket), TicketModel.class); } /** @@ -123,10 +140,11 @@ public class SaSsoServerTemplate extends SaSsoTemplate { * @return 账号id */ public Object getLoginId(String ticket) { - if(SaFoxUtil.isEmpty(ticket)) { + TicketModel ticketModel = getTicket(ticket); + if(ticketModel == null) { return null; } - return SaManager.getSaTokenDao().get(splicingTicketSaveKey(ticket)); + return ticketModel.getLoginId(); } /** @@ -141,28 +159,30 @@ public class SaSsoServerTemplate extends SaSsoTemplate { } /** - * 查询 指定 loginId 其所属的 ticket 值 + * 查询 指定 client、loginId 其所属的 ticket 值 + * + * @param client 应用 * @param loginId 账号id * @return Ticket值 */ - public String getTicketValue(Object loginId) { + public String getTicketValue(String client, Object loginId) { if(loginId == null) { return null; } - return SaManager.getSaTokenDao().get(splicingTicketIndexKey(loginId)); + return SaManager.getSaTokenDao().get(splicingTicketIndexKey(client, loginId)); } - /** - * 查询 ticket 关联的 client,如果 ticket 码无效则返回 null - * @param ticket Ticket码 - * @return 账号id - */ - public String getTicketToClient(String ticket) { - if(SaFoxUtil.isEmpty(ticket)) { - return null; - } - return SaManager.getSaTokenDao().get(splicingTicketToClientSaveKey(ticket)); - } +// /** +// * 查询 ticket 关联的 client,如果 ticket 码无效则返回 null +// * @param ticket Ticket码 +// * @return 账号id +// */ +// public String getTicketToClient(String ticket) { +// if(SaFoxUtil.isEmpty(ticket)) { +// return null; +// } +// return SaManager.getSaTokenDao().get(splicingTicketToClientSaveKey(ticket)); +// } // /** @@ -174,11 +194,15 @@ public class SaSsoServerTemplate extends SaSsoTemplate { public String createTicket(Object loginId, String client) { // 创建 Ticket String ticket = randomTicket(loginId); + TicketModel ticketModel = new TicketModel(); + ticketModel.setTicket(ticket); + ticketModel.setClient(client); + ticketModel.setLoginId(loginId); + // TODO ticketModel.setDeviceId(); // 保存 Ticket - saveTicket(ticket, loginId); - saveTicketIndex(ticket, loginId); - saveTicketToClient(ticket, client); + saveTicket(ticketModel); + saveTicketIndex(client, ticket, loginId); // 返回 Ticket return ticket; @@ -201,31 +225,32 @@ public class SaSsoServerTemplate extends SaSsoTemplate { */ public Object checkTicket(String ticket, String client) { // 读取 loginId - String loginId = SaManager.getSaTokenDao().get(splicingTicketSaveKey(ticket)); - - if(loginId != null) { - - // 解析出这个 ticket 关联的 Client - String ticketClient = getTicketToClient(ticket); - - // 校验 client 参数是否正确,即:创建 ticket 的 client 和当前校验 ticket 的 client 是否一致 - if(SaSsoConsts.CLIENT_WILDCARD.equals(client)) { - // 如果提供的是通配符,直接越过 client 校验 - } else if (SaFoxUtil.isEmpty(client) && SaFoxUtil.isEmpty(ticketClient)) { - // 如果提供的和期望的两者均为空,则通过校验 - } else { - // 开始详细比对 - if(SaFoxUtil.notEquals(client, ticketClient)) { - throw new SaSsoException("该 ticket 不属于 client=" + client + ", ticket 值: " + ticket).setCode(SaSsoErrorCode.CODE_30011); - } - } - - // 删除 ticket 信息,使其只有一次性有效 - deleteTicket(ticket); - deleteTicketIndex(loginId); - deleteTicketToClient(ticket); + TicketModel ticketModel = getTicket(ticket); + if(ticketModel == null) { + return null; } + Object loginId = ticketModel.getLoginId(); + String ticketClient = ticketModel.getClient(); + + // 解析出这个 ticket 关联的 Client + + // 校验 client 参数是否正确,即:创建 ticket 的 client 和当前校验 ticket 的 client 是否一致 + if(SaSsoConsts.CLIENT_WILDCARD.equals(client)) { + // 如果提供的是通配符,直接越过 client 校验 + } else if (SaFoxUtil.isEmpty(client) && SaFoxUtil.isEmpty(ticketClient)) { + // 如果提供的和期望的两者均为空,则通过校验 + } else { + // 开始详细比对 + if(SaFoxUtil.notEquals(client, ticketClient)) { + throw new SaSsoException("该 ticket 不属于 client=" + client + ", ticket 值: " + ticket).setCode(SaSsoErrorCode.CODE_30011); + } + } + + // 删除 ticket 信息,使其只有一次性有效 + deleteTicket(ticket); + deleteTicketIndex(ticket, loginId); + // return loginId; } @@ -333,7 +358,7 @@ public class SaSsoServerTemplate extends SaSsoTemplate { checkRedirectUrl(client, redirect); // 删掉 旧Ticket - deleteTicket(getTicketValue(loginId)); + deleteTicket(getTicketValue(client, loginId)); // 创建 新Ticket String ticket = createTicket(loginId, client); @@ -714,22 +739,27 @@ public class SaSsoServerTemplate extends SaSsoTemplate { return getStpLogic().getConfigOrGlobal().getTokenName() + ":ticket:" + ticket; } - /** - * 拼接key:Ticket 查 所属的 client - * @param ticket ticket值 - * @return key - */ - public String splicingTicketToClientSaveKey(String ticket) { - return getStpLogic().getConfigOrGlobal().getTokenName() + ":ticket-client:" + ticket; - } +// /** +// * 拼接key:Ticket 查 所属的 client +// * @param ticket ticket值 +// * @return key +// */ +// public String splicingTicketToClientSaveKey(String ticket) { +// return getStpLogic().getConfigOrGlobal().getTokenName() + ":ticket-client:" + ticket; +// } /** * 拼接key:账号Id 反查 Ticket + * + * @param client 应用标识 * @param id 账号id * @return key */ - public String splicingTicketIndexKey(Object id) { - return getStpLogic().getConfigOrGlobal().getTokenName() + ":id-ticket:" + id; + public String splicingTicketIndexKey(String client, Object id) { + if(SaFoxUtil.isEmpty(client) || SaSsoConsts.CLIENT_WILDCARD.equals(client)) { + client = SaSsoConsts.CLIENT_ANON; + } + return getStpLogic().getConfigOrGlobal().getTokenName() + ":id-ticket:" + client + ":" + id; } } diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoUtil.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoUtil.java index f0bad5b8..e98c59ed 100644 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoUtil.java +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoUtil.java @@ -50,10 +50,11 @@ public class SaSsoUtil { /** * 删除 Ticket索引 - * @param loginId 账号id + * @param client 应用 id + * @param loginId 账号id */ - public static void deleteTicketIndex(Object loginId) { - SaSsoServerProcessor.instance.ssoServerTemplate.deleteTicketIndex(loginId); + public static void deleteTicketIndex(String client, Object loginId) { + SaSsoServerProcessor.instance.ssoServerTemplate.deleteTicketIndex(client, loginId); } /** diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/util/SaSsoConsts.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/util/SaSsoConsts.java index 1ee4b4d9..b51f4f5b 100644 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/util/SaSsoConsts.java +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/util/SaSsoConsts.java @@ -48,6 +48,9 @@ public class SaSsoConsts { /** client 身份,* 代表通配,可以解析出所有 client 的 ticket */ public static final String CLIENT_WILDCARD = "*"; + /** client 身份,代表匿名 client */ + public static final String CLIENT_ANON = "anon"; + /** SSO 模式1 */ public static final int SSO_MODE_1 = 1; /** SSO 模式2 */