Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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;             // 多线程保护锁
};

设计要点

  1. std::vector存储:用vector动态管理Session
  2. Mutex保护:多线程环境下需要锁保护
  3. 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管理模块。