会话与安全

文档会话和访问控制

Doconut 令牌功能强大:如果不绑定到打开会话,任何持有它的人都可以请求文档的每一页。本页解释了会话包含的内容、其存活时间以及 UseDoconut() 默认启用的检查。

文档会话包含的内容

每次成功调用 OpenDocumentAsync 都会在 IMemoryCache 中创建一个会话:

  • 已加载的 格式查看器(保存已解析文档的文档引擎实例),
  • 每页状态——用户在小部件中应用的旋转、翻转和注释数据,
  • 可选的 搜索索引,在首次搜索时惰性构建(或在页面从共享存储提供时从预构建的 .srh 文件加载),
  • 会话的 水印,来源于 DocOptions.Watermark。

生命周期

会话在 滑动窗口 上过期:DocOptions.TimeOut 分钟(默认 60),每次携带令牌的请求都会重置。当会话被驱逐——无论是因过期还是通过 CloseDocument(token)——其驱逐回调会立即释放文档引擎并释放相关内存。

csharp
// 短暂的会话用于一次性预览
var token = await viewer.OpenDocumentAsync(path, new DocOptions { TimeOut = 10 });

使用已过期令牌的请求会收到错误图片,显示 Document session not found. Please re-open document. —— 客户端必须重新打开文档以获取新的令牌。

内置令牌绑定

当 UnsafeMode = false(默认)时,OpenDocumentAsync 会将新令牌绑定到打开它的 HTTP 请求的 ASP.NET 会话,通过在该会话中写入 secure-{token} 标记实现。随后 Doconut 中间件会拒绝向任何其他浏览器会话提供页面:

  • 不同的浏览器/会话使用被盗令牌 → 错误图片 You Are Not Authorized To View This Page.
  • 未注册会话中间件 → HTTP 500 Session middleware not configured. Call UseSession() before UseDoconut().

这就是快速入门坚持在 Doconut 分支之前使用 AddSession() + app.UseSession() 的原因。两个实际后果:

  • 客户端必须在页面请求中发送 ASP.NET 会话 cookie。跨域设置剥离 cookie(或没有 cookie jar 的 API 客户端)会导致检查失败——这是一种功能,而非 bug。
  • options.UnsafeMode = true 会完全禁用绑定。它用于受控场景;在单节点生产环境中请保持 false。

注意拼写。之前的 .NET Standard 库将此属性称为 UnSafeMode 并传递给中间件;这里使用的是 UnsafeMode,并在 AddDoconut() 中仅设置一次。

令牌绑定仅由全局 UnsafeMode 开关控制——默认开启(UnsafeMode = false),适用于所有会话。没有针对单个文档的关闭选项;将 UnsafeMode = true 会全局禁用绑定。

访问授权和已认证用户

当 UnsafeMode 为 false 时,UseDoconut() 会自动在页面中间件之前插入文档访问中间件。不要二次注册。当请求携带令牌时,它会查找文档打开时记录的 访问授权,仅在以下全部满足时才授权:

  1. 令牌对应的授权存在,
  2. 授权未过期(授权生命周期等于文档的 TimeOut),
  3. 请求的 ASP.NET 会话 ID 与打开文档时的会话 ID 匹配,
  4. 如果打开者已认证,请求用户的 NameIdentifier 声明也必须匹配。

失败时返回 403 —— 对于页面/缩略图请求返回 PNG 错误图片,其他情况返回纯文本。消息和令牌查询键来源于 DocumentSecurityOptions(TokenQueryKey,默认 "token";UnauthorizedMessage,默认 "You Are Not Authorized To View This Page.")。在构建应用之前通过 ASP.NET Core DI 配置这些选项。如果会话状态不可用,中间件会以 HTTP 500 关闭并返回:ASP.NET Session is required for Doconut document security.

csharp
builder.Services.Configure<Doconut.Security.DocumentSecurityOptions>(options =>
{
    options.TokenQueryKey = "token";
    options.UnauthorizedMessage = "You Are Not Authorized To View This Page.";
});

核心页面中间件在提供文档之前会验证 secure-{token} 会话标记。若 UnsafeMode = true,UseDoconut() 会跳过访问中间件,核心标记检查也随之禁用。

跨多个节点的安全性

会话绑定的令牌假设提供页面的节点也曾处理打开文档的请求。当多个节点从共享存储提供相同文档时,这一假设不成立:页面请求可能落在从未看到打开请求的节点上,进而被每会话检查拒绝。

解决方案不是让文档无保护。通过 AddDoconutDistributedDocumentSecurity(...) 使用签名访问票据,它用每个节点都能独立验证的凭证替代会话检查。完整方案请参见 分布式部署。

撤销

CloseDocument(token) 不仅释放内存——还会移除 secure-{token} 标记并撤销访问授权,因此关闭的令牌会立即在两个安全层面失效。

生产环境检查清单

  • 在单节点上保持 UnsafeMode = false(默认)——此全局开关负责将令牌绑定到会话。
  • 注册 AddDistributedMemoryCache()(MVC 和 Razor Pages 应用已默认包含)和 AddSession(),并在 Doconut 中间件分支之前调用 app.UseSession()。
  • 确保会话 cookie 策略允许小部件的请求携带 cookie(SameSite、HTTPS)。
  • 用户离开文档时使用 CloseDocument —— 可同时节省内存并提升安全性。
  • 切勿记录或共享令牌;将其视为短期凭证。
  • 从共享存储提供服务?请改用签名访问票据,而不是接受未受保护的文档。

此页面对您有帮助吗?