JWT 不会通过 Blazor 存储在 ASP.NET Core 中
Posted
技术标签:
【中文标题】JWT 不会通过 Blazor 存储在 ASP.NET Core 中【英文标题】:JWT doesn't get stored in ASP.NET Core with Blazor 【发布时间】:2019-03-12 14:19:25 【问题描述】:我遵循了这个教程:https://medium.com/@st.mas29/microsoft-blazor-web-api-with-jwt-authentication-part-1-f33a44abab9d
我下载了例子:https://github.com/StuwiiDev/DotnetCoreJwtAuthentication/tree/Part2
我可以看到令牌已创建,但我不明白它是如何或应该保存在客户端的,因为每次我访问具有 Authorize
标记的 SampleDataController
时,它都会返回 401 .
当使用 Postman 调用和添加令牌时,它可以工作。
我缺少什么让我的用户通过身份验证? Microsoft.AspNetCore.Authentication.JwtBearer
不处理客户端部分(存储令牌)吗?
【问题讨论】:
您是否在标题中使用Authorize
作为名称?如果是,应该是Authorization
。
这是我不明白的部分。我按照教程下载了项目,并希望在登录后看到令牌。这个令牌应该如何设置?根据您的评论,我认为 JwtBearer 不会为我做这件事?
我理解您的困惑...本教程似乎没有使用 Authorization
标头,而是将令牌作为查询字符串参数提供。作者展示了如何在 Postman 中使用它进行测试,而不是在客户端应用程序中。他在第 2 部分末尾提到您可以将令牌保存到 localStorage,但没有给出实际使用令牌从客户端向服务器发出请求的示例。这不是一个很好或有用的 IMO 教程。
【参考方案1】:
我缺少什么让我的用户通过身份验证? Microsoft.AspNetCore.Authentication.JwtBearer 不处理客户端部分(存储令牌)吗?
JwtBearer
在服务器端运行,它只会验证请求的授权头,即Authorization: Bearer your_access_token
,而不关心你的WebAssembly代码如何运行。因此,您需要使用 jwt accessToken 发送请求。由于教程建议您应该使用 localStorage
,让我们将 accessToken
存储为 localStorage
。
因为WebAssembly
还不能访问BOM
,所以我们需要一些javascript 代码作为粘合剂。为此,请在 JwtAuthentication.Client/wwwroot/js/
下添加 helper.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");
结果将是:
【讨论】:
感谢您的精彩解释,现在可以使用了!仅作记录:为了能够注入TokenService
,我需要将其添加到Startup.ConfigureServices - services.AddSingleton<TokenService>(myTokenService);
中的服务中
很好的答案,真的很有用。另外,上面的评论非常有用。我建议将它放在答案中,因为没有它就无法调用令牌服务。
@GeorgeHarnwell 感谢您的建议,我已经编辑了我的答案:)【参考方案2】:
提前道歉,因为这在某种程度上是对先前答案的回应,但我没有代表对此发表评论。
如果它可以帮助其他正在寻找在 Blazor 应用程序中使用 JWT 的解决方案的其他人,我发现 @itminus 的答案非常有用,但它也为我指明了另一门课程。
我发现的一个问题是,第二次调用 FetchData.cshtml
会在它再次尝试添加 Authorization
标头时崩溃。
我没有在此处添加默认标头,而是在成功登录后将其添加到 HttpClient 单例中(我相信 Blazor 会自动为您创建)。因此,将 SubmitForm
从 @itminus 的答案更改为 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。
【讨论】:
【参考方案3】:以下类处理客户端上的登录过程,将 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)]
等等……
希望这会有所帮助...
【讨论】:
以上是关于JWT 不会通过 Blazor 存储在 ASP.NET Core 中的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 B2C 和 Blazor 获取 JWT 不记名令牌
通过 javascript 在 Blazor 中更改输入值不会更改它的绑定属性值
解析 JWT 令牌以仅在 C# 或 Blazor 中没有外部库的情况下获取有效负载内容
使用通过 Blazor WASM 使用 Windows 身份验证的 WebAPI