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

View File

@@ -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 +
'}';
}
}

View File

@@ -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;
}
/**
* 拼接keyTicket 查 所属的 client
* @param ticket ticket值
* @return key
*/
public String splicingTicketToClientSaveKey(String ticket) {
return getStpLogic().getConfigOrGlobal().getTokenName() + ":ticket-client:" + ticket;
}
// /**
// * 拼接keyTicket 查 所属的 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;
}
}

View File

@@ -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);
}
/**

View File

@@ -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 */