Sharepoint 2013 - How to logout a WinRT Client

2019-08-31 14:36发布

问题:

we have written a WinRT App connected to a Sharepoint 2013. We are able to authenticate and login to the sharepoint, but we have problems with the logout 'process'. Login is implemented as follows:

We are setting up a HttpClient with the corresponding user credentials and domain information. The configuration is wrapped in the HttpClientConfig class an delivered to a the HttpClientService which holds the HttpClient object. After that we retrieve the formdigestValue from the sharepoint and use the token in the X-RequestDigest Header in every request. If the token times out we retrieve a new one.

Here is some code how we implemented the above mentioned authentication.

public async Task Inialize()
{
      var httpConfig = new HttpClientConfig();
            httpConfig.Headers.Add("Accept", "application/json;odata=verbose");
            httpConfig.Headers.Add("User-Agent", _userAgent);
            httpConfig.DefaultTimeout = Statics.DEFAULT_NETWORK_TIMEOUT_SECONDS;
            httpConfig.PreAuthenticate = true;

            httpConfig.NetworkCredentials = new NetworkCredential(username, password, _domain);


            _httpClientService.ResetCookies();

            _httpClientService.ConfigureHttpClient(httpConfig);

}

The ConfigureHttpClient method disposes an old HttpClient instance and creates a new HttpClient instance, like this:

    public void ConfigureHttpClient(HttpClientConfig config, bool disposeCurrent = true)
    {
        _config = config;
        if (disposeCurrent)
        {
            DisposeHttpClient();
        }

        _httpClient = CreateHttpClient(config);
        if (disposeCurrent)
        {
            //make sure remove old httpclient and httpclienthandler instances after they are not hold anywhere else
            GC.Collect();
        }

        _httpClientDisposed = false;
    }


    public HttpClient CreateHttpClient(HttpClientConfig config)
    {
        _httpClientHandler = _httpClientFactoryService.CreateHttpClientHandler();

        _httpClientHandler.CookieContainer = _cookieContainer;

        _httpClientHandler.UseCookies = true;

        _httpClientHandler.AllowAutoRedirect = config.AllowAutoRedirect;

        _httpClientHandler.PreAuthenticate = config.PreAuthenticate;

        if (config.NetworkCredentials != null)
        {
            _httpClientHandler.Credentials = config.NetworkCredentials;
        }

        var client = _httpClientFactoryService.CreateHttpClient(_httpClientHandler, true);

        client.Timeout = TimeSpan.FromSeconds(config.DefaultTimeout);

        if (config.UseGzipCompression)
        {
            if (_httpClientHandler.SupportsAutomaticDecompression)
            {
                _httpClientHandler.AutomaticDecompression = DecompressionMethods.GZip;
                client.DefaultRequestHeaders.AcceptEncoding.Add(StringWithQualityHeaderValue.Parse("gzip"));
            }
        }
        return client;
    }



    public void DisposeHttpClient()
    {
        var client = _httpClient;
        _httpClientDisposed = true; //set flag before disposing is done to be able to react correctly!
        if (client != null)
        {
            client.Dispose();
        }
        var handler = _httpClientHandler;
        if (handler != null)
        {
            handler.Dispose();
        }
        GC.Collect();
    }



        public async Task<object> InitNewSharepointSession(bool useCookies = true)
        {
            var config = _httpClientService.CurrentClientConfig;
            config.UseCookies = useCookies;
            var res = await getRequestDigestAsync();
            if (res.IsSuccess)
            {
                SharepointContextInformation = res.Response;
                if (config.Headers.ContainsKey("X-RequestDigest"))
                    {
                        config.Headers.Remove("X-RequestDigest");
                    }
                    config.Headers.Add("X-RequestDigest", SharepointContextInformation.FormDigestValue);

                return new DataServiceResponse<bool>(true);
            }
            else
            {
                return new DataServiceResponse<bool>(res.Error);
            }
        }

The ResetCookies method only disposes the old cookies list:

public void ResetCookies()
        {
            _cookieContainer = new CookieContainer();
        }

As you can see we used some GC.Collect() calls which shows a bit our helplessness according the logout stuff. For logout, we just dispose our httpclient. But for some reason, if we login with another user, we sometimes get the data of the previous user which is a pretty high rated bug for us. Everything works nice if we restart the app, but if we only dispose the current users httpClient we may run in this failure having access with the wrong credential/user context of the previous user.

Another thing I watched is the behaviour after a password change. The old password remains and is valid until the app has been restarted.

So I would be very thankful for some hints or suggestions of a sharepoint REST specialist on how to solve this issue.

回答1:

I guess you are creating a Universal app for Windows 10. In that case, there is no other option than restarting the app, see this answer.

HTTP credentials are not the same as cookies, so resetting the cookies will not help.

However, if you are using System.Net.Http.HttpClient in a Windows 8/8.1 project (no Universal project), disposing the HttpClient should work.

Example with Windows 8/8.1 template. Do NOT use with Universal template.

private async void Foo()
{
    // Succeeds, correct username and password.
    await Foo("foo", "bar");

    // Fails, wrong username and passord.
    await Foo("fizz", "buzz");
}

private async Task Foo(string user, string password)
{
    Uri uri = new Uri("http://heyhttp.org/?basic=1&user=foo&password=bar");
    HttpClientHandler handler = new HttpClientHandler();
    handler.Credentials = new System.Net.NetworkCredential(user, password);
    HttpClient client = new HttpClient(handler);
    Debug.WriteLine(await client.GetAsync(uri));
}