跳转到内容

Session 与请求状态

HTTP 请求天然是无状态的,但很多业务并不是:登录后的用户标识、一次向导流程里的临时选择、后台系统里的少量上下文,都需要在多次请求之间保留。

Feat Cloud 的 Session 解决的就是这种“小而短”的请求状态。它不是默认认证架构,也不应该替代数据库、缓存或权限系统;它更像一层贴近 HTTP 的会话能力,让 Controller 可以在需要时读写当前访问者的状态。

先看 Session 如何进入 Controller,以及什么内容适合放进会话里。

Controller 方法声明 Session 参数后,Feat Cloud 会把当前请求对应的会话对象传入方法。下面的接口用它记录访问次数:

UserController.java
import tech.smartboot.feat.cloud.annotation.Controller;
import tech.smartboot.feat.cloud.annotation.RequestMapping;
import tech.smartboot.feat.cloud.annotation.RequestMethod;
import tech.smartboot.feat.core.server.Session;
@Controller
public class UserController {
@RequestMapping(value = "/visit", method = RequestMethod.GET)
public String visitCount(Session session) {
String count = session.get("visitCount");
int visits = (count == null ? 0 : Integer.parseInt(count)) + 1;
session.put("visitCount", String.valueOf(visits));
return "Visit count: " + visits;
}
}

第一次访问时,Feat 会创建会话并通过 Cookie 把会话 ID 返回给客户端。之后客户端带着同一个 Cookie 再请求,session.get("visitCount") 就能读到前一次写入的值。

Session 适合保存能用字符串表达、体积很小、过期后可重新建立的状态,例如:

  • 用户 ID
  • 用户名
  • 简单角色信息
  • 访问计数或短期状态

不要把下面这些内容放进 Session:

  • 大对象
  • 敏感明文数据
  • 需要长期可靠持久化的业务信息
  • 复杂权限模型或用户完整资料

Session 的生命周期跟访问者和过期时间相关,而不是跟业务数据的生命周期相关。订单、账户、审计记录这类信息应该进入数据库;访问者当前是谁、短时间内走到哪一步,才是 Session 的职责范围。

Session 的风险通常不在 API 本身,而在存储位置、数据大小和认证模型边界。

Feat Cloud 会在编译期根据 feat.yml 生成 SessionManager 的初始化代码。默认情况下使用本地内存 Session;当配置为 Redis 存储时,会生成 ClusterSessionManager,让多实例共享同一份会话状态。

本地 Session 适合开发环境、单机部署和简单应用。它最快,也最少依赖,但应用重启后会话会丢失,多实例之间也不能共享。

Redis Session 适合多实例部署、容器平台和需要跨节点保持登录态的场景。它引入了 Redis 依赖,但能让不同应用实例读写同一份会话。

feat.yml
server:
session:
timeout: 1800

上面只设置过期时间,仍然使用本地 Session。单位为秒。

如果要使用 Redis Session,需要同时声明 Session 存储类型和 Redis 连接信息:

feat-prod.yml
server:
session:
store-type: redis
timeout: 1800
feat:
redis:
host: redis.example.com
port: 6379

这类配置属于编译期输入。修改 feat.ymlfeat-prod.yml 后,需要重新构建应用,生成代码才会更新。多环境项目里,本地环境可以继续用内存 Session,生产环境再通过 feat-prod.yml 覆盖为 Redis Session。

Session 常用操作
session.put("username", "john");
String username = session.get("username");
session.setTimeout(1800);
String sessionId = session.getSessionId();
session.invalidate();

putget 读写字符串;setTimeout 设置当前会话的过期时间;invalidate 清理会话并移除客户端 Cookie。

如果你的应用已经开始走向下面这些方向:

  • 前后端分离
  • Token 认证
  • 多端登录
  • 更复杂的权限和身份体系

那就不要把 Session 当成认证体系的唯一中心。更稳的做法通常是:身份认证交给 Token 或专门的认证服务,用户资料和权限放在数据库或缓存里,Session 只保留少量与当前 HTTP 会话有关的临时状态。

Session 最适合让简单应用保持简单。一旦部署拓扑、登录模型和权限边界开始复杂,优先重新审视状态划分,而不是继续往 Session 里塞更多内容。