【问题标题】:impersonation in Exchange Web Service (EWS)Exchange Web 服务 (EWS) 中的模拟
【发布时间】:2014-03-20 22:51:57
【问题描述】:

我正在开发一个应用程序,用于在我的日历中创建约会并使用 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;我只是不能为每个将使用此应用程序的员工使用用户名/密码。我认为生产 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>

请帮我解决这个问题。

谢谢穆克什

【问题讨论】:

    标签: impersonation exchangewebservices


    【解决方案1】:

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

    然后,您需要像在示例中那样进行模拟。您不会使用 Windows 模拟。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-28
      • 2016-05-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多