我正在使用azure移动服务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;
}
}
答案 0 :(得分:0)
如何使用MobileServiceClient嵌入这些凭据,以便每当我调用方法时,它都具有正确的身份验证凭据。
根据我的理解,AuthHandler
类可以提供一种方法,用于在用户使用正确的电子邮件和密码成功登录后设置当前有效的用户信息。此外,您需要缓存用于构造AuthHandler
实例的MobileServiceClient
实例,在用户登录后,您可以将当前用户信息嵌入AuthHandler
实例。
如果您正在谈论使用用户名和密码提供登录流程而不是使用社交提供商,则可以按Custom Authentication建立CustomAuthController
以使用{{3} (EasyAuth)。对于您的客户端,您可以使用以下代码进行日志记录:
MobileServiceUser azureUser = await _client.LoginAsync("custom", JObject.FromObject(account));
此外,您需要缓存移动应用后端发布的MobileServiceAuthenticationToken
并手动验证缓存的令牌,并根据您的exp
方法检查JWT令牌的SendAsync
属性AuthHandler
类,并在当前令牌即将过期或已过期而不要求用户再次登录时,使用缓存的用户帐户显式调用LoginAsync
以获取新的MobileServiceAuthenticationToken
。详细的代码示例,您可以关注adrian hall的关于App Service Authentication / Authorization的书。
或者如果您正在谈论Caching Tokens,您还可以参考上一部分关于将凭据嵌入AuthHandler
的内容。对于服务器端,您还可以添加自定义DelegatingHandler
以验证授权标头,并将相关的Principal设置为HttpContext.Current.User
。您可以在DelegatingHandler
文件下初始化Startup.MobileApp.cs
,如下所示:
HttpConfiguration config = new HttpConfiguration();
config.MessageHandlers.Add(new MessageHandlerBasicAuthentication());
此外,您可以关注Basic access authentication。