Blazor 服务器身份验证
Blazor server authentication
我想为我的 Blazor 服务器端应用程序添加身份验证。我有这个代码登录。
var claims = new List<Claim>
{
new Claim(type: ClaimTypes.NameIdentifier, user.Username),
new Claim(type: ClaimTypes.Name, user.Name ?? user.Username),
new Claim(type: ClaimTypes.Sid, user.ID.ToString())
};
if (user.Email != null)
claims.Add(new Claim(type: ClaimTypes.Email, user.Email));
if (user.UserRoles != null)
{
foreach (var userRole in user.UserRoles)
{
claims.Add(new Claim(type: ClaimTypes.Role, userRole.ID_Role));
}
}
var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
var authProperties = new AuthenticationProperties
{
AllowRefresh = _authModel.Value.AllowRefresh,
ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(_authModel.Value.LoginExpirationMinutes),
IsPersistent = input.IsPersistent
};
await _httpContextAccessor.HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity), authProperties);
一切看起来都很好。代码正常运行。但是当我看到 HttpContext.User 或 AuthenticationStateProvider 时,它是空的。在资源管理器中我没有看到 cookie。我做什么不好?
非常感谢
身份验证是在您的 Blazor 应用程序内部进行的还是您正在执行任何 API 访问?
通常情况下,您会移交给 SPA 身份验证提供程序的外部人员,然后后者会使用 HttpContext.User
集回调您的 SPA。默认 AuthenticationStateProvider
然后从 HttpContext.User
.
读取用户数据
您正在 SPA 内部进行身份验证。启动页面已加载并且 HttpContext.User
已设置。我不确定您是否可以重置它。你可以写一个自定义AuthenticationStateProvider
。看看我对问题 Custom AuthenticationStateProvider in blazor project doesn't work on server side 的回答,其中显示了如何构建测试 AuthenticationStateProvider
。您应该能够将其与您的代码一起插入。
更新
这是一个非常愚蠢的AuthenticationStateProvider
,它为两个固定用户提供身份验证。从按钮调用 ChangeIdentity
进行切换。您无需尝试使用 HttpContext
.
做任何事情
using Microsoft.AspNetCore.Components.Authorization;
using System.Security.Claims;
using System.Threading.Tasks;
namespace Blazor.Auth.Test
{
public class DumbAuthenticationStateProvider : AuthenticationStateProvider
{
public static ClaimsPrincipal User
=> new ClaimsPrincipal(new ClaimsIdentity(UserClaims, "Dumb Auth Type"));
public static Claim[] UserClaims
=> new[]{
new Claim(ClaimTypes.Sid, "024672e0-250a-46fc-bd35-1902974cf9e1"),
new Claim(ClaimTypes.Name, "Normal User"),
new Claim(ClaimTypes.NameIdentifier, "Normal User"),
new Claim(ClaimTypes.Email, "user@user,com"),
new Claim(ClaimTypes.Role, "User")
};
public static ClaimsPrincipal Visitor
=> new ClaimsPrincipal(new ClaimsIdentity(VisitorClaims, "Dumb Auth Type"));
public static Claim[] VisitorClaims
=> new[]{
new Claim(ClaimTypes.Sid, "324672e0-250a-46fc-bd35-1902974cf9e1"),
new Claim(ClaimTypes.Name, "Visitor"),
new Claim(ClaimTypes.NameIdentifier, "Normal Visitor"),
new Claim(ClaimTypes.Email, "visitor@user,com"),
new Claim(ClaimTypes.Role, "Visitor")
};
bool _switch;
public override Task<AuthenticationState> GetAuthenticationStateAsync()
=> Task.FromResult(_switch
? new AuthenticationState(User)
: new AuthenticationState(Visitor)
);
public Task<AuthenticationState> ChangeIdentity()
{
_switch = !_switch;
var task = this.GetAuthenticationStateAsync();
this.NotifyAuthenticationStateChanged(task);
return task;
}
}
}
StartUp 中的服务设置:
services.AddScoped<AuthenticationStateProvider, DumbAuthenticationStateProvider>();
services.AddAuthorizationCore();
<button class="btn btn-dark" @onclick="ChangeID">Switch</button>
@code {
[Inject] private AuthenticationStateProvider authenticationStateProvider {get; set;}
private DumbAuthenticationStateProvider myAuthenticationStateProvider => authenticationStateProvider as DumbAuthenticationStateProvider;
private async Task ChangeID()
{
await myAuthenticationStateProvider.ChangeIdentity();
}
}
基于 GitHub 应用程序副本的第二次更新。
首先你需要确保新的 AuthenticationStateProvider
在 ServerSideBlazor
之后加载(它加载 ServerAuthenticationSateProvider
会重载之前加载的任何内容)并清理 StartUp:
{
//services.AddAuthentication(
// CookieAuthenticationDefaults.AuthenticationScheme)
// .AddCookie();
// services.AddAuthorization();
//services.AddScoped<AuthenticationStateProvider>(provider => provider.GetRequiredService<DumbAuthenticationStateProvider>());
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddScoped<AuthenticationStateProvider, DumbAuthenticationStateProvider>();
services.AddAuthorizationCore();
services.AddSingleton<WeatherForecastService>();
}
更新 App.razor - 将 RouteView
更改为 AuthorizeRouteView
。
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
我的测试页
@page "/fetchdata"
@using Microsoft.AspNetCore.Components.Authorization
@using WebApplication6.Data
<AuthorizeView>
<Authorized>
<div class="m-2 p-2">
<a href="Identity/Account/Manage">Hello, @context.User.Identity.Name!</a>
</div>
</Authorized>
</AuthorizeView>
<button class="btn btn-dark" @onclick="ChangeID">Switch</button>
@code {
[Inject] private AuthenticationStateProvider authenticationStateProvider { get; set; }
private DumbAuthenticationStateProvider myAuthenticationStateProvider => authenticationStateProvider as DumbAuthenticationStateProvider;
private async Task ChangeID()
{
await myAuthenticationStateProvider.ChangeIdentity();
}
}
我想为我的 Blazor 服务器端应用程序添加身份验证。我有这个代码登录。
var claims = new List<Claim>
{
new Claim(type: ClaimTypes.NameIdentifier, user.Username),
new Claim(type: ClaimTypes.Name, user.Name ?? user.Username),
new Claim(type: ClaimTypes.Sid, user.ID.ToString())
};
if (user.Email != null)
claims.Add(new Claim(type: ClaimTypes.Email, user.Email));
if (user.UserRoles != null)
{
foreach (var userRole in user.UserRoles)
{
claims.Add(new Claim(type: ClaimTypes.Role, userRole.ID_Role));
}
}
var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
var authProperties = new AuthenticationProperties
{
AllowRefresh = _authModel.Value.AllowRefresh,
ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(_authModel.Value.LoginExpirationMinutes),
IsPersistent = input.IsPersistent
};
await _httpContextAccessor.HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity), authProperties);
一切看起来都很好。代码正常运行。但是当我看到 HttpContext.User 或 AuthenticationStateProvider 时,它是空的。在资源管理器中我没有看到 cookie。我做什么不好? 非常感谢
身份验证是在您的 Blazor 应用程序内部进行的还是您正在执行任何 API 访问?
通常情况下,您会移交给 SPA 身份验证提供程序的外部人员,然后后者会使用 HttpContext.User
集回调您的 SPA。默认 AuthenticationStateProvider
然后从 HttpContext.User
.
您正在 SPA 内部进行身份验证。启动页面已加载并且 HttpContext.User
已设置。我不确定您是否可以重置它。你可以写一个自定义AuthenticationStateProvider
。看看我对问题 Custom AuthenticationStateProvider in blazor project doesn't work on server side 的回答,其中显示了如何构建测试 AuthenticationStateProvider
。您应该能够将其与您的代码一起插入。
更新
这是一个非常愚蠢的AuthenticationStateProvider
,它为两个固定用户提供身份验证。从按钮调用 ChangeIdentity
进行切换。您无需尝试使用 HttpContext
.
using Microsoft.AspNetCore.Components.Authorization;
using System.Security.Claims;
using System.Threading.Tasks;
namespace Blazor.Auth.Test
{
public class DumbAuthenticationStateProvider : AuthenticationStateProvider
{
public static ClaimsPrincipal User
=> new ClaimsPrincipal(new ClaimsIdentity(UserClaims, "Dumb Auth Type"));
public static Claim[] UserClaims
=> new[]{
new Claim(ClaimTypes.Sid, "024672e0-250a-46fc-bd35-1902974cf9e1"),
new Claim(ClaimTypes.Name, "Normal User"),
new Claim(ClaimTypes.NameIdentifier, "Normal User"),
new Claim(ClaimTypes.Email, "user@user,com"),
new Claim(ClaimTypes.Role, "User")
};
public static ClaimsPrincipal Visitor
=> new ClaimsPrincipal(new ClaimsIdentity(VisitorClaims, "Dumb Auth Type"));
public static Claim[] VisitorClaims
=> new[]{
new Claim(ClaimTypes.Sid, "324672e0-250a-46fc-bd35-1902974cf9e1"),
new Claim(ClaimTypes.Name, "Visitor"),
new Claim(ClaimTypes.NameIdentifier, "Normal Visitor"),
new Claim(ClaimTypes.Email, "visitor@user,com"),
new Claim(ClaimTypes.Role, "Visitor")
};
bool _switch;
public override Task<AuthenticationState> GetAuthenticationStateAsync()
=> Task.FromResult(_switch
? new AuthenticationState(User)
: new AuthenticationState(Visitor)
);
public Task<AuthenticationState> ChangeIdentity()
{
_switch = !_switch;
var task = this.GetAuthenticationStateAsync();
this.NotifyAuthenticationStateChanged(task);
return task;
}
}
}
StartUp 中的服务设置:
services.AddScoped<AuthenticationStateProvider, DumbAuthenticationStateProvider>();
services.AddAuthorizationCore();
<button class="btn btn-dark" @onclick="ChangeID">Switch</button>
@code {
[Inject] private AuthenticationStateProvider authenticationStateProvider {get; set;}
private DumbAuthenticationStateProvider myAuthenticationStateProvider => authenticationStateProvider as DumbAuthenticationStateProvider;
private async Task ChangeID()
{
await myAuthenticationStateProvider.ChangeIdentity();
}
}
基于 GitHub 应用程序副本的第二次更新。
首先你需要确保新的 AuthenticationStateProvider
在 ServerSideBlazor
之后加载(它加载 ServerAuthenticationSateProvider
会重载之前加载的任何内容)并清理 StartUp:
{
//services.AddAuthentication(
// CookieAuthenticationDefaults.AuthenticationScheme)
// .AddCookie();
// services.AddAuthorization();
//services.AddScoped<AuthenticationStateProvider>(provider => provider.GetRequiredService<DumbAuthenticationStateProvider>());
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddScoped<AuthenticationStateProvider, DumbAuthenticationStateProvider>();
services.AddAuthorizationCore();
services.AddSingleton<WeatherForecastService>();
}
更新 App.razor - 将 RouteView
更改为 AuthorizeRouteView
。
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
我的测试页
@page "/fetchdata"
@using Microsoft.AspNetCore.Components.Authorization
@using WebApplication6.Data
<AuthorizeView>
<Authorized>
<div class="m-2 p-2">
<a href="Identity/Account/Manage">Hello, @context.User.Identity.Name!</a>
</div>
</Authorized>
</AuthorizeView>
<button class="btn btn-dark" @onclick="ChangeID">Switch</button>
@code {
[Inject] private AuthenticationStateProvider authenticationStateProvider { get; set; }
private DumbAuthenticationStateProvider myAuthenticationStateProvider => authenticationStateProvider as DumbAuthenticationStateProvider;
private async Task ChangeID()
{
await myAuthenticationStateProvider.ChangeIdentity();
}
}