我遵循了本教程: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
是否不处理客户端部分(存储令牌)?
答案 0 :(得分:6)
要验证用户身份,我缺少什么? Microsoft.AspNetCore.Authentication.JwtBearer不能处理客户端部分(存储令牌)吗?
type actual_price recommended_price num_videos image_ava text_length
1 67.85 59 5 0 7
0 100.70 53 5 0 224
0 74.00 74 4 1 21
0 135.00 75 1 0 184
0 59.36 53 2 1 31
在服务器端运行,它将仅验证请求的授权标头,即JwtBearer
,而不关心WebAssembly代码的运行方式。因此,您需要使用jwt accessToken发送请求。由于本教程建议您使用Authorization: Bearer your_access_token
,因此将localStorage
与accessToken
一起存储。
由于localStorage
尚未访问WebAssembly
,因此我们需要一些javascript代码作为胶水。为此,请在BOM
下添加一个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
现在,让我们将javascript代码包装到C#中。创建一个新文件<body>
<app>Loading...</app>
<script src="js/helper.js"></script>
<script src="_framework/blazor.webassembly.js"></script>
</body>
:
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");
}
}
注入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
结果将是:
答案 1 :(得分: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)]
以此类推...
希望这对您有帮助...
答案 2 :(得分:2)
提前道歉,因为这在某种程度上回应了先前的答案,但我没有代表对此发表评论。
如果它可以帮助其他在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上。