如何在具有自动渲染模式的 .NET 8 Blazor 应用程序中使用自定义 JWT 身份验证

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

当我创建 Blazor 应用程序时,它会生成服务器和客户端应用程序。我正在应用程序的服务器端创建 Api。因此,当应用程序渲染时,它会以服务器模式渲染,并且我的页面会从 clinet 渲染。我创建了一个 custom AuthenticationStateProvider 并在服务器和客户端上注册它。 问题是我需要将令牌存储在某个地方,该令牌应该可以从服务器和客户端访问。

public class CustomAuthenticationStateProvider : AuthenticationStateProvider
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    private ClaimsPrincipal _anonymous = new ClaimsPrincipal(new ClaimsIdentity());

    public CustomAuthenticationStateProvider(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public override async Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        var token = _httpContextAccessor.HttpContext.Request.Cookies["token"];
        if (string.IsNullOrEmpty(token))
            return new AuthenticationState(_anonymous);

        var tokenHandler = new JwtSecurityTokenHandler();
        var identity = new ClaimsIdentity(tokenHandler.ReadJwtToken(token).Claims, "jwt");
        var user = new ClaimsPrincipal(identity);
        return await Task.FromResult(new AuthenticationState(user));
    }

    public void UpdateAuthenticationState(string token)
    {
        if (string.IsNullOrEmpty(token))
        {
            _httpContextAccessor.HttpContext.Response.Cookies.Delete("token");
            NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(_anonymous)));
            return;
        }

        var tokenHandler = new JwtSecurityTokenHandler();
        var identity = new ClaimsIdentity(tokenHandler.ReadJwtToken(token).Claims, "jwt");
        var user = new ClaimsPrincipal(identity);

        var cookieOptions = new CookieOptions
        {
            Expires = DateTime.Now.AddHours(1),
            HttpOnly = true,
            SameSite = SameSiteMode.Strict,
            Secure = true
        };
        _httpContextAccessor.HttpContext.Response.Cookies.Append("token", token, cookieOptions);
        NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(user)));
    }
}

使用 httpContextAccessor 时客户端没有此对象。 Error blazor.web.js:1 错误:发生一个或多个错误。 (尝试激活“Client.CustomAuthenticationStateProvider”时无法解析类型“Microsoft.AspNetCore.Http.IHttpContextAccessor”的服务。)

使用本地存储或会话存储时服务器出现 JSRuntime interpole 错误

asp.net-core blazor blazor-server-side razor-pages blazor-webassembly
1个回答
0
投票
只能在SSR模式下使用httpcontext.response.cookie。在“ServerSignalR”和“WASM”期间,不能使用它,必须使用Js interop来操作浏览器中的cookie(要确定渲染模式,请参阅我的这个答案:

How can I programmaticallyly certain the Blazor render mode) 。我已经测试了以下代码并且运行良好。 首先,在App.razor底部添加以下js脚本

<script> function test() { console.log("test"); } function WriteCookie(name, value, days) { var expires; if (days) { var date = new Date(); date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); expires = "; expires=" + date.toGMTString(); } else { expires = ""; } document.cookie = name + "=" + value + expires + "; path=/"; } function ReadCookie(name) { console.log("test"); const value = `; ${document.cookie}`; const parts = value.split(`; ${name}=`); if (parts.length === 2) return parts.pop().split(';').shift(); } </script>
确定渲染模式后,您可以选择使用httpcontext或jsinterop来设置/读取cookie。

public class CustomAuthenticationStateProvider : AuthenticationStateProvider { private readonly IHttpContextAccessor _httpContextAccessor; private readonly IJSRuntime js; public CustomAuthenticationStateProvider(IHttpContextAccessor httpContextAccessor, IJSRuntime js) { this._httpContextAccessor = httpContextAccessor; this.js = js; } public async override Task<AuthenticationState> GetAuthenticationStateAsync() { string token = ""; if (_httpContextAccessor.HttpContext != null) { if (!_httpContextAccessor.HttpContext.Response.HasStarted) { token = _httpContextAccessor.HttpContext.Request.Cookies["token"]; } else { //js.InvokeVoidAsync("test"); token =await js.InvokeAsync<string>("ReadCookie", "token"); } } else { token= await js.InvokeAsync<string>("ReadCookie", "token"); } if (string.IsNullOrEmpty(token)) { return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); } var tokenHandler = new JwtSecurityTokenHandler(); var identity = new ClaimsIdentity(tokenHandler.ReadJwtToken(token).Claims, "jwt"); return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity(identity))); } public async Task Update(string token) { if (_httpContextAccessor.HttpContext != null) { if (!_httpContextAccessor.HttpContext.Response.HasStarted) { _httpContextAccessor.HttpContext.Response.Cookies.Append("token", token); } else { await js.InvokeVoidAsync("WriteCookie", "token", token, DateTime.Now.AddMinutes(1)); } } else { await js.InvokeVoidAsync("WriteCookie", "token", token, DateTime.Now.AddMinutes(1)); } var tokenHandler = new JwtSecurityTokenHandler(); var identity = new ClaimsIdentity(tokenHandler.ReadJwtToken(token).Claims, "jwt"); NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(new ClaimsPrincipal(identity)))); } }
    
© www.soinside.com 2019 - 2024. All rights reserved.