Windows 用户从交换服务器获取 "access denied"

Windows User getting "access denied" from exchange server

我有一个 MVC Web 应用程序使用 Windows 身份验证和 Exchange Web 服务。在开发过程中,这非常有效,因为我的开发机器上 IIS 中的应用程序池在我的 windows 用户下设置为 运行,并且 Exchange Server 位于同一域中。

但是,在 Web 服务器上,我们所有的应用程序都设置为 运行,系统用户可以访问所有数据库服务器等。数据库连接使用集成安全性,因此我无法模拟用户在应用程序级别。

我一直在尝试通过如下代码冒充当前windows用户:

public abstract class ExchangeServiceImpersonator
{
    private static WindowsImpersonationContext _ctx;

    public Task<string> CreateMeetingAsync(string from, List<string> to, string subject, string body, string location, DateTime begin, DateTime end)
    {
        var tcs = new TaskCompletionSource<string>();
        EnableImpersonation();

        try
        {
            tcs.TrySetResult(CreateMeetingImpersonated(from, to, subject, body, location, begin, end));
        }
        catch(Exception e)
        {
            tcs.TrySetException(e);
        }
        finally
        {
            DisableImpersonation();
        }

        return tcs.Task;
    }

    public abstract string CreateMeetingImpersonated(string from, List<string> to, string subject, string body, string location, DateTime begin, DateTime end);

    private static void EnableImpersonation()
    {
        WindowsIdentity winId = (WindowsIdentity)HttpContext.Current.User.Identity;
        _ctx = winId.Impersonate();
    }

    private static void DisableImpersonation()
    {
        if (_ctx != null)
            _ctx.Undo();
    }
}

然后,实现抽象方法的class:

public class ExchangeServiceExtensionsBase : ExchangeServiceImpersonator
{
    private ExchangeService _service;

    public ExchangeService Service
    {
        get
        {
            if (this._service == null)
            {
                this._service = new ExchangeService(ExchangeVersion.Exchange2013);
                this._service.Url = new Uri(WebConfigurationManager.AppSettings["ExchangeServer"]);
                this._service.UseDefaultCredentials = true;
            }

            return this._service;
        }
        set { return; }
    }

    public override string CreateMeetingImpersonated(string from, List<string> to, string subject, string body, string location, DateTime begin, DateTime end)
    {
        //this.Service.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, from);

        Appointment meeting = new Appointment(Service);
        string meetingID = Guid.NewGuid().ToString();

        meeting.Subject = subject;
        meeting.Body = "<span style=\"font-family:'Century Gothic'\" >" + body.Replace(Environment.NewLine, "<br/>") + "<br/><br/>" +
            "<span style=\"color: white;\">Meeting Identifier: " + meetingID + "</span></span><br/><br/>";
        meeting.Body.BodyType = BodyType.HTML;
        meeting.Start = begin;
        meeting.End = end;
        meeting.Location = location;
        meeting.ReminderMinutesBeforeStart = 60;

        foreach (string attendee in to)
        {
            meeting.RequiredAttendees.Add(attendee);
        }
        meeting.Save(SendInvitationsMode.SendToAllAndSaveCopy);

        return meetingID;
    }
}

然后,访问方法如下:

public static class ExchangeServiceExtensions
{
    public static async Task<string> CreateMeetingAsync(string from, List<string> to, string subject, string body, string location, DateTime begin, DateTime end)
    {
        ExchangeServiceImpersonator serviceImpersonator = new ExchangeServiceExtensionsBase();
        return await serviceImpersonator.CreateMeetingAsync(from, to, subject, body, location, begin, end);
    }
}

这在我的本地开发机器上仍然有效,但无论我做什么,从服务器访问的用户总是被交换服务器拒绝访问:

The request failed. The remote server returned an error: (401) Unauthorized.

我试过使用默认凭据:

this._service.UseDefaultCredentials = true;

并尝试手动将凭据设置为当前(假设是模拟的)用户:

this._service.Credentials = new WebCredentials(CredentialCache.DefaultNetworkCredentials);

此外,我尝试使用 Exchange ImpersonatedUserId 对象使用电子邮件地址:

this._service.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, from);

其中returns以下异常:

The account does not have permission to impersonate the requested user.

默认情况下,作为一项安全措施,Windows 将阻止您将凭据从 Web 服务器委托给 Exchange。这意味着您不能冒充访问您网站的用户。

这被称为 "server double hop" 场景。第一个 "hop" 是从用户的机器到 web 服务器,第二个 "hop" 是从 web 服务器到 Exchange 服务器(Google 会在服务器双跳上给你很多点击).

这是一件好事,因为它可以防止任何黑客在您的服务器周围移动。

它在您的开发机器上运行的原因是只有一个 "hop" 从您的本地 Web 服务器到 Exchange 服务器。

要解决这个问题,您需要允许网络服务器将凭据委托给 Exchange 服务器。这称为 Kerberos 委派,必须由您的系统管理员以某种方式在 Active Directory 中设置(这超出了我的知识范围)。

我尝试将 AD 对象设置更改为 Trust this computer for delegation..(您需要 AD 管理员权限),但这并没有解决问题。

我的突破是将应用程序池的标识(高级设置...)设置为 NetworkService。它也适用于 LocalServiceLocalSystem,但要小心,因为它们具有更高的权限。 令我惊讶的是,当我输入实际上获得了交易系统所有权限的 AD 管理员帐户时,它不适用于 自定义帐户

关于我的申请的一般信息:

  • ASP.CORE 2.1 网络服务
  • Windows 服务器 2016
  • IIS 10.0.x
  • 公司内部网络