4.6 Session管理模块:跟踪每一次交互
一个问题:Session如何被跟踪?
假设你刚刚理解了SlotManager的“注册中心“角色。
现在,应用程序调用C_OpenSession,打开了一个Session。
你可能会想:
- Session对象怎么被存储?
- 多个Session怎么被管理?
- 登录状态怎么被共享?
更深层的问题是:
Session就像“窗口“,Window Manager如何管理所有窗口?
“窗口调度员“的设计意图
SessionManager就像服务中心的窗口调度员:
窗口调度员类比:
服务中心窗口调度员(SessionManager)
│
├── 职责:
│ ├── 分配窗口编号(分配Session Handle)
│ ├── 记录窗口状态(记录Session状态)
│ ├── 管理窗口登录状态(管理登录状态)
│ ├── 关闭窗口时清理(清理Session资源)
│ └── 关闭所有窗口(closeAllSessions)
│ │
│ ├── 窗口编号(Session Handle):
│ │ ├── 每个窗口有一个编号
│ │ ├── 编号用于后续操作
│ │ └── 编号是"不透明"的(客户不理解内部含义)
│ │
│ ├── 窗口状态(Session State):
│ │ ├── 窗口类型:只读/读写
│ │ ├── 登录状态:未登录/User登录/SO登录
│ │ ├── 当前操作:签名/加密/查找...
│ │ └── 绑定Slot:窗口在哪个柜台
│ │
│ ├── 登录状态共享:
│ │ ├── 同一客户的所有窗口共享登录状态
│ │ ├── 在一个窗口登录,其他窗口也登录
│ │ └── 防止绕过认证
│ │
│ └── 窗口生命周期:
│ │ ├── 打开窗口:openSession
│ │ ├── 使用窗口:各种操作
│ │ ├── 关闭窗口:closeSession
│ │ └── Session Object随窗口关闭消失
│ │
└── 关键设计:
├── 用vector/map存储Session
├── 用Mutex保护并发访问
├── 登录状态全局共享
└── 窗口关闭时清理Session Object
SessionManager的作用
SessionManager负责管理所有Session:
- 创建Session:C_OpenSession时创建Session对象
- 销毁Session:C_CloseSession时销毁Session对象
- Session查询:根据Handle查找Session对象
- Session状态:维护Session的登录状态、读写状态
SessionManager类的定义
从SessionManager.h可以看到核心接口:
class SessionManager
{
public:
SessionManager();
virtual ~SessionManager();
// Session操作
CK_RV openSession(Slot* slot, CK_FLAGS flags,
CK_VOID_PTR pApplication, CK_NOTIFY Notify,
CK_SESSION_HANDLE_PTR phSession);
CK_RV closeSession(CK_SESSION_HANDLE hSession);
CK_RV closeAllSessions(Slot* slot);
// Session查询
CK_RV getSessionInfo(CK_SESSION_HANDLE hSession, CK_SESSION_INFO_PTR pInfo);
Session* getSession(CK_SESSION_HANDLE hSession);
bool haveSession(CK_SLOT_ID slotID);
bool haveROSession(CK_SLOT_ID slotID);
private:
// Session存储
std::vector<Session*> sessions; // Session数组
Mutex* sessionsMutex; // 多线程保护锁
};
设计要点:
- std::vector存储:用vector动态管理Session
- Mutex保护:多线程环境下需要锁保护
- Session Handle映射:Handle是索引,指向vector中的Session
openSession的实现流程
CK_RV SessionManager::openSession(Slot* slot, CK_FLAGS flags,
CK_VOID_PTR pApplication, CK_NOTIFY Notify,
CK_SESSION_HANDLE_PTR phSession)
{
// 1. 检查参数
if (slot == NULL || phSession == NULL) {
return CKR_ARGUMENTS_BAD;
}
// 2. 加锁保护
MutexLocker lock(sessionsMutex);
// 3. 创建Session对象
Session* session = new Session(slot, flags, pApplication, Notify);
// 4. 添加到vector
sessions.push_back(session);
// 5. 返回Handle(教学简化:使用vector索引,实际SoftHSM2通过HandleManager统一分配递增句柄)
*phSession = sessions.size() - 1;
return CKR_OK;
}
Handle的本质(教学简化):
Session Handle本质上是一个整数索引:
- Handle = vector中的位置
- getSession通过Handle找到vector中的Session对象
Session* SessionManager::getSession(CK_SESSION_HANDLE hSession)
{
MutexLocker lock(sessionsMutex);
// Handle是vector索引
if (hSession >= sessions.size()) {
return NULL;
}
return sessions[hSession];
}
closeSession的实现流程
CK_RV SessionManager::closeSession(CK_SESSION_HANDLE hSession)
{
MutexLocker lock(sessionsMutex);
// 1. 检查Handle有效性
if (hSession >= sessions.size()) {
return CKR_SESSION_HANDLE_INVALID;
}
Session* session = sessions[hSession];
if (session == NULL) {
return CKR_SESSION_HANDLE_INVALID;
}
// 2. 销毁Session对象
delete session;
// 3. 从vector中移除(设置NULL,不删除位置)
sessions[hSession] = NULL;
return CKR_OK;
}
为什么不删除vector位置?
如果删除vector位置,后面的Session Handle会改变。PKCS#11要求Handle稳定不变,所以采用“设置NULL“的方式,保留位置。
Session类的内部结构
Session类维护Session的完整状态:
class Session
{
public:
Session(Slot* slot, CK_FLAGS flags, CK_VOID_PTR pApplication, CK_NOTIFY Notify);
~Session();
// Session状态
CK_STATE getState();
CK_SLOT_ID getSlotID();
// 登录状态
CK_USER_TYPE getLoggedInUser();
CK_RV login(CK_USER_TYPE userType, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen);
CK_RV logout();
// Object管理
ObjectStore* getObjectStore();
// 密码操作状态
void setOperation(OperationType op, ...);
void clearOperation();
private:
Slot* slot; // 所属Slot
CK_FLAGS flags; // Session标志(读/写)
CK_STATE state; // Session状态
CK_USER_TYPE loggedInUser; // 登录用户类型
ObjectStore* objectStore; // Session Object存储
Operation* currentOperation; // 当前密码操作
};
Session状态转换
Session状态转换:
C_OpenSession
│
↓
CKS_RO_PUBLIC_SESSION 或 CKS_RW_PUBLIC_SESSION
│
│ C_Login(CKU_USER)
↓
CKS_RO_USER_FUNCTIONS 或 CKS_RW_USER_FUNCTIONS
│
│ C_Login(CKU_SO)(仅R/W Session)
↓
CKS_RW_SO_FUNCTIONS
│
│ C_Logout
↓
CKS_RO_PUBLIC_SESSION 或 CKS_RW_PUBLIC_SESSION
状态定义:
├── CKS_RO_PUBLIC_SESSION:只读,未登录
├── CKS_RW_PUBLIC_SESSION:读写,未登录
├── CKS_RO_USER_FUNCTIONS:只读,User登录
├── CKS_RW_USER_FUNCTIONS:读写,User登录
└── CKS_RW_SO_FUNCTIONS:读写,SO登录
并发Session的管理
PKCS#11允许一个Token有多个Session:
/* Token信息中的Session限制 */
CK_TOKEN_INFO tokenInfo;
rv = C_GetTokenInfo(slotID, &tokenInfo);
// 最大Session数
CK_ULONG maxSessions = tokenInfo.ulMaxSessionCount;
// 当前Session数
CK_ULONG currentSessions = tokenInfo.ulSessionCount;
SessionManager的并发控制:
// MutexLocker确保线程安全
MutexLocker lock(sessionsMutex);
// 检查Session数量限制
if (sessions.size() >= MAX_SESSIONS) {
return CKR_SESSION_COUNT;
}
一个完整的Session生命周期
/* Session生命周期示例 */
// 1. 创建Session
CK_SESSION_HANDLE hSession;
CK_RV rv = sessions->openSession(slot, CKF_SERIAL_SESSION | CKF_RW_SESSION,
NULL_PTR, NULL_PTR, &hSession);
// 2. 使用Session
Session* session = sessions->getSession(hSession);
// 查询状态
CK_SESSION_INFO info;
session->getSessionInfo(&info);
// info.state = CKS_RW_PUBLIC_SESSION
// 登录
session->login(CKU_USER, pin, pinLen);
// state变为CKS_RW_USER_FUNCTIONS
// 执行密码操作
session->setOperation(OP_SIGN, hKey, mechanism);
// ...
// 3. 销毁Session
sessions->closeSession(hSession);
本篇小结
SessionManager是SoftHSM2的Session管理核心:
核心功能:
- openSession:创建Session,返回Handle
- closeSession:销毁Session
- getSession:根据Handle查找Session
设计要点:
- std::vector存储Session对象
- Handle是vector索引
- Mutex保护多线程访问
- closeSession时设置NULL,保留位置
Session类:
- 维护Session状态(读/写、登录)
- 管理Session Object
- 记录当前密码操作
状态转换:
- 未登录 → User登录 → SO登录(仅R/W)
- logout回到未登录
下一节,我们将分析Handle管理模块——Session Handle和Object Handle如何统一管理。
【下集预告】
Handle如何分配和回收?
Session关闭时Object如何清理?
车载HSM的Handle管理与软件实现有何不同?
下一节,Handle管理模块。