Jwt Blazor:重新加载页面时缺少授权标头

Jwt Blazor:重新加载页面时缺少授权标头,jwt,local-storage,blazor,Jwt,Local Storage,Blazor,Blazor应用程序中的授权标头有问题。 我设法登录并使用执行api请求的不同页面来获取一些数据,但每当我想要重新加载页面时,问题就会出现 点击菜单中的链接并转到“”时,数据被提取并显示(如果我单击另一页并返回): 但只要我在浏览器中键入路径或刷新页面,请求中的授权标头就会丢失: :method: GET :path: /api/ExerciseTemplateBanks :scheme: https accept: */* accept-encoding: gzip, deflate, br

Blazor应用程序中的授权标头有问题。 我设法登录并使用执行api请求的不同页面来获取一些数据,但每当我想要重新加载页面时,问题就会出现

点击菜单中的链接并转到“”时,数据被提取并显示(如果我单击另一页并返回):

但只要我在浏览器中键入路径或刷新页面,请求中的授权标头就会丢失:

:method: GET
:path: /api/ExerciseTemplateBanks
:scheme: https
accept: */*
accept-encoding: gzip, deflate, br
accept-language: fr-BE,fr;q=0.9,en-BE;q=0.8,en;q=0.7,fr-FR;q=0.6,en-US;q=0.5
cookie: .AspNetCore.Identity.Application=CfDJ8P4mU_JbFqRCn54otwf12q5yA7N68HaG-bobWYp3vyOztrMZVddJea_LhUAXKRcVKyGD3eATQrfSqtp7_ruRNrtujaFHdGJYTfE-RsOGmm9MschC_eTNzJfC13U4J7IlxNgO6J578tm_3DG8WnmxFjg2H7qQJJm1IYHt8OY49TqFpjCWpvQVkiAvf4iUmjO6CXJrRWwYQoDM65NRXtJtbcVE0zzA12r2E15WLMMOb6DMGwMpGA5DsU1zuXMY83f0ZBUmlnXBb4xYbPSICwKX-RxAnSJZYp7dYfHmx08_fbnQzQ_1FgMGcZSrf_TxRnFuxMh6o1YYMxkgnScZ4sWiIJIfFBIEJdXuyUKjRyEKH8vczojfFsGLDLoCcBSumSRMxAWV_2wm6rHT6OhEQLlYFWwlJdlqgMz0ZzwcVnqijkznystakHngxQNdJjjVVrt9uQwNi1SaOl2pvh0g0RqjPT0jbkU5BeO7XJ_pWRNK7bs2G4LMaFSt14M45-PerDKD6nBV6XS_7he3oEZMw49PSjHjQnc6BlKhT_TGj6RrP3RotEz61JSQU8wiCtTpLrGouD-f4FtThQsO4oHCV6r6HzD2WGo9oGm_qbsRnIlB16NbseqsAV-7TVuF2fMfboSVhZj7YFzHDNGLfTqK4yIjkU65qWRHsdPoR5WBLuStBAW1QC1-7nj8H_NjXfEuOhPyzUKR4usBXbYBr6a_PtkGS0I
referer: https://localhost:44387/MyExerciseTemplateBanks/
sec-fetch-mode: cors
sec-fetch-site: same-origin
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36 
我的第一个猜测是,这与状态管理有关,但本地存储应该通过重新加载来保持,对吗

基本上,我的问题是: 1.刷新页面时blazor客户端中发生了什么 2.在刷新使用API的页面时,如何确保授权标头将位于请求中

以下是与我的项目中的身份验证和授权相关的代码:

提交登录表单时调用AuthService.Login():

        public async Task<LoginResult> Login(LoginModel loginModel)
    {
        var loginAsJson = JsonSerializer.Serialize(loginModel);
        var response = await _httpClient.PostAsync("api/Accounts/Login", new StringContent(loginAsJson, Encoding.UTF8, "application/json"));
        var loginResult = JsonSerializer.Deserialize<LoginResult>(await response.Content.ReadAsStringAsync(), new JsonSerializerOptions { PropertyNameCaseInsensitive = true });

        if (!response.IsSuccessStatusCode)
        {
            return loginResult;
        }

        await _localStorage.SetItemAsync("authToken", loginResult.Token);
        ((ApiAuthenticationStateProvider)_authenticationStateProvider).MarkUserAsAuthenticated(loginResult.Token);
        _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", loginResult.Token);

        return loginResult;
    }
公共异步任务登录(LoginModel LoginModel)
{
var loginAsJson=JsonSerializer.Serialize(loginModel);
var response=wait_httpClient.PostAsync(“api/Accounts/Login”,新的StringContent(loginAsJson,Encoding.UTF8,“application/json”);
var loginResult=JsonSerializer.Deserialize(wait response.Content.ReadAsStringAsync(),新的JsonSerializerOptions{PropertyNameCaseSensitive=true});
如果(!response.issucessStatusCode)
{
返回登录结果;
}
wait_localStorage.SetItemAsync(“authToken”,loginResult.Token);
((APAuthenticationStateProvider)\u authenticationStateProvider.MarkUserAsAuthenticated(loginResult.Token);
_httpClient.DefaultRequestHeaders.Authorization=新的AuthenticationHeaderValue(“承载者”,loginResult.Token);
返回登录结果;
}
APAuthenticationStateProvider:

   public class ApiAuthenticationStateProvider : AuthenticationStateProvider
{
    private readonly HttpClient _httpClient;
    private readonly ILocalStorageService _localStorage;

    public ApiAuthenticationStateProvider(HttpClient httpClient, ILocalStorageService localStorage)
    {
        _httpClient = httpClient;
        _localStorage = localStorage;
    }

    public override async Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        var savedToken = await _localStorage.GetItemAsync<string>("authToken");

        if (string.IsNullOrWhiteSpace(savedToken))
        {
            return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
        }

        _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", savedToken);

        return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity(ParseClaimsFromJwt(savedToken), "jwt")));
    }

    public void MarkUserAsAuthenticated(string token)
    {
        var authenticatedUser = new ClaimsPrincipal(new ClaimsIdentity(ParseClaimsFromJwt(token), "jwt"));
        var authState = Task.FromResult(new AuthenticationState(authenticatedUser));
        NotifyAuthenticationStateChanged(authState);
    }
        [AllowAnonymous]
    [HttpPost]
    public async Task<IActionResult> Login([FromBody] LoginDto loginDto) //Former CreateToken (Renamed)
    {

        var user = await _userManager.FindByNameAsync(loginDto.Username);
        if (user != null)
        {
            var result = await _signInManager.CheckPasswordSignInAsync(user, loginDto.Password, false);

            if (result.Succeeded)
            {
                //Create the token
                var claims = new List<Claim>()
                {
                    new Claim(JwtRegisteredClaimNames.Sub, user.Id),
                    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                    new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName)

                };

                var userRoles = await _userManager.GetRolesAsync(user);

                foreach (var userRole in userRoles)
                {
                    claims.Add(new Claim("role", userRole));
                    var role = await _roleManager.FindByNameAsync(userRole);

                    if (role == null)
                    {
                        continue;
                    }

                    var roleClaims = await _roleManager.GetClaimsAsync(role);

                    foreach (Claim roleClaim in roleClaims)
                    {
                        claims.Add(roleClaim);
                    }
                }

                //
                //Calculate the Permissions Claim value and add it
                claims.Add(new Claim("Permissions", await _rtoPCalcer.CalcPermissionsForUser(user, userRoles)));
                //


                var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Tokens:Key"])); //
                var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
                var token = new JwtSecurityToken(
                    _config["Tokens:Issuer"],
                    _config["Tokens:Audience"],
                    claims,
                    expires: DateTime.UtcNow.AddMinutes(30),
                    signingCredentials: credentials
                    );
                DateTime expiration = token.ValidTo;

                return Ok(new LoginResult { Successful = true, Token = new JwtSecurityTokenHandler().WriteToken(token), ValidTo = expiration });
            }
        }

        return BadRequest(new LoginResult { Successful = false, Error = "Username and password are invalid." });
    }
  @using System.Net.Http;
    @using ViewModels.ExerciseModule
    @inject HttpClient Http

@page "/MyExerciseTemplateBanks"

<h3>ExerciseTemplateBanks</h3>
<p>This component demonstrates fetching data from the server.</p>

@if (ApiResponse == null)
{
    <p><em>Loading...</em></p>
}
else
{
    if (ApiResponse != null && ApiResponse.Results.Any())
        foreach (var item in ApiResponse.Results)
        {
            <div class="card" style="width: 18rem;">
                <div class="card-body">
                    <h3 class="card-title">Name : @item.Name</h3>
                    <p class="card-text">Text : Bla bla bla</p>
                </div>
            </div>
        }

    <div class="card" style="width: 18rem;">
        <div class="card-body">
            <p class="card-text"><a href="/AddExerciseTemplateBank">Create New Bank</a></p>
        </div>
    </div>
}


@code {
    private string Response { get; set; }
    private WebApiMessageAndResult<List<ExerciseTemplateBankVm>> ApiResponse { get; set; }
    private List<ExerciseTemplateBankVm> Banks { get; set; }

    protected override async Task OnInitializedAsync()
    {
        ApiResponse = await Http.GetJsonAsync<WebApiMessageAndResult<List<ExerciseTemplateBankVm>>>("api/ExerciseTemplateBanks");

    }

公共类APAuthenticationStateProvider:AuthenticationStateProvider
{
私有只读HttpClientu HttpClient;
专用只读ILocalStorageService\u localStorage;
公共ApiAuthenticationStateProvider(HttpClient HttpClient、ILocalStorageService localStorage)
{
_httpClient=httpClient;
_localStorage=localStorage;
}
公共覆盖异步任务

} @代码{ 私有字符串响应{get;set;} 私有WebApiMessageAndResult ApiResponse{get;set;} 私有列表库{get;set;} 受保护的重写异步任务OnInitializedAsync() { ApiResponse=wait Http.GetJsonAsync(“api/ExerciseTemplateBanks”); }
根据Enet的要求(参见下面的评论): 我尝试添加这两行代码

        var token = await localStorage.GetItemAsync<string>("authToken");

        Http.DefaultRequestHeaders.Authorization = new authenticationHeaderValue("bearer", token);
var-token=await localStorage.GetItemAsync(“authToken”);
Http.DefaultRequestHeaders.Authorization=新的authenticationHeaderValue(“承载者”,令牌);
但只需添加第一个和一个Console.Writeline(令牌)并重新加载页面即可(授权标头已存在)

var-token=await\u localStorage.GetItemAsync(“authToken”);
ApiResponse=wait Http.GetJsonAsync(“api/ExerciseTemplateBanks”);
这是否意味着,在每次Api请求之前,我必须从头部检索令牌,以确保重新加载页面可以正常工作?
需要一个解释,这里有点遗漏了:o

在构造函数中设置Http.DefaultRequestHeaders.Authorization
AuthenticationStateProvider
implementation为我解决了这个问题。

当您在浏览器中键入路径或刷新页面时,是否提取并显示数据?您使用哪个浏览器?我使用的是Chrome。当我刷新时,在新选项卡中键入路径=>在请求标头中没有授权。如果没有提取数据,我将获得401(未经授权)。即使我打开一个新窗口,键入路径并获取401,然后单击指向索引的链接(单击指向我已在的路由的链接,不会重新加载页面),然后单击转到页面并获取数据,它也会在ExerciseTemplateBanks中工作。onInitializedAsync尝试此代码并报告结果…vart token=wait _localStorage.GetItemAsync(“authToken”);Http.DefaultRequestHeaders.Authorization=new AuthenticationHeaderValue(“bearer”,token);然后添加对GetJsonAsync的调用………@Gapwe您是否完全解决了问题?
        var token = await localStorage.GetItemAsync<string>("authToken");

        Http.DefaultRequestHeaders.Authorization = new authenticationHeaderValue("bearer", token);