C# EWS连接出现未经授权(401)错误

C# EWS连接出现未经授权(401)错误,c#,.net,exchangewebservices,C#,.net,Exchangewebservices,我一直在开发一个程序,该程序可以扫描exchange收件箱中来自指定地址的特定电子邮件。目前,该程序读取收件箱,下载附件,并将电子邮件移动到另一个文件夹。然而,在从EWS服务器拉取大约15次后,连接开始出现401未经授权的错误,直到我重新启动程序。该程序设置为通过OAuth登录,因为系统管理员禁用了基本身份验证。下面是我用来获取exchange连接并从收件箱中读取电子邮件的代码 交换连接代码: public static async Task<ExchangeService> Get

我一直在开发一个程序,该程序可以扫描exchange收件箱中来自指定地址的特定电子邮件。目前,该程序读取收件箱,下载附件,并将电子邮件移动到另一个文件夹。然而,在从EWS服务器拉取大约15次后,连接开始出现401未经授权的错误,直到我重新启动程序。该程序设置为通过OAuth登录,因为系统管理员禁用了基本身份验证。下面是我用来获取exchange连接并从收件箱中读取电子邮件的代码

交换连接代码:

public static async Task<ExchangeService> GetExchangeConnection()
    {
        var pcaOptions = new PublicClientApplicationOptions
        {
            ClientId = AppID,
            TenantId = TenantID,
        };

        var pca = PublicClientApplicationBuilder.CreateWithApplicationOptions(pcaOptions).Build();

        var ewsScopes = new string[] { "https://outlook.office365.com/EWS.AccessAsUser.All" };

        var securePassword = new SecureString();
        foreach (char c in Pasword)
            securePassword.AppendChar(c);

        try
        {
            var authResult = await pca.AcquireTokenByUsernamePassword(ewsScopes, Username, securePassword).ExecuteAsync();

            ExchangeService exchangeService = new ExchangeService()
            {
                Credentials = new OAuthCredentials(authResult.AccessToken),
                Url = new Uri("https://outlook.office365.com/ews/exchange.asmx"),
            };

            return exchangeService;
        }
        catch
        {
            return null;
        }
    }
公共静态异步任务GetExchangeConnection()
{
var pcaooptions=新的PublicClientApplicationOptions
{
ClientId=AppID,
TenantId=TenantId,
};
var pca=PublicClientApplicationBuilder.CreateWithApplicationOptions(PCAOOptions).Build();
var ewscopes=新字符串[]{”https://outlook.office365.com/EWS.AccessAsUser.All" };
var securePassword=new SecureString();
foreach(Pasword中的字符c)
securePassword.AppendChar(c);
尝试
{
var authResult=wait pca.AcquireTokenByUsernamePassword(EWSCOpes、Username、securePassword)。ExecuteAsync();
ExchangeService ExchangeService=新的ExchangeService()
{
凭据=新的OAuthCredentials(authResult.AccessToken),
Url=新Uri(“https://outlook.office365.com/ews/exchange.asmx"),
};
返回交换服务;
}
抓住
{
返回null;
}
}
电子邮件检索器

public static List<Email> RetreiveEmails()
    {
        ExchangeService exchangeConnection = GetExchangeConnection().Result;

        try
        {
            List<Email> Emails = new List<Email>();
            TimeSpan ts = new TimeSpan(0, -5, 0, 0);
            DateTime date = DateTime.Now.Add(ts);
            SearchFilter.IsGreaterThanOrEqualTo EmailTimeFilter = new SearchFilter.IsGreaterThanOrEqualTo(ItemSchema.DateTimeReceived, date);

            if (exchangeConnection != null)
            {
                FindItemsResults<Item> findResults = exchangeConnection.FindItems(WellKnownFolderName.Inbox, EmailTimeFilter, new ItemView(10));

                foreach (Item item in findResults)
                {
                    if (item.Subject != null)
                    {
                        EmailMessage message = EmailMessage.Bind(exchangeConnection, item.Id);
                        message.Load(new PropertySet(BasePropertySet.FirstClassProperties, ItemSchema.TextBody));
                        Emails.Add(new Email(message.DateTimeReceived, message.From.Name.ToString(), message.Subject, message.TextBody.ToString(), (message.HasAttachments) ? "Yes" : "No", message.Id.ToString()));
                    }
                }
            }

            exchangeConnection = null;
            return Emails;
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
            return null;
        }
    }
publicstaticlist RetreiveEmails()
{
ExchangeService exchangeConnection=GetExchangeConnection()。结果;
尝试
{
列表电子邮件=新列表();
TimeSpan ts=新的TimeSpan(0,-5,0,0);
DateTime date=DateTime.Now.Add(ts);
SearchFilter.IsGreaterThanOrEqualTo EmailTimeFilter=newsearchfilter.IsGreaterThanOrEqualTo(ItemSchema.DateTimeReceived,date);
if(exchangeConnection!=null)
{
FindItemsResults findResults=exchangeConnection.FindItems(WellKnownFolderName.Inbox、EmailTimeFilter、new ItemView(10));
foreach(findResults中的项目)
{
if(item.Subject!=null)
{
EmailMessage=EmailMessage.Bind(exchangeConnection,item.Id);
Load(新属性集(BasePropertySet.FirstClassProperties,ItemSchema.TextBody));
Emails.Add(新电子邮件(message.DateTimeReceived,message.From.Name.ToString(),message.Subject,message.TextBody.ToString(),(message.HasAttachments)?“是”:“否”,message.Id.ToString());
}
}
}
exchangeConnection=null;
回复邮件;
}
捕获(例外e)
{
Console.WriteLine(如ToString());
返回null;
}
}

当电子邮件检索器尝试创建exchange连接或从文件夹请求电子邮件时,会发生此错误。在这两种情况下,代码都会出错,并在使用前十几次有效的凭据时给我401个未经授权的凭据,然后在多次尝试后失败。我已尝试使用多个不同的帐户,但所有帐户都存在此问题,我已确保该应用程序已获得访问exchange收件箱的授权。非常感谢您提供的任何建议或帮助。

在对401错误进行进一步跟踪后,该错误导致令牌在其1小时寿命结束时出现问题。这是由于原始OAuth令牌的初始寿命为1小时。但是,这可以通过设置代码在需要时自动刷新令牌来解决。下面是为遇到此问题的任何其他人解决此问题的代码

身份验证管理器:

class AuthenticationManager
{ 
    protected IPublicClientApplication App { get; set; }

    public AuthenticationManager(IPublicClientApplication app)
    {
        App = app;
    }

    public async Task<AuthenticationResult> AcquireATokenFromCacheOrUsernamePasswordAsync(IEnumerable<String> scopes, string username, SecureString password)
    {
        AuthenticationResult result = null;
        var accounts = await App.GetAccountsAsync();

        if (accounts.Any())
        {
            try
            {
                result = await (App as PublicClientApplication).AcquireTokenSilent(scopes, accounts.FirstOrDefault()).ExecuteAsync();
            }
            catch (MsalUiRequiredException)
            { }
        }

        if (result == null)
        {
            result = await (App as PublicClientApplication).AcquireTokenByUsernamePassword(scopes, username, password).ExecuteAsync();
        }

        return result;
    }
}
类身份验证管理器
{ 
受保护的IPublicClient应用程序应用程序{get;set;}
公共身份验证管理器(IPublicClient应用程序)
{
App=App;
}
公共异步任务AcquireATokenFromCacheOrUsernamePasswordAsync(IEnumerable作用域、字符串用户名、SecureString密码)
{
AuthenticationResult=null;
var accounts=wait App.GetAccountsAsync();
if(accounts.Any())
{
尝试
{
结果=等待(应用程序作为PublicClientApplication)。AcquireTokenSilent(作用域、帐户。FirstOrDefault())。ExecuteAsync();
}
捕获(MsalUiRequiredException)
{ }
}
如果(结果==null)
{
结果=等待(应用程序作为PublicClientApplication)。AcquireTokenByUsernamePassword(作用域、用户名、密码)。ExecuteAsync();
}
返回结果;
}
}
我使用直接用户名和密码身份验证,但代码行也可以切换到通过交互方法获得用户身份验证。该代码实质上创建了一个新的身份验证管理器实例,其中包含一个PublicClientApplication,用于初始化它,其中包含appID和tenantID。初始化后,您可以调用AquireTokenFromCacheOrUsernamePasswordAsync,它将尝试查看是否存在用于获取令牌的帐户。接下来,它将尝试检索以前缓存的令牌,如果令牌在5分钟内过期,则刷新该令牌。如果有可用的令牌,它会将其返回给主应用程序。如果没有可用的令牌,它将使用提供的用户名和密码获取新令牌。这段代码的实现如下所示

class ExchangeServices
{
     AuthenticationManager Manager = null;

     public ExchangeServices(String AppId, String TenantID)
     {
          var pcaOptions = new PublicClientApplicationOptions
          {
               ClientId = AppID,
               TenantId = TenantID,
          };
          var pca = PublicClientApplicationBuilder.CreateWithApplicationOptions(pcaOptions).Build();
          Manager = new AuthenticationManager(pca);
     }

     public static async Task<ExchangeService> GetExchangeService()
     {
          var ewsScopes = new string[] { "https://outlook.office365.com/EWS.AccessAsUser.All" }
          var securePassword = new SecureString();
          foreach(char c in Password)
               securePassword.AppendChar(c);

          var authResult = await Manager.AquireATokenFromCacheOrUsernamePasswordAsync(ewsScopes,           Username, securePassword);

          ExchangeService exchangeService = new ExchangeService()
          {
               Credentials = new OAuthCredentials(authResult.AccessToken),
               Url = new Uri("https://outlook.office365.com/ews/exchange.asmx");
          };
          return exchangeService;
     }
}
类交换服务
{
AuthenticationManager=null;
公共Exchange服务(字符串AppId,