JWT не хранится в ASP.NET Core с Blazor - PullRequest
0 голосов
/ 08 октября 2018

Я следовал этому руководству: https://medium.com/@st.mas29/microsoft-blazor-web-api-with-jwt-authentication-part-1-f33a44abab9d

Я скачал пример: https://github.com/StuwiiDev/DotnetCoreJwtAuthentication/tree/Part2

Я вижу, что токен создан, но я не понимаю, как он должен или долженсохраняться на стороне клиента, так как каждый раз, когда я получаю доступ к SampleDataController, который имеет тег Authorize, он возвращает 401.

При вызове и добавлении токена с помощью Postman это работает.

Чего мне не хватает для аутентификации моего пользователя?Разве Microsoft.AspNetCore.Authentication.JwtBearer не обрабатывает клиентскую часть (хранит токен)?

Ответы [ 3 ]

0 голосов
/ 08 октября 2018

Следующий класс обрабатывает процесс входа в систему на клиенте, сохраняя токен JWT в local хранилище.Примечание. Разработчик несет ответственность за сохранение токена JWT и передачу его на сервер.Клиент (Blazor, Angular и т. Д.) Не делает этого для него автоматически.

public class SignInManager
    {
        // Receive 'http' instance from DI
        private readonly HttpClient http;
        public SignInManager(HttpClient http)
        {
            this.http = http;
        }

        [Inject]
        protected LocalStorage localStorage;


        public bool IsAuthenticated()
        {
            var token = localStorage.GetItem<string>("token");

            return (token != null); 
        }

        public string getToken()
        {
            return localStorage.GetItem<string>("token");
        }

        public void Clear()
        {
            localStorage.Clear();
        }


        // model.Email, model.Password, model.RememberMe, lockoutOnFailure: false
        public async Task<bool> PasswordSignInAsync(LoginViewModel model)
        {
            SearchInProgress = true;
            NotifyStateChanged();

            var result = await http.PostJsonAsync<Object>("/api/Account", model);

            if (result)// result.Succeeded
           {
              _logger.LogInformation("User logged in.");

              // Save the JWT token in the LocalStorage
              // https://github.com/BlazorExtensions/Storage
              await localStorage.SetItem<Object>("token", result);


              // Returns true to indicate the user has been logged in and the JWT token 
              // is saved on the user browser
             return true;

           }

        }
    }

// Вот как вы называете свой веб-API, отправляя ему токен JWT для // текущего пользователя

public async Task<IList<Profile>> GetProfiles()
        {   
            SearchInProgress = true;
            NotifyStateChanged();

            var token = signInManager.getToken();
            if (token == null) {
                throw new ArgumentNullException(nameof(AppState)); //"No token";
            }

            this.http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

            // .set('Content-Type', 'application/json')
            // this.http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

            Profiles = await this.http.GetJsonAsync<Profile[]>("/api/Profiles");


            SearchInProgress = false;
            NotifyStateChanged();
        } 

// Вам также необходимо установить класс запускана клиенте следующим образом:

public void ConfigureServices(IServiceCollection services)
    {
        // Add Blazor.Extensions.Storage
       // Both SessionStorage and LocalStorage are registered
       // https://github.com/BlazorExtensions/Storage
       **services.AddStorage();**

      ...
    }

// Вообще говоря, это то, что вы должны сделать на клиенте.// На сервере у вас должен быть метод, скажем, в контроллере учетных записей, функция которого состоит в том, чтобы генерировать токен JWT, вы должны сконфигурировать промежуточное программное обеспечение JWT, чтобы аннотировать ваши контроллеры необходимым атрибутом, как дляэкземпляр:

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]  

и так далее ...

Надеюсь, это поможет ...

0 голосов
/ 14 ноября 2018

Заранее извиняюсь, так как это в некоторой степени отвечает на предыдущий ответ, но у меня нет представителя, чтобы комментировать это.

Если это поможет кому-то еще, кто так же искал решение для использования JWTв приложении Blazor я нашел ответ @itminus невероятно полезным, но он также указал мне на другой курс.

Одна проблема, которую я обнаружил, заключалась в том, что при повторном вызове FetchData.cshtml во время попытки добавитьЗаголовок Authorization во второй раз.

Вместо добавления заголовка по умолчанию, я добавил его в синглтон HttpClient после успешного входа в систему (который, я считаю, Blazor создает для вас автоматически).Таким образом, изменив SubmitForm в Login.cshtml из ответа @itminus.

    protected async Task SubmitForm()
    {
        // Remove any existing Authorization headers
        Http.DefaultRequestHeaders.Remove("Authorization");

        TokenViewModel vm = new TokenViewModel()
        {
            Email = Email,
            Password = Password
        };

        TokenResponse response = await Http.PostJsonAsync<TokenResponse>("api/Token/Login", vm);

        // Now add the token to the Http singleton
        Http.DefaultRequestHeaders.Add("Authorization", string.Format("Bearer {0} ", response.Token));
    }

Тогда я понял, что когда я создаю SPA, мне не нужно было сохранять токен при запросах ввсе - это просто в приложении к HttpClient.

0 голосов
/ 08 октября 2018

Чего мне не хватает для аутентификации моего пользователя?Разве Microsoft.AspNetCore.Authentication.JwtBearer не обрабатывает клиентскую часть (хранит токен)?

JwtBearer выполняется на стороне сервера, он только проверяет заголовок авторизации запроса, а именно Authorization: Bearer your_access_token, и не будет заботиться о том, как работают ваши коды WebAssembly.Так что вам нужно отправить запрос с jwt accessToken.Так как в руководстве предлагается использовать localStorage, давайте сохраним accessToken с localStorage.

Поскольку WebAssembly пока не имеет доступа к BOM, нам нужны некоторые коды javascript, используемые как клей.Чтобы сделать это, добавьте helper.js под JwtAuthentication.Client/wwwroot/js/:

var wasmHelper = {};

wasmHelper.ACCESS_TOKEN_KEY ="__access_token__";

wasmHelper.saveAccessToken = function (tokenStr) {
    localStorage.setItem(wasmHelper.ACCESS_TOKEN_KEY,tokenStr);
};

wasmHelper.getAccessToken = function () {
    return localStorage.getItem(wasmHelper.ACCESS_TOKEN_KEY);
};

и сделайте ссылку на скрипт в вашем JwtAuthentication.Client/wwwroot/index.html

<body>
    <app>Loading...</app>
    <script src="js/helper.js"></script>
    <script src="_framework/blazor.webassembly.js"></script>
</body>

Теперь давайте упаковываем коды JavaScript вC #.Создайте новый файл Client/Services/TokenService.cs:

public class TokenService
{
    public Task SaveAccessToken(string accessToken) {
        return JSRuntime.Current.InvokeAsync<object>("wasmHelper.saveAccessToken",accessToken);
    }
    public Task<string> GetAccessToken() {
        return JSRuntime.Current.InvokeAsync<string>("wasmHelper.getAccessToken");
    }
}

Зарегистрируйте эту услугу:

// file: Startup.cs 
services.AddSingleton<TokenService>(myTokenService);

И теперь мы можем вставить TokenService в Login.cshtml и использовать его для сохранения токена:

@using JwtAuthentication.Client.Services
// ...
@page "/login"
// ...
@inject TokenService tokenService

// ...

@functions {
    public string Email { get; set; } = "";
    public string Password { get; set; } = "";
    public string Token { get; set; } = "";


    /// <summary>
    /// response from server
    /// </summary>
    private class TokenResponse{
        public string Token;
    }

    private async Task SubmitForm()
    {
        var vm = new TokenViewModel
        {
            Email = Email,
            Password = Password
        };

        var response = await Http.PostJsonAsync<TokenResponse>("http://localhost:57778/api/Token", vm);
        await tokenService.SaveAccessToken(response.Token);
    }
}

Допустим, вы хотите отправить данные в пределах FetchData.cshtml

@functions {
    WeatherForecast[] forecasts;


    protected override async Task OnInitAsync()
    {
        var token = await tokenService.GetAccessToken();
        Http.DefaultRequestHeaders.Add("Authorization",String.Format("Bearer {0} ",token));
        forecasts = await Http.GetJsonAsync<WeatherForecast[]>("api/SampleData/WeatherForecasts");
    }
}

, и результат будет:

enter image description here

...