Phiên & Bảo mật

Phiên tài liệu và kiểm soát truy cập

Một token Doconut xác định một tài liệu đang mở. Nếu bất kỳ yêu cầu nào mang token này được phục vụ, người có được nó có thể đọc mọi trang. Trang này giải thích một phiên tài liệu chứa gì, thời gian tồn tại của nó, và cách DocImage.axd quyết định ai có thể xem các trang của nó — trên một máy chủ và trong một web farm.

Những gì một phiên tài liệu chứa

Mỗi lần gọi thành công OpenDocumentAsync tạo ra một phiên, được lưu trong bộ nhớ:

  • trình xem định dạng đã tải — thể hiện engine giữ tài liệu đã phân tích,
  • trạng thái theo trang — xoay, lật, và dữ liệu chú thích mà người dùng áp dụng trong widget,
  • chỉ mục tìm kiếm (tùy chọn), được xây dựng khi thực hiện tìm kiếm đầu tiên (hoặc tải từ tệp .srh đã được tạo sẵn),
  • đánh dấu nước của phiên từ DocOptions.Watermark.

Thời gian tồn tại

Các phiên hết hạn dựa trên cửa sổ trượt: DocOptions.TimeOut phút (mặc định 60), được đặt lại bởi mỗi yêu cầu trình bày token. Khi một phiên kết thúc — do hết thời gian hoặc bởi CloseDocument(token) — engine tài liệu của nó được giải phóng và bộ nhớ được giải phóng ngay lập tức.

csharp
// Phiên ngắn hạn cho việc xem trước một lần
var token = await viewer.OpenDocumentAsync(path, new DocOptions { TimeOut = 10 });

Một yêu cầu với token đã hết hạn sẽ nhận được hình ảnh lỗi ghi Document session not found. Please re-open document. Trang phải mở lại tài liệu để nhận token mới. Trên một máy chủ đơn, việc tái tạo pool ứng dụng cũng sẽ kết thúc mọi phiên.

Các trang chỉ được phục vụ cho phiên đã mở chúng

Với UnsafeMode = false — mặc định — việc mở một tài liệu cấp quyền truy cập vào phiên ASP.NET của yêu cầu đã mở nó, bằng cách ghi một mục secure-{token} vào phiên đó. DocImage.axd yêu cầu trạng thái phiên và chỉ phục vụ một trang khi phiên yêu cầu chứa mục đó. Bất kỳ trình duyệt nào khác trình bày cùng token sẽ nhận được hình ảnh lỗi ghi You Are Not Authorized To View This Page.

csharp
Doconut.DoconutHost.Initialize(options =>
{
    // false (mặc định): DocImage.axd chỉ phục vụ một trang cho phiên ASP.NET đã mở
    // tài liệu. Trình xử lý yêu cầu trạng thái phiên; nó chỉ cần site không
    // tắt trạng thái phiên trong Web.config.
    options.UnsafeMode = false;
});

Ba hậu quả thực tế:

  • Endpoint mở cần trạng thái phiên. Các controller MVC và trang Web Forms có trạng thái phiên theo mặc định. Một IHttpHandler tùy chỉnh không có trừ khi nó triển khai System.Web.SessionState.IRequiresSessionState. Nếu một lần mở chạy mà không có phiên, không có gì để cấp quyền, vì vậy viewer hiển thị “You Are Not Authorized To View This Page.” trên mọi trang.
  • Trạng thái phiên phải luôn bật. <sessionState mode="Off" /> trong Web.config vô hiệu hoá cả việc cấp quyền và kiểm tra, và mọi trang sẽ bị từ chối.
  • Trình duyệt phải gửi cookie phiên. Các yêu cầu của widget là cùng nguồn, vì vậy chúng tự động mang ASP.NET_SessionId. Một client không có cookie jar — một script, một cuộc gọi server‑to‑server, một thiết lập cross‑origin mà loại bỏ cookie — sẽ bị từ chối. Đó là tính năng đang hoạt động.

UnsafeMode = true tắt kiểm tra: bất kỳ yêu cầu nào mang token đều được phục vụ. Đây là một công tắc toàn cục cho mọi tài liệu, không có ghi đè theo tài liệu. Để nó là false.

Thu hồi

CloseDocument(token) làm nhiều hơn việc giải phóng bộ nhớ: nó loại bỏ mục secure-{token} khỏi phiên của yêu cầu hiện tại và thu hồi token, vì vậy một token đã đóng sẽ bị từ chối ngay lập tức.

Web farms: vé ký

Trong một farm, yêu cầu tiếp theo có thể đến một máy chủ chưa bao giờ thấy tài liệu, trong một tiến trình worker khác, với phiên ASP.NET riêng của nó. Quyền phiên không thể theo nó. Chế độ phân tán thay thế bằng một vé ký gắn với trình duyệt: mỗi node chia sẻ một kho lưu cho các phiên tài liệu và một khóa ký, và DocImage.axd chấp nhận yêu cầu khi vé của nó hợp lệ cho token đó, trình duyệt đó, và — khi người dùng đã đăng nhập — người dùng đó.

Cấu hình, giống nhau trên mọi node:

csharp
// Global.asax.cs - Application_Start
Doconut.DoconutHost.Initialize(
    options => options.UnsafeMode = false,     // kiểm tra vé chỉ chạy khi giá trị này là false
    services => services.AddDoconutDistributed(d =>
    {
        d.Store             = CloudLocation.FileShare;
        d.FileShareRootPath = @"\\fileserver\doconut-shared";  // thư mục mà mọi node đều truy cập được
        d.SigningKey        = farmKey;                          // ít nhất 32 byte, CÙNG MỘT trên mọi node
    }));

Endpoint mở sau đó gắn vé vào trình duyệt trước khi mở, và trả về vé cùng với token:

  • đặt DocOptions.BrowserId = DoconutFarmBinding.EnsureBrowserId(System.Web.HttpContext.Current, securityOptions) — nó phát hành cookie doconut-client mà vé được gắn vào — và DocOptions.UserId = DoconutFarmBinding.ResolveUserId(System.Web.HttpContext.Current);
  • sau OpenDocumentAsync, trả về viewer.AccessToken cùng với token;
  • trong trang, truyền cả hai cho widget: objViewer.View(token, access).

Doconut.MvcSample.WebFarm trong gói mẫu là một node farm đầy đủ, có thể chạy, và hướng dẫn di chuyển bên cạnh nó giải thích từng bước.

Giữ khóa ký khỏi hệ thống kiểm soát nguồn: nó vừa ký các vé vừa mã hoá tài liệu trong kho chia sẻ. Một node có khóa khác sẽ từ chối mọi vé mà các node khác phát hành.

Kiểm tra một farm với trình duyệt thực, hoặc một script giữ cookie. Một client không có cookie doconut-client sẽ không nhận được phản hồi 401: nó nhận hình ảnh lỗi “not authorized” với trạng thái 200, khoảng 2 KB, trong khi một trang thực tế có kích thước hàng chục đến hàng trăm kilobyte.

Danh sách kiểm tra sản xuất

  • Giữ UnsafeMode = false (mặc định).
  • Mở tài liệu từ các yêu cầu có trạng thái phiên, và không bao giờ tắt trạng thái phiên.
  • Đảm bảo cookie phiên tới được DocImage.axd (cùng nguồn, HTTPS, chính sách SameSite cho phép).
  • Trong một farm, sử dụng chế độ phân tán với một khóa ký được chia sẻ bởi mọi node.
  • Gọi CloseDocument khi người dùng rời tài liệu — cả bộ nhớ và bảo mật đều được cải thiện.
  • Không bao giờ ghi log hoặc chia sẻ token; coi chúng như thông tin xác thực ngắn hạn.

Trang này có hữu ích không?