Реализовать пользовательскую аутентификацию в мобильных службах Windows Azure
Windows Azure Mobile Services в настоящее время не имеет возможности настраиваемой проверки подлинности и просмотра запроса функции
http://feedback.azure.com/forums/216254-mobile-services/suggestions/3313778-custom-user-auth
Это не придет в ближайшее время.
С бэкэндом.NET и приложением.NET, как вы реализуете пользовательскую аутентификацию, чтобы вам не приходилось использовать Facebook, Google или любого другого их нынешнего провайдера?
Есть много частично завершенных учебных пособий о том, как это сделать с помощью JS-бэкенда и iOS и Android, но где примеры.NET?
2 ответа
Я наконец-то поработал над решением, с некоторой помощью статей, перечисленных ниже, некоторого intellisense и некоторых проб и ошибок.
Как работает WAMS
Сначала я хотел описать, что такое WAMS, в очень простой форме, так как эта часть меня смущала некоторое время, пока она наконец не щелкнула. WAMS - это просто набор уже существующих технологий, упакованных для быстрого развертывания. Что вам нужно знать для этого сценария:
Как вы можете видеть, WAMS - это всего лишь контейнер для WebAPI и других вещей, которые я не буду здесь вдаваться в подробности. При создании новой мобильной службы в Azure вы получаете возможность загрузить проект, содержащий WebAPI. В качестве примера они используют TodoItem, поэтому вы увидите код этого сценария в проекте.
Ниже вы можете скачать этот пример (я просто делал приложение для Windows Phone 8)
Я мог бы продолжить об этом, но этот урок поможет вам начать:
Настройка проекта WAMS
Вам понадобятся ваш MasterKey и ApplicationKey. Вы можете получить их на портале Azure, щелкнув приложение "Мобильные службы" и нажав "Управление ключами" внизу.
Проект, который вы только что загрузили, в папке Controllers я только что создал новый контроллер с именем AccountController.cs и внутри я положил
public HttpResponseMessage GetLogin(String username, String password)
{
String masterKey = "[enter your master key here]";
bool isValidated = true;
if (isValidated)
return new HttpResponseMessage() { StatusCode = HttpStatusCode.OK, Content = new StringContent("{ 'UserId' : 'F907F58C-09FE-4F25-A26B-3248CD30F835', 'token' : '" + GetSecurityToken(new TimeSpan(1,0, 0), String.Empty, "F907F58C-09FE-4F25-A26B-3248CD30F835", masterKey) + "' }") };
else
return Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "Username and password are incorrect");
}
private static string GetSecurityToken(TimeSpan periodBeforeExpires, string aud, string userId, string masterKey)
{
var now = DateTime.UtcNow;
var utc0 = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
var payload = new
{
exp = (int)now.Add(periodBeforeExpires).Subtract(utc0).TotalSeconds,
iss = "urn:microsoft:windows-azure:zumo",
ver = 2,
aud = "urn:microsoft:windows-azure:zumo",
uid = userId
};
var keyBytes = Encoding.UTF8.GetBytes(masterKey + "JWTSig");
var segments = new List<string>();
//kid changed to a string
var header = new { alg = "HS256", typ = "JWT", kid = "0" };
byte[] headerBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(header, Formatting.None));
byte[] payloadBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(payload, Formatting.None));
segments.Add(Base64UrlEncode(headerBytes));
segments.Add(Base64UrlEncode(payloadBytes));
var stringToSign = string.Join(".", segments.ToArray());
var bytesToSign = Encoding.UTF8.GetBytes(stringToSign);
SHA256Managed hash = new SHA256Managed();
byte[] signingBytes = hash.ComputeHash(keyBytes);
var sha = new HMACSHA256(signingBytes);
byte[] signature = sha.ComputeHash(bytesToSign);
segments.Add(Base64UrlEncode(signature));
return string.Join(".", segments.ToArray());
}
// from JWT spec
private static string Base64UrlEncode(byte[] input)
{
var output = Convert.ToBase64String(input);
output = output.Split('=')[0]; // Remove any trailing '='s
output = output.Replace('+', '-'); // 62nd char of encoding
output = output.Replace('/', '_'); // 63rd char of encoding
return output;
}
Вы можете заменить то, что есть в GetLogin, своим собственным кодом проверки. После проверки он вернет маркер безопасности (JWT), который необходим.
Если вы тестируете на своем локальном хосте, не забудьте зайти в свой файл web.config и заполнить следующие ключи
<add key="MS_MasterKey" value="Overridden by portal settings" />
<add key="MS_ApplicationKey" value="Overridden by portal settings" />
Вы должны ввести свои Мастер и Ключи Приложения здесь. Они будут переопределены, когда вы загрузите их, но их необходимо ввести, если вы все запускаете локально.
В верхней части TodoItemController добавьте атрибут AuthorizeLevel, как показано ниже
[AuthorizeLevel(AuthorizationLevel.User)]
public class TodoItemController : TableController<TodoItem>
Вам нужно будет изменить большинство функций в вашем TodoItemController, но вот пример функции Get All.
public IQueryable<TodoItem> GetAllTodoItems()
{
var currentUser = User as ServiceUser;
Guid id = new Guid(currentUser.Id);
return Query().Where(todo => todo.UserId == id);
}
Просто примечание: я использую UserId в качестве Guid (uniqueidentifier), и вам нужно добавить это в определение модели todo. Вы можете сделать UserId любого типа, например, Int32.
Приложение для Windows Phone/Store
Обратите внимание, что это всего лишь пример, и вы должны очистить код в основном приложении, как только оно заработает.
В вашем клиентском приложении
Установите пакет NuGet: мобильные службы Windows Azure
Зайдите в App.xaml.cs и добавьте это в начало
public static MobileServiceClient MobileService = new MobileServiceClient(
"http://localhost:50527/",
"[enter application key here]"
);
В MainPage.xaml.cs я создал
public class Token
{
public Guid UserId { get; set; }
public String token { get; set; }
}
В основной класс добавьте функцию Authenticate
private bool Authenticate(String username, String password)
{
HttpClient client = new HttpClient();
// Enter your own localhost settings here
client.BaseAddress = new Uri("http://localhost:50527/");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = client.GetAsync(String.Format("api/Account/Login?username={0}&password={1}", username, password)).Result;
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
var token = Newtonsoft.Json.JsonConvert.DeserializeObject<Token>(response.Content.ReadAsStringAsync().Result);
App.MobileService.CurrentUser = new MobileServiceUser(token.UserId.ToString());
App.MobileService.CurrentUser.MobileServiceAuthenticationToken = token.token;
return true;
}
else
{
//Something has gone wrong, handle it here
return false;
}
}
Затем в функции Main_Loaded
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
Authenticate("test", "test");
RefreshTodoItems();
}
Если у вас есть точки останова в WebAPI, вы увидите, что он входит, получает токен, затем возвращается к ToDoItemController, и currentUser будет заполнен UserId и токеном.
Вам нужно будет создать свою собственную страницу входа, так как при этом методе вы не можете использовать автоматически созданную страницу с другими поставщиками удостоверений. Однако я все равно предпочитаю создать свой собственный экран входа в систему.
Любые другие вопросы, дайте мне знать в комментариях, и я помогу, если смогу.
Примечание по безопасности
Не забудьте использовать SSL.
Рекомендации
[] http://www.thejoyofcode.com/Exploring_custom_identity_in_Mobile_Services_Day_12_.aspx
[] http://www.contentmaster.com/azure/creating-a-jwt-token-to-access-windows-azure-mobile-services/
[] http://chrisrisner.com/Custom-Authentication-with-Azure-Mobile-Services-and-LensRocket
Это именно то, как вы это делаете. Этому человеку нужно 10 звезд и 5 ящиков пива!
Во-первых, я использовал мобильный сервис LoginResult для входа в систему, например: var token = Newtonsoft.Json.JsonConvert.DeserializeObject(response.Content.ReadAsStringAsync().Result);
Надеюсь получить это в Android сейчас!