Exchange Web服务(EWS)中的模拟

时间:2014-02-20 06:47:28

标签: impersonation exchangewebservices

我正在开发一个用于在我的日历中创建约会的应用程序,并使用asp.net 4.0 Web应用程序在我们公司的Exchange 2013服务器上发送邮件。我得到如下错误:

无法将“System.Security.Principal.GenericIdentity”类型的对象强制转换为“System.Security.Principal.WindowsIdentity”。

我的LogonUserA(userName,domain,password,LOGON32_LOGON_INTERACTIVE,LOGON32_PROVIDER_DEFAULT,ref token)函数始终返回0.

我正在使用service.UseDefaultCredentials = true;我无法为将要使用此应用程序的每位员工使用用户名/ paasword。我认为生产Web应用程序服务器(Windows 2008 m / c)存在一些问题(权限/权限/禁用模拟)。

这些是代码文件:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;


using Microsoft.Exchange.WebServices.Data;
using Microsoft.Exchange.WebServices.Autodiscover;
using System.Web.Configuration;

//for impersonation before making calls
using System.Security.Principal;
using System.Web.Security;
using System.Runtime.InteropServices;

namespace MvcApplication1
{
    public partial class Test1 : System.Web.UI.Page
    {
         protected ExchangeService service;

        //The following Impersonator*** variables are of the exchange account which has been configured to impersonate other users by enabling impersonation on the exchange server as they show at this link: http://msdn.microsoft.com/en-us/library/office/bb204095(v=exchg.140).aspx
        protected string ImpersonatorUsername = WebConfigurationManager.AppSettings["ImpersonatorUsername"];
        protected string ImpersonatorPassword =WebConfigurationManager.AppSettings["ImpersonatorPassword"];
        protected string ImpersonatorDomain = WebConfigurationManager.AppSettings["ImpersonatorDomain"];


        // This is for the user for whom the appointment need to be set on their exchange server. This user will be impersonated by the above impersonator. You do not need to get the password information for this user, just the email address will work.
        private string Username ="mtyagi@talygen.local";// HttpContext.Current.User.Identity.Name.Split('\\').Last(); //extract the username out of the "Domain\Username" format. It doesn't have to be the currently logged in user. As per your need you can use the username of any other company user for whom you know the email address.
        protected string ImpersonatedEmailAddress;//= Username +"@"+ WebConfigurationManager.AppSettings["EmailDomain"];

        //start impersonation setup block. Credits: Impersonate a Specific User in Code http://support.microsoft.com/kb/306158#4
        public const int LOGON32_LOGON_INTERACTIVE = 2;
        public const int LOGON32_PROVIDER_DEFAULT = 0;

        WindowsImpersonationContext impersonationContext;

        [DllImport("advapi32.dll")]
        public static extern int LogonUserA(String lpszUserName,
            String lpszDomain,
            String lpszPassword,
            int dwLogonType,
            int dwLogonProvider,
            ref IntPtr phToken);
        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern int DuplicateToken(IntPtr hToken,
            int impersonationLevel,
            ref IntPtr hNewToken);

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool RevertToSelf();

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        public static extern bool CloseHandle(IntPtr handle);
        //end impersonation setup block;


        protected void Page_Load(object sender, EventArgs e)
        {
            ImpersonatedEmailAddress = Username;// + "@" + WebConfigurationManager.AppSettings["EmailDomain"]; //form the email address out of the username, provided they both are same

            service = new ExchangeService(ExchangeVersion.Exchange2013);
            //service.UseDefaultCredentials = true;
            service.Credentials = new WebCredentials(ImpersonatorUsername, ImpersonatorPassword, ImpersonatorDomain);
            service.Url = new Uri("https://exchange.talygen.com/EWS/exchange.asmx");//new Uri(WebConfigurationManager.AppSettings["EWSURL"]);
            service.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, ImpersonatedEmailAddress);

            SetAppointment("Test", DateTime.Now, "Test");

        }

        public void SetAppointment(string Subject, DateTime AptDateTime, string Body)
        {
            Appointment apt = new Appointment(service);
            apt.Subject = Subject;
            apt.Body = Body;
            apt.Body.BodyType = BodyType.HTML;
            apt.Start = AptDateTime;
            apt.End = apt.Start.AddMinutes(30.00);
            apt.ReminderMinutesBeforeStart = 15;
            apt.IsReminderSet = true;


            if (impersonateValidUser(ImpersonatorUsername, ImpersonatorDomain, ImpersonatorPassword)) //For this code to work you will have to enable impersonation on the Exchange server. This code works on the web application running on the company server, but not from my XP PC that is not part of the domain but is on VPN connection.
            {
                HttpContext.Current.Trace.Write("Before Saving Appointment. System.Security.Principal.WindowsIdentity.GetCurrent().Name = " + System.Security.Principal.WindowsIdentity.GetCurrent().Name);

                apt.Save(SendInvitationsMode.SendToNone);

                HttpContext.Current.Trace.Write("After Saving Appointment.");

                Label1.Text = String.Format("Appointment set successfully for {0}", ImpersonatedEmailAddress);

            }

            else //fall back to the code that uses logged in user's window identity and not impersonation. This code "strangely" worked from the web application installed on my Windows XP PC that was not part of the domain but was on VPN connection and yet saved appointments on the company's exchange server. I guess, the VPN connection compensates for all the mumbo-jumbo round about impersonation code in the impersonateValidUser method. Hack, this code worked even I had not configured the impersonation on the exchange server as they tell you to do at this link:  http://msdn.microsoft.com/en-us/library/office/bb204095(v=exchg.140).aspx
            {
                service.Credentials = null;
                service.ImpersonatedUserId = null;
                service.UseDefaultCredentials = true;

                HttpContext.Current.Trace.Write("Before Impersonation: System.Security.Principal.WindowsIdentity.GetCurrent().Name = " + System.Security.Principal.WindowsIdentity.GetCurrent().Name);

                //this is not impersonation. It uses the logged in user's window identity. The window identity does not have to be that of the company domain. The windows identity of Local PC that is not part of the domain will also work
                System.Security.Principal.WindowsImpersonationContext impersonationContext;
                impersonationContext = ((System.Security.Principal.WindowsIdentity)HttpContext.Current.User.Identity).Impersonate();// //System.Threading.Thread.CurrentPrincipal.Identity

                HttpContext.Current.Trace.Write("Before Saving Appointment. System.Security.Principal.WindowsIdentity.GetCurrent().Name = " + System.Security.Principal.WindowsIdentity.GetCurrent().Name);

                apt.Save(SendInvitationsMode.SendToNone);

                impersonationContext.Undo();

            }

        }

        //impersonation methods. Credit: Impersonate a Specific User in Code: http://support.microsoft.com/kb/306158#4
        private bool impersonateValidUser(String userName, String domain, String password)
        {
            WindowsIdentity tempWindowsIdentity;
            IntPtr token = IntPtr.Zero;
            IntPtr tokenDuplicate = IntPtr.Zero;

            if (RevertToSelf())
            {
                if (LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE,
                    LOGON32_PROVIDER_DEFAULT, ref token) != 0)
                {
                    if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                    {
                        tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                        impersonationContext = tempWindowsIdentity.Impersonate();
                        if (impersonationContext != null)
                        {
                            CloseHandle(token);
                            CloseHandle(tokenDuplicate);
                            return true;
                        }
                    }
                }
            }
            if (token != IntPtr.Zero)
                CloseHandle(token);
            if (tokenDuplicate != IntPtr.Zero)
                CloseHandle(tokenDuplicate);
            return false;
        }
        private void undoImpersonation()
        {
            impersonationContext.Undo();
        }
    }
    }

我的网络配置代码如下:

<?xml version="1.0" encoding="utf-8"?>
<!--
  For more information on how to configure your ASP.NET application, please visit
  http://go.microsoft.com/fwlink/?LinkId=169433
  -->
<configuration>
  <configSections>
    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />`enter code here
  </configSections>
  <connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=aspnet-MvcApplication1-20140210213058;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\aspnet-MvcApplication1-20140210213058.mdf" providerName="System.Data.SqlClient" />
  </connectionStrings>
  <appSettings>
    <add key="webpages:Version" value="2.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="PreserveLoginUrl" value="true" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
    <!--<authentication mode="Forms">
      <forms loginUrl="~/Account/Login" timeout="2880" />
    </authentication>-->
    <authentication mode="Windows" />
    <identity impersonate="true" />
    <pages>
      <namespaces>
        <add namespace="System.Web.Helpers" />
        <add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Ajax" />
        <add namespace="System.Web.Mvc.Html" />
        <add namespace="System.Web.Optimization" />
        <add namespace="System.Web.Routing" />
        <add namespace="System.Web.WebPages" />
      </namespaces>
    </pages>
  </system.web>
  <system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
    <handlers>
      <remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
      <remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
      <add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
  </system.webServer>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
  </entityFramework>
</configuration>

请帮我解决这个问题。

感谢Mukesh

1 个答案:

答案 0 :(得分:1)

您的asp.net 4.0 Web应用程序需要在对邮箱用户具有 Exchange模拟权限的服务帐户下运行。假设每个人都在同一个Exchange组织中,您需要为您的服务帐户运行New-ManagementRoleAssignment cmdlet至grant impersonation权限。

然后你需要像在你的例子中那样进行模仿。您不会使用Windows模拟。