1
0
mirror of synced 2026-05-22 14:43:15 +00:00

refactor: 新增 TicketModel 对象,以便在 ticket 中储存更多信息

This commit is contained in:
click33
2025-04-30 11:27:14 +08:00
parent 66c431bb3e
commit 0229459d8d
4 changed files with 309 additions and 84 deletions
@@ -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 +
'}';
}
}
@@ -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.SaSsoMessageCheckTicketHandle;
import cn.dev33.satoken.sso.message.handle.server.SaSsoMessageSignoutHandle; import cn.dev33.satoken.sso.message.handle.server.SaSsoMessageSignoutHandle;
import cn.dev33.satoken.sso.model.SaSsoClientInfo; 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.sso.util.SaSsoConsts;
import cn.dev33.satoken.strategy.SaStrategy; import cn.dev33.satoken.strategy.SaStrategy;
import cn.dev33.satoken.util.SaFoxUtil; import cn.dev33.satoken.util.SaFoxUtil;
@@ -51,38 +52,38 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
// ---------------------- Ticket 操作 ---------------------- // ---------------------- Ticket 操作 ----------------------
/** /**
* 保存 Ticket 关联的 loginId * 保存 Ticket
* @param ticket ticket码 * @param ticketModel /
* @param loginId 账号id
*/ */
public void saveTicket(String ticket, Object loginId) { public void saveTicket(TicketModel ticketModel) {
// 保存 ticket -> loginId 的关系
long ticketTimeout = getServerConfig().getTicketTimeout(); long ticketTimeout = getServerConfig().getTicketTimeout();
SaManager.getSaTokenDao().set(splicingTicketSaveKey(ticket), String.valueOf(loginId), ticketTimeout); SaManager.getSaTokenDao().setObject(splicingTicketSaveKey(ticketModel.getTicket()), ticketModel, ticketTimeout);
} }
/** /**
* 保存 Ticket 索引 id 反查 ticket * 保存 Ticket 索引 id 反查 ticket
*
* @param client 应用端
* @param ticket ticket码 * @param ticket ticket码
* @param loginId 账号id * @param loginId 账号id
*/ */
public void saveTicketIndex(String ticket, Object loginId) { public void saveTicketIndex(String client, String ticket, Object loginId) {
long ticketTimeout = getServerConfig().getTicketTimeout(); 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 // * 保存 Ticket 关联的 client
* @param ticket ticket码 // * @param ticket ticket码
* @param client 客户端标识 // * @param client 客户端标识
*/ // */
public void saveTicketToClient(String ticket, String client) { // public void saveTicketToClient(String ticket, String client) {
if(SaFoxUtil.isEmpty(client)) { // if(SaFoxUtil.isEmpty(client)) {
return; // return;
} // }
long ticketTimeout = getServerConfig().getTicketTimeout(); // long ticketTimeout = getServerConfig().getTicketTimeout();
SaManager.getSaTokenDao().set(splicingTicketToClientSaveKey(ticket), client, ticketTimeout); // SaManager.getSaTokenDao().set(splicingTicketToClientSaveKey(ticket), client, ticketTimeout);
} // }
/** /**
* 删除 Ticket * 删除 Ticket
@@ -92,29 +93,45 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
if(ticket == null) { if(ticket == null) {
return; return;
} }
SaManager.getSaTokenDao().delete(splicingTicketSaveKey(ticket)); SaManager.getSaTokenDao().deleteObject(splicingTicketSaveKey(ticket));
} }
/** /**
* 删除 Ticket索引 * 删除 Ticket索引
*
* @param client 应用标识
* @param loginId 账号id * @param loginId 账号id
*/ */
public void deleteTicketIndex(Object loginId) { public void deleteTicketIndex(String client, Object loginId) {
if(loginId == null) { if(loginId == null) {
return; 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码 * @param ticket Ticket码
* @return 账号id
*/ */
public void deleteTicketToClient(String ticket) { public TicketModel getTicket(String ticket) {
if(ticket == null) { if(SaFoxUtil.isEmpty(ticket)) {
return; 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 * @return 账号id
*/ */
public Object getLoginId(String ticket) { public Object getLoginId(String ticket) {
if(SaFoxUtil.isEmpty(ticket)) { TicketModel ticketModel = getTicket(ticket);
if(ticketModel == null) {
return 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 * @param loginId 账号id
* @return Ticket值 * @return Ticket值
*/ */
public String getTicketValue(Object loginId) { public String getTicketValue(String client, Object loginId) {
if(loginId == null) { if(loginId == null) {
return null; return null;
} }
return SaManager.getSaTokenDao().get(splicingTicketIndexKey(loginId)); return SaManager.getSaTokenDao().get(splicingTicketIndexKey(client, loginId));
} }
/** // /**
* 查询 ticket 关联的 client,如果 ticket 码无效则返回 null // * 查询 ticket 关联的 client,如果 ticket 码无效则返回 null
* @param ticket Ticket码 // * @param ticket Ticket码
* @return 账号id // * @return 账号id
*/ // */
public String getTicketToClient(String ticket) { // public String getTicketToClient(String ticket) {
if(SaFoxUtil.isEmpty(ticket)) { // if(SaFoxUtil.isEmpty(ticket)) {
return null; // return null;
} // }
return SaManager.getSaTokenDao().get(splicingTicketToClientSaveKey(ticket)); // return SaManager.getSaTokenDao().get(splicingTicketToClientSaveKey(ticket));
} // }
// //
/** /**
@@ -174,11 +194,15 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
public String createTicket(Object loginId, String client) { public String createTicket(Object loginId, String client) {
// 创建 Ticket // 创建 Ticket
String ticket = randomTicket(loginId); String ticket = randomTicket(loginId);
TicketModel ticketModel = new TicketModel();
ticketModel.setTicket(ticket);
ticketModel.setClient(client);
ticketModel.setLoginId(loginId);
// TODO ticketModel.setDeviceId();
// 保存 Ticket // 保存 Ticket
saveTicket(ticket, loginId); saveTicket(ticketModel);
saveTicketIndex(ticket, loginId); saveTicketIndex(client, ticket, loginId);
saveTicketToClient(ticket, client);
// 返回 Ticket // 返回 Ticket
return ticket; return ticket;
@@ -201,31 +225,32 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
*/ */
public Object checkTicket(String ticket, String client) { public Object checkTicket(String ticket, String client) {
// 读取 loginId // 读取 loginId
String loginId = SaManager.getSaTokenDao().get(splicingTicketSaveKey(ticket)); TicketModel ticketModel = getTicket(ticket);
if(ticketModel == null) {
if(loginId != null) { return 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);
} }
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; return loginId;
} }
@@ -333,7 +358,7 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
checkRedirectUrl(client, redirect); checkRedirectUrl(client, redirect);
// 删掉 旧Ticket // 删掉 旧Ticket
deleteTicket(getTicketValue(loginId)); deleteTicket(getTicketValue(client, loginId));
// 创建 新Ticket // 创建 新Ticket
String ticket = createTicket(loginId, client); String ticket = createTicket(loginId, client);
@@ -714,22 +739,27 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
return getStpLogic().getConfigOrGlobal().getTokenName() + ":ticket:" + ticket; return getStpLogic().getConfigOrGlobal().getTokenName() + ":ticket:" + ticket;
} }
/** // /**
* 拼接keyTicket 查 所属的 client // * 拼接keyTicket 查 所属的 client
* @param ticket ticket值 // * @param ticket ticket值
* @return key // * @return key
*/ // */
public String splicingTicketToClientSaveKey(String ticket) { // public String splicingTicketToClientSaveKey(String ticket) {
return getStpLogic().getConfigOrGlobal().getTokenName() + ":ticket-client:" + ticket; // return getStpLogic().getConfigOrGlobal().getTokenName() + ":ticket-client:" + ticket;
} // }
/** /**
* 拼接key:账号Id 反查 Ticket * 拼接key:账号Id 反查 Ticket
*
* @param client 应用标识
* @param id 账号id * @param id 账号id
* @return key * @return key
*/ */
public String splicingTicketIndexKey(Object id) { public String splicingTicketIndexKey(String client, Object id) {
return getStpLogic().getConfigOrGlobal().getTokenName() + ":id-ticket:" + id; if(SaFoxUtil.isEmpty(client) || SaSsoConsts.CLIENT_WILDCARD.equals(client)) {
client = SaSsoConsts.CLIENT_ANON;
}
return getStpLogic().getConfigOrGlobal().getTokenName() + ":id-ticket:" + client + ":" + id;
} }
} }
@@ -50,10 +50,11 @@ public class SaSsoUtil {
/** /**
* 删除 Ticket索引 * 删除 Ticket索引
* @param loginId 账号id * @param client 应用 id
* @param loginId 账号id
*/ */
public static void deleteTicketIndex(Object loginId) { public static void deleteTicketIndex(String client, Object loginId) {
SaSsoServerProcessor.instance.ssoServerTemplate.deleteTicketIndex(loginId); SaSsoServerProcessor.instance.ssoServerTemplate.deleteTicketIndex(client, loginId);
} }
/** /**
@@ -48,6 +48,9 @@ public class SaSsoConsts {
/** client 身份,* 代表通配,可以解析出所有 client 的 ticket */ /** client 身份,* 代表通配,可以解析出所有 client 的 ticket */
public static final String CLIENT_WILDCARD = "*"; public static final String CLIENT_WILDCARD = "*";
/** client 身份,代表匿名 client */
public static final String CLIENT_ANON = "anon";
/** SSO 模式1 */ /** SSO 模式1 */
public static final int SSO_MODE_1 = 1; public static final int SSO_MODE_1 = 1;
/** SSO 模式2 */ /** SSO 模式2 */