You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WebBroker-ISAPI模块为何仅在独立线程中可正常访问文件?

问题分析:ISAPI直接写入文件失败,线程中成功的原因

问题重现

使用Delphi 11.1 WebBroker开发ISAPI模块,尝试向Web应用目录写入日志文件:

写入文件的核心过程

procedure WriteMyFile;
var
  fname: string;
  fs: TFileStream;
begin
  fname := TPath.Combine(WebApplicationDirectory, 'myfile.log');
  fs := TFileStream.Create(fname, fmCreate or fmOpenWrite or fmShareDenyNone);
  fs.Write([$54,$65,$73,$74,$0D,$0A], 6); { 写入"Test"及换行符 }
  fs.Free;
end;

直接调用触发权限异常

在DefaultHandler动作中直接调用时,抛出访问被拒绝异常:

procedure TWebModule1.WebModule1DefaultHandlerAction(Sender: TObject;
  Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
  WriteMyFile;  //==> 异常:access denied!
  Response.Content := 'no error!';
end;

线程中调用功能正常

通过匿名线程调用时,可成功创建并写入文件:

procedure TWebModule1.WebModule1DefaultHandlerAction(Sender: TObject;
  Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var
  thr: TThread;
begin
  //WriteMyFile;  //==> 异常:access denied!

  thr := TThread.CreateAnonymousThread(WriteMyFile); // 正常执行,文件创建成功
  thr.FreeOnTerminate := False;
  thr.Start;
  thr.WaitFor;
  thr.Free;
  Response.Content := 'no error!';
end;

核心原因:IIS请求线程的身份模拟机制

这是IIS ISAPI的身份模拟特性导致的权限差异:

  1. 请求线程的身份:ISAPI模块处理HTTP请求时,IIS会自动让请求处理线程模拟当前请求的用户身份(默认是匿名用户IUSR,若开启认证则为对应认证用户)。你虽然给应用池账户IIS AppPool\MYPOOL配置了文件夹修改权限,但当前请求线程的身份是模拟用户,该用户未被授予目标文件夹的写入权限,因此直接调用会触发“访问被拒绝”。
  2. 新建线程的身份:通过TThread.CreateAnonymousThread创建的新线程,不会继承请求线程的模拟令牌,而是使用应用池的原始账户身份(即IIS AppPool\MYPOOL)运行。该账户已被配置文件夹修改权限,因此能成功完成文件操作。

验证与解决方向

  • 验证方法:在两种调用场景中分别获取当前线程的身份(调用Windows.GetUserNameAPI),可观察到直接调用时身份为IUSR,线程中为IIS AppPool\MYPOOL。
  • 解决方式:
    • 方式一:给模拟用户(如IUSR或网站认证用户)添加目标文件夹的修改权限。
    • 方式二:在代码中临时取消身份模拟,完成文件操作后恢复,示例代码:
      procedure WriteMyFile;
      var
        fname: string;
        fs: TFileStream;
        OldToken: THandle;
      begin
        // 保存当前模拟令牌并取消模拟
        if ImpersonateSelf(SecurityImpersonation) then
        begin
          OldToken := Windows.GetCurrentThreadToken;
          RevertToSelf;
          try
            fname := TPath.Combine(WebApplicationDirectory, 'myfile.log');
            fs := TFileStream.Create(fname, fmCreate or fmOpenWrite or fmShareDenyNone);
            fs.Write([$54,$65,$73,$74,$0D,$0A], 6);
            fs.Free;
          finally
            // 恢复之前的模拟身份
            if OldToken <> 0 then
              SetThreadToken(nil, OldToken);
            CloseHandle(OldToken);
          end;
        end;
      end;
      

内容的提问来源于stack exchange,提问作者headfime

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 12:22:43