8

我遵循了本教程: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处理客户端部分(存储令牌)?

4

3 回答 3

12

我缺少什么让我的用户通过身份验证?Microsoft.AspNetCore.Authentication.JwtBearer 不处理客户端部分(存储令牌)吗?

JwtBearer服务器端运行,它只会验证请求的授权标头,即Authorization: Bearer your_access_token,而不关心您的 WebAssembly 代码如何运行。因此,您需要使用 jwt accessToken 发送请求。由于教程建议您应该使用localStorage,让我们存储accessTokenwith 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);

现在我们可以注入TokenServiceLogin.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");
    }
}

结果将是:

在此处输入图像描述

于 2018-10-08T02:25:45.287 回答
3

提前道歉,因为这在某种程度上是对先前答案的回应,但我没有代表对此发表评论。

如果它可以帮助其他同样在 Blazor 应用程序中寻找使用 JWT 的解决方案的人,我发现 @itminus 的答案非常有用,但它也为我指明了另一门课程。

我发现的一个问题是,FetchData.cshtml当它尝试第二次添加Authorization标头时,第二次调用会崩溃。

我没有在此处添加默认标头,而是在成功登录后将其添加到 HttpClient 单例中(我相信 Blazor 会自动为您创建)。所以从@itminus 的答案SubmitForm改变。Login.cshtml

    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。

于 2018-11-14T17:52:28.973 回答
2

以下类处理客户端上的登录过程,将 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;

           }

        }
    }

// 这就是您调用 Web 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();
        } 

// 您还必须在客户端上设置 Startup 类,如下所示:

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

      ...
    }

// 一般来说,这是你必须在客户端上做的事情。// 在服务器上,你必须有一个方法,比如在 Account 控制器中,它的功能是生成 JWT 令牌,你必须配置 JWT 中间件,用必要的属性注释你的控制器,如实例:

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]  

等等...

希望这可以帮助...

于 2018-10-08T02:46:25.653 回答