"Unauthorized" Simple.OData.Client 使用来自 Xamarin PCL 的有效凭据访问 SharePoint REST API 异常

"Unauthorized" exception by Simple.OData.Client by accessing the SharePoint REST API with valid credentials from Xamarin PCL

在我们的 Xamarin 项目中使用 Simple.OData.Client 之前,我在 LINQPad 的帮助下进行了尝试。它的易用性给我留下了深刻的印象。当我将它构建到我们的 Xamarin 项目中时,我尝试从 SharePoint 的 REST Api 获取数据时遇到异常。

Simple.OData.Client.WebRequestException: Unexpected WebException encountered ---> System.Net.WebException: Error: SendFailure (Error writing headers) ---> System.Net.WebException: Error writing headers ---> System.IO.IOException: The authentication or decr...{Simple.OData.Client.WebRequestException: Unexpected WebException encountered ---> System.Net.WebException: Error: SendFailure (Error writing headers) ---> System.Net.WebException: Error writing headers ---> System.IO.IOException: The authentication or decryption has failed. ---> Mono.Security.Protocol.Tls.TlsException: Invalid certificate received from server. Error code: 0xffffffff800b010a

据我所知,此异常是由于我们的 SharePoint 实例使用自签名证书这一事实引起的。我尝试通过返回 ServerCertificateValidationCallback

始终为真来消除它
System.Net.ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true;

现在我总是从 Simple.OData.Client

中得到 Unauthorized 异常

Simple.OData.Client.WebRequestException: Unauthorized

初始调用来自 MainViewModel 通过业务层

    private async void InitializeAsync()
    {
        // TODO [Anton Kalcik - Dienstag, 05. Mai 2015 17:09:55]: Show loading indicator
        TaskEntity getTaskForCurrentMonthAsyncTask = await _taksBusinessLayer.GetTaskForCurrentMonthAsync();
        _timeToDisplay = getTaskForCurrentMonthAsyncTask.DueDate - DateTime.Now;
        // TODO [Anton Kalcik - Dienstag, 05. Mai 2015 17:10:04]: Hide loading indicator

        StartCountdownTimer();
    }

执行调用的class是classSharePointTaskRepository

public class SharePointTaskRepository : ITaskRepository
{
    private readonly string _collectionName;
    private readonly ODataClient _oDataClient;

    public SharePointTaskRepository(Uri sharepointUri, string collectionName, ICredentials credentials)
    {
        if (sharepointUri == null)
        {
            throw new ArgumentNullException("sharepointUri");
        }
        if (String.IsNullOrWhiteSpace(collectionName))
        {
            throw new ArgumentException("Argument can't be null, empty or white space!", "collectionName");
        }
        if (credentials == null)
        {
            throw new ArgumentNullException("credentials");
        }

        _collectionName = collectionName;

        var oDataClientSettings = new ODataClientSettings(sharepointUri, credentials);
        _oDataClient = new ODataClient(oDataClientSettings);
    }

    public async Task<IEnumerable<TaskModel>> ReadAsync(Expression<Func<TaskModel, bool>> filter, Expression<Func<TaskModel, object>> orderBy, int numberOfResults)
    {
        return await _oDataClient
                    .For<TaskModel>(_collectionName)
                    .Filter(filter)
                    .OrderBy(orderBy)
                    .Top(numberOfResults)
                    .FindEntriesAsync();
    }
}

我仔细检查了凭据,绝对正确。利用 ServerCertificateValidationCallback 的代码在 ApplicationRuntimeSettings 中。这个 class 是特定于平台的,单例的,并通过依赖注入作为所有其他对象提供。

[assembly: Dependency(typeof(ApplicationRuntimeSettings))]
namespace AZeitReminder.Droid.Infrastructure
{
    public class ApplicationRuntimeSettings : ApplicationRuntimeSettingsBase
    {
        public ApplicationRuntimeSettings()
        {
            System.Net.ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true;
        }

        public override SQLiteConnection CreateSqLiteConnection()
        {
                string documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
                var path = Path.Combine(documentsPath, DatabaseFileName);
                var currentPlatform = new SQLitePlatformAndroid();
                var connection = new SQLiteConnection(currentPlatform, path);

                return connection;
        }

        public override CultureInfo GetCultureInfo()
        {
            var androidLocale = Java.Util.Locale.Default;
            var netLanguage = androidLocale.ToString().Replace("_", "-"); // NOTE [Anton Kalcik - Dienstag, 05. Mai 2015 17:21:10]: turns pt_BR into pt-BR
            return new CultureInfo(netLanguage);
        }
    }
}

您可以尝试设置PreAuthenticate = false;为您的要求。 Simple.ODataClient 在内部使用 HttpClientHandler。这个 HttpClientHandler 设置 PreAuthenticate = true;但您可以在 OnApplyClientHandler 中修改此处理程序并将 属性 设置为 false。在您的代码中试试这个:

oDataClientSettings.OnApplyClientHandler = handler => handler.PreAuthenticate = false;

原因是您的 Sharepoint 服务器可以抛出 "Unauthorized" 作为质询响应,并且 WebRequest 不会回答质询,除非此 属性 为假。