Реализовать пользовательскую аутентификацию в мобильных службах 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)

Я мог бы продолжить об этом, но этот урок поможет вам начать:

http://azure.microsoft.com/en-us/documentation/articles/mobile-services-dotnet-backend-windows-store-dotnet-get-started/

Настройка проекта 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 сейчас!

Другие вопросы по тегам