自定义 AuthenticationProvider 中的 GetAuthenticationStateAsync 未触发 - Blazor 客户端

问题描述 投票:0回答:2

我的令牌过期遇到问题。令牌在服务器端过期,但在客户端,仅当我按 F5 时才显示过期。

App.razor代码

<CascadingAuthenticationState>
    <Router AppAssembly="@typeof(Program).Assembly">
        <Found Context="routeData">
            <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
                <NotAuthorized>
                    <MCF.BlazorClient.Components.RedirectToLogin></MCF.BlazorClient.Components.RedirectToLogin>
                </NotAuthorized>
                <Authorizing>
                    <h1>Authentication in progress</h1>

                </Authorizing>
            </AuthorizeRouteView>
        </Found>
        <NotFound>

            <LayoutView Layout="@typeof(MainLayout)">
                <p>Sorry, there's nothing at this address.</p>
            </LayoutView>

        </NotFound>
    </Router>
</CascadingAuthenticationState>

我的 AuthService 代码:

public async Task<LoginResult> Login(LoginModel loginModel)
    {
        var result = await _httpClient.PostJsonAsync<LoginResult>(GetApi("api/login"), loginModel);

        if (result.Successful)
        {
            await SetTokenAsync(result.Token, result.Expiry);
            ((ApiAuthenticationStateProvider)_authenticationStateProvider).MarkUserAsAuthenticated(result.Token);
            _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", result.Token);

            return result;
        }

        return result;
    }

    public async Task SetTokenAsync(string token, DateTime expiry = default)
    {
        if (token == null)
        {
            await _localStorage.RemoveItemAsync("authToken");
            await _localStorage.RemoveItemAsync("authTokenExpiry");
        }
        else
        {
            await _localStorage.SetItemAsync("authToken", token);
            await _localStorage.SetItemAsync("authTokenExpiry", expiry);
        }
    }

    public async Task<string> GetTokenAsync()
    {
        var expiry = await _localStorage.GetItemAsync<string>("authTokenExpiry");
        if (expiry != null)
        {
            if (DateTime.Parse(expiry.ToString()) > DateTime.Now)
            {
                return await _localStorage.GetItemAsync<string>("authToken");
            }
            else
            {
                await SetTokenAsync(null);
            }
        }
        return null;
    }

    public async Task Logout()
    {
        await _localStorage.RemoveItemAsync("authToken");
        ((ApiAuthenticationStateProvider)_authenticationStateProvider).MarkUserAsLoggedOut();
        _httpClient.DefaultRequestHeaders.Authorization = null;
    }

自定义ApiAuthenticationStateProvider代码

namespace ABC
{
    public class ApiAuthenticationStateProvider : AuthenticationStateProvider
    {
        private readonly HttpClient _httpClient;
        private readonly ILocalStorageService _localStorage;
        public List<Claim> Claims { get; set; } = new List<Claim>();

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

        public override async Task<AuthenticationState> GetAuthenticationStateAsync()
        {
            var savedToken = await _localStorage.GetItemAsync<string>("authToken");
            var expiry = await _localStorage.GetItemAsync<string>("authTokenExpiry");
            var anonymousState = new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));

            // Not authenticated
            if (string.IsNullOrWhiteSpace(savedToken))
            {
                return anonymousState;
            }

            var claims = ParseClaimsFromJwt(savedToken);

            // Checks the exp field of the token
            if (DateTime.Parse(expiry.ToString()) < DateTime.Now)
            {
                await _localStorage.RemoveItemAsync("authToken");
                await _localStorage.RemoveItemAsync("authTokenExpiry");
                MarkUserAsLoggedOut();
                return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
            }

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

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

        public void MarkUserAsAuthenticated(string token)
        {
            var authenticatedUser = new ClaimsPrincipal(new ClaimsIdentity(ParseClaimsFromJwt(token), "jwt"));
            var authState = Task.FromResult(new AuthenticationState(authenticatedUser));
            NotifyAuthenticationStateChanged(authState);
        }

        public void MarkUserAsLoggedOut()
        {
            var anonymousUser = new ClaimsPrincipal(new ClaimsIdentity());
            var authState = Task.FromResult(new AuthenticationState(anonymousUser));
            NotifyAuthenticationStateChanged(authState);
        }

        private IEnumerable<Claim> ParseClaimsFromJwt(string jwt)
        {
            var claims = new List<Claim>();
            var payload = jwt.Split('.')[1];
            var jsonBytes = ParseBase64WithoutPadding(payload);
            var keyValuePairs = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonBytes);

            keyValuePairs.TryGetValue(ClaimTypes.Role, out object roles);

            if (roles != null)
            {
                if (roles.ToString().Trim().StartsWith("["))
                {
                    var parsedRoles = JsonSerializer.Deserialize<string[]>(roles.ToString());

                    foreach (var parsedRole in parsedRoles)
                    {
                        claims.Add(new Claim(ClaimTypes.Role, parsedRole));
                    }
                }
                else
                {
                    claims.Add(new Claim(ClaimTypes.Role, roles.ToString()));
                }

                keyValuePairs.Remove(ClaimTypes.Role);
            }

            claims.AddRange(keyValuePairs.Select(kvp => new Claim(kvp.Key, kvp.Value.ToString())));
            return claims;
        }

        private byte[] ParseBase64WithoutPadding(string base64)
        {
            switch (base64.Length % 4)
            {
                case 2: base64 += "=="; break;
                case 3: base64 += "="; break;
            }
            return Convert.FromBase64String(base64);
        }
    }
}

Program.cs 中的代码用于注册身份验证提供程序:

builder.Services.AddScoped<ApiAuthenticationStateProvider>();
            builder.Services.AddScoped<AuthenticationStateProvider>(provider => provider.GetRequiredService<ApiAuthenticationStateProvider>());
            builder.Services.AddScoped<IAuthService, AuthService>();

最后在我的 MainLayout.cs 中编码

@inherits LayoutComponentBase
@inject IJSRuntime JsInterop
<TelerikRootComponent>
    <AuthorizeView>
        <Authorized>
            <div class="sidebar">
                <DynamicNavMenu />
            </div>
        </Authorized>
        <Authorizing>
            <h1>Authentication in progress</h1>
            <p>You can only see this content while authentication is in progress.</p>
        </Authorizing>
    </AuthorizeView>

                    <div class="main">
                        <div class="top-row px-4">
                            <LoginDisplay />
                            <TelerikMenu Data="@MenuItems" OnClick="@((MenuItem item) => ChangeTheme(item))" IconField="TelerikIcon" ImageUrlField="RasterImage" IconClassField="CustomFontIcon">
                            </TelerikMenu>

                        </div>

                         <div class="content px-4" id="BlazorSection">
                            @Body
                        </div>
                        <div></div>
                    </div>

@code {
    [CascadingParameter] private Task<AuthenticationState> authenticationStateTask { get; set; }
    private bool isAuthenticated = false;
    public List<MenuItem> MenuItems { get; set; }

    public class MenuItem
{
    public string Text { get; set; }
    public List<MenuItem> Items { get; set; }
}

protected override void OnInitialized()
{
    base.OnInitialized();
}

private async Task IsAuthenticated()
{
    var authState = await authenticationStateTask;
    var user = authState.User;

    if (user.Identity.IsAuthenticated)
    {

        isAuthenticated = true;
    }
    else
    {

        isAuthenticated = false;
    }
}

}

authentication state blazor jwt blazor-client-side
2个回答
0
投票

可能是

app.UseAuthentication()
中缺少
Program.cs
。它在设置服务之后、
app.Run()
之前进行。


0
投票

我也遇到过类似的情况,我实现了自定义 AuthenticationStateProvider,但 GetAuthenticationStateAsync 没有触发。

对我来说问题是我扩展了 ServerAuthenticationStateProvider,而不是 AuthenticationStateProvider。

由于某种我还没有解决的原因,ServerAuthenticationStateProvider 似乎没有触发 GetAuthenticationStateAsync。将我的自定义提供程序的基类更改为 AuthenticationStateProvider 解决了我的问题。

© www.soinside.com 2019 - 2024. All rights reserved.