当我创建 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 错误
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))));
}
}