我有一个MVC Web应用程序使用Windows身份验证和Exchange Web服务。在开发过程中,这很有效,因为我的开发机器上的IIS中的应用程序池设置为在我的Windows用户下运行,并且Exchange Server位于同一域中。
但是,在Web服务器上,我们的所有应用程序都设置为在可以访问所有数据库服务器等的系统用户下运行。数据库连接使用集成安全性,因此我无法在应用程序级别模拟用户。 / p>
我一直在尝试通过代码模拟当前的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();
}
}
然后,实现抽象方法的类:
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);
}
}
这仍然可以在我的本地开发机器上运行,但无论我做什么,从服务器访问的用户都不断从交换服务器获取访问权限:
请求失败。远程服务器返回错误:(401)未经授权。
我尝试将其保留为默认凭据:
this._service.UseDefaultCredentials = true;
尝试手动将凭据设置为当前(假设模拟的)用户:
this._service.Credentials = new WebCredentials(CredentialCache.DefaultNetworkCredentials);
另外,我尝试使用电子邮件地址
来使用ExchangeImpersonatedUserId
对象
this._service.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, from);
返回以下异常:
该帐户无权模拟所请求的用户。
答案 0 :(得分:1)
默认情况下,作为安全措施,Windows将阻止您将凭据从Web服务器委派给Exchange。这意味着您无法模拟访问您网站的用户。
这称为“服务器双跃点”方案。第一个“跳”是从用户的机器到Web服务器,第二个“跳”是从Web服务器到Exchange服务器(Google会在服务器双跳上给你很多点击量)。
这是一件好事,因为它可以防止任何黑客在您的服务器周围移动。
它在您的开发机器上工作的原因是从本地Web服务器到Exchange服务器只有一个“跳”。
要解决此问题,您需要允许Web服务器将凭据委派给Exchange服务器。这称为Kerberos委派,必须由系统管理员以某种方式在Active Directory中设置(我不知道)。
答案 1 :(得分:0)