0

我正在使用 azure mobile services sdk 进行离线同步。我制作了我的 api,以便使用电子邮件和密码通过基本身份验证对其进行保护。

如何将这些凭据嵌入到 MobileServiceClient 中,以便每当我调用一个方法时,它都具有正确的身份验证凭据。

这是我现有的 MobileServiceClient 代码。

var handler = new AuthHandler();

        //TODO 1: Create our client
        //Create our client
        MobileService = new MobileServiceClient(Helpers.Keys.AzureServiceUrl, handler)
        {
            SerializerSettings = new MobileServiceJsonSerializerSettings()
            {
                CamelCasePropertyNames = true
            }
        };

        //assign mobile client to handler
        handler.Client = MobileService;

        MobileService.CurrentUser = new MobileServiceUser(Settings.UserId);
        MobileService.CurrentUser.MobileServiceAuthenticationToken = Settings.AuthToken;

AuthHandler 类

class AuthHandler : DelegatingHandler
{
    public IMobileServiceClient Client { get; set; }
    private static readonly SemaphoreSlim semaphore = new SemaphoreSlim(1);
    private static bool isReauthenticating = false;
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        //Clone the request in case we need to send it again
        var clonedRequest = await CloneRequest(request);
        var response = await base.SendAsync(clonedRequest, cancellationToken);

        //If the token is expired or is invalid, then we need to either refresh the token or prompt the user to log back in
        if (response.StatusCode == HttpStatusCode.Unauthorized)
        {
            if (isReauthenticating)
                return response;

            var service = DependencyService.Get<AzureService>();
            var client = new MobileServiceClient(Helpers.Keys.AzureServiceUrl, null);
            client.CurrentUser = new MobileServiceUser(Settings.UserId);
            client.CurrentUser.MobileServiceAuthenticationToken = Settings.AuthToken;


            string authToken = client.CurrentUser.MobileServiceAuthenticationToken;
            await semaphore.WaitAsync();
            //In case two threads enter this method at the same time, only one should do the refresh (or re-login), the other should just resend the request with an updated header.
            if (authToken != client.CurrentUser.MobileServiceAuthenticationToken)  // token was already renewed
            {
                semaphore.Release();
                return await ResendRequest(client, request, cancellationToken);
            }

            isReauthenticating = true;
            bool gotNewToken = false;
            try
            {

                gotNewToken = await RefreshToken(client);


                //Otherwise if refreshing the token failed or Facebook\Twitter is being used, prompt the user to log back in via the login screen
                if (!gotNewToken)
                {
                    gotNewToken = await service.LoginAsync();
                }
            }
            catch (System.Exception e)
            {
                Debug.WriteLine("Unable to refresh token: " + e);
            }
            finally
            {
                isReauthenticating = false;
                semaphore.Release();
            }


            if (gotNewToken)
            {
                if (!request.RequestUri.OriginalString.Contains("/.auth/me"))   //do not resend in this case since we're not using the return value of auth/me
                {
                    //Resend the request since the user has successfully logged in and return the response
                    return await ResendRequest(client, request, cancellationToken);
                }
            }
        }

        return response;
    }


    private async Task<HttpResponseMessage> ResendRequest(IMobileServiceClient client, HttpRequestMessage request, CancellationToken cancellationToken)
    {
        // Clone the request
        var clonedRequest = await CloneRequest(request);

        // Set the authentication header
        clonedRequest.Headers.Remove("X-ZUMO-AUTH");
        clonedRequest.Headers.Add("X-ZUMO-AUTH", client.CurrentUser.MobileServiceAuthenticationToken);

        // Resend the request
        return await base.SendAsync(clonedRequest, cancellationToken);
    }

    private async Task<bool> RefreshToken(IMobileServiceClient client)
    {
        var authentication = DependencyService.Get<IAuthentication>();
        if (authentication == null)
        {
            throw new InvalidOperationException("Make sure the ServiceLocator has an instance of IAuthentication");
        }

        try
        {
            return await authentication.RefreshUser(client);
        }
        catch (System.Exception e)
        {
            Debug.WriteLine("Unable to refresh user: " + e);
        }

        return false;
    }



    private async Task<HttpRequestMessage> CloneRequest(HttpRequestMessage request)
    {
        var result = new HttpRequestMessage(request.Method, request.RequestUri);
        foreach (var header in request.Headers)
        {
            result.Headers.Add(header.Key, header.Value);
        }

        if (request.Content != null && request.Content.Headers.ContentType != null)
        {
            var requestBody = await request.Content.ReadAsStringAsync();
            var mediaType = request.Content.Headers.ContentType.MediaType;
            result.Content = new StringContent(requestBody, Encoding.UTF8, mediaType);
            foreach (var header in request.Content.Headers)
            {
                if (!header.Key.Equals("Content-Type", StringComparison.OrdinalIgnoreCase))
                {
                    result.Content.Headers.Add(header.Key, header.Value);
                }
            }
        }

        return result;
    }
}
4

1 回答 1

0

如何将这些凭据嵌入到 MobileServiceClient 中,以便每当我调用一个方法时,它都具有正确的身份验证凭据。

据我了解,在AuthHandler用户使用正确的电子邮件和密码成功登录后,该类可以提供一种设置当前有效用户信息的方法。此外,您需要缓存AuthHandler用于构造实例的MobileServiceClient实例,在用户登录后,您可以将当前用户信息嵌入到AuthHandler实例中。

如果您正在谈论使用用户名和密码而不是使用社交提供商来提供登录过程,您可以按照自定义身份验证来构建您CustomAuthController应用服务身份验证/授权(EasyAuth)。对于您的客户,您可以使用以下代码进行日志记录:

MobileServiceUser azureUser = await _client.LoginAsync("custom", JObject.FromObject(account));

此外,您需要缓存MobileServiceAuthenticationToken您的移动应用后端发出的并手动验证缓存的令牌并在您的类的方法exp下检查JWT令牌的属性,并在当前令牌时使用缓存的用户帐户显式调用以获取新的即将过期或已过期而无需要求用户再次登录。详细的代码示例,你可以关注 adrian hall 的关于Caching Tokens的书。SendAsyncAuthHandlerLoginAsyncMobileServiceAuthenticationToken

或者,如果您正在谈论基本访问身份验证,您还可以参考前面关于将凭据嵌入到您的AuthHandler. 对于您的服务器端,您还可以添加自定义DelegatingHandler以验证授权标头并将相关的 Principal 设置为HttpContext.Current.User. 您可以按如下方式初始化您的DelegatingHandler下文件:Startup.MobileApp.cs

HttpConfiguration config = new HttpConfiguration();
config.MessageHandlers.Add(new MessageHandlerBasicAuthentication());

此外,您可以使用 Web API 中的消息处理程序进行基本身份验证

于 2018-03-20T09:31:32.107 回答