可能会尝试以错误的方式解决此问题,但这就是这种情况。
我有一个组件,如果用户尝试访问的登录页面未经身份验证,则重定向到该登录页面;如果未授权所请求页面的访问权限,则显示找不到页面。
<AuthorizeViewWithPermissions RequiredPermission="RequiredPermission">
<Authorized>
@ChildContent
</Authorized>
<NotAuthenticated>
<LoginRedirect />
</NotAuthenticated>
<NotAuthorized>
<NotFoundRedirect />
</NotAuthorized>
</AuthorizeViewWithPermissions>
@code {
[Parameter] public RenderFragment ChildContent { get; set; }
[Parameter] public Permissions RequiredPermission { get; set; }
protected override void OnInitialized()
{
}
}
LoginRedirect是这样的:
public class LoginRedirect : ComponentBase
{
[Inject] protected NavigationManager NavigationManager { get; set; }
protected override void OnInitialized()
{
NavigationManager.NavigateTo("/Login", true);
}
}
AuthorizeViewWithPermissions内部:
/// <summary>
/// Largely borrowed from the original AuthorizeView, but cut up a bit to use custom permissions and cut out a lot of stuff that isn't needed.
/// </summary>
public class AuthorizeViewWithPermissions : ComponentBase
{
private AuthenticationState _currentAuthenticationState;
private bool _isAuthorized;
private bool _isAuthenticated;
/// <summary>
/// The permission type required to display the content
/// </summary>
[Parameter] public Permissions RequiredPermission { get; set; }
/// <summary>
/// The content that will be displayed if the user is authorized.
/// </summary>
[Parameter] public RenderFragment<AuthenticationState> ChildContent { get; set; }
/// <summary>
/// The content that will be displayed if the user is not authorized.
/// </summary>
[Parameter] public RenderFragment<AuthenticationState> NotAuthorized { get; set; }
/// <summary>
/// The content that will be displayed if the user is not authenticated.
/// </summary>
[Parameter] public RenderFragment<AuthenticationState> NotAuthenticated { get; set; }
/// <summary>
/// The content that will be displayed if the user is authorized.
/// If you specify a value for this parameter, do not also specify a value for <see cref="ChildContent"/>.
/// </summary>
[Parameter] public RenderFragment<AuthenticationState> Authorized { get; set; }
/// <summary>
/// The content that will be displayed while asynchronous authorization is in progress.
/// </summary>
[Parameter] public RenderFragment Authorizing { get; set; }
/// <summary>
/// The resource to which access is being controlled.
/// </summary>
[Parameter] public object Resource { get; set; }
[CascadingParameter] private Task<AuthenticationState> AuthenticationState { get; set; }
/// <inheritdoc />
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
// We're using the same sequence number for each of the content items here
// so that we can update existing instances if they are the same shape
if (_currentAuthenticationState == null)
{
builder.AddContent(0, Authorizing);
}
else if (_isAuthorized)
{
var authorized = Authorized ?? ChildContent;
builder.AddContent(0, authorized?.Invoke(_currentAuthenticationState));
}
else if (!_isAuthenticated)
{
builder.AddContent(0, NotAuthenticated?.Invoke(_currentAuthenticationState));
}
else
{
builder.AddContent(0, NotAuthorized?.Invoke(_currentAuthenticationState));
}
}
/// <inheritdoc />
protected override async Task OnParametersSetAsync()
{
// We allow 'ChildContent' for convenience in basic cases, and 'Authorized' for symmetry
// with 'NotAuthorized' in other cases. Besides naming, they are equivalent. To avoid
// confusion, explicitly prevent the case where both are supplied.
if (ChildContent != null && Authorized != null)
{
throw new InvalidOperationException($"Do not specify both '{nameof(Authorized)}' and '{nameof(ChildContent)}'.");
}
if (AuthenticationState == null)
{
throw new InvalidOperationException($"Authorization requires a cascading parameter of type Task<{nameof(AuthenticationState)}>. Consider using {typeof(CascadingAuthenticationState).Name} to supply this.");
}
// First render in pending state
// If the task has already completed, this render will be skipped
_currentAuthenticationState = null;
// Then render in completed state
// Importantly, we *don't* call StateHasChanged between the following async steps,
// otherwise we'd display an incorrect UI state while waiting for IsAuthorizedAsync
_currentAuthenticationState = await AuthenticationState;
SetAuthorizedAndAuthenticated(_currentAuthenticationState.User);
}
private void SetAuthorizedAndAuthenticated(ClaimsPrincipal user)
{
var userWithData = SessionHelper.GetCurrentUser(user);
_isAuthenticated = userWithData != null;
_isAuthorized = userWithData?.Permissions.Any(p => p == RequiredPermission || p == Permissions.SuperAdmin) ?? false;
}
}
身份验证和授权检查工作正常,但是问题是页面OnInitializedAsync或OnParametersSetAsync在LoginRedirect OnInitialized之前启动。
我在OnInitializedAsync中启动了对数据的调用(调用API,该API使用存储在已登录用户数据中的令牌),这导致它尝试加载数据(没有auth令牌),而不仅仅是重定向。如果我注释掉数据调用,重定向将按预期正常工作而不会出现问题,因此这只是事件发生的时间/顺序。
有解决方案吗?我是否应该将api客户端代码更改为静默失败,而不是在缺少auth令牌的情况下抛出未经授权的异常?
这也是我的app.razor组件:
CascadingAuthenticationState>
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
<NotAuthorized>
<LoginRedirect />
</NotAuthorized>
<Authorizing>
<p>Checking authorization...</p>
</Authorizing>
</AuthorizeRouteView>
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p>These aren't the droids you're looking for.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>
答案 0 :(得分:1)
只需在调用api之前检查您是否有用户:
@inject AuthenticationStateProvider AuthenticationStateProvider
...
@code {
protected override async Task OnInitializedAsync()
{
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var user = authState.User;
if (!user.Identity.IsAuthenticated)
{
return;
}
}
}
答案 1 :(得分:0)
不熟悉内部的AuthorizeViewWithPermissions,我只能冒险地说LoginRedirect组件应该从NotAuthorized属性元素中公开,因为NotAuthorized传达了没有访问资源权限以及未经身份验证的双重含义。 。您可以通过身份验证,但不能被授权,并且仅在通过身份验证(这是默认设置)的情况下,才能被授权,除非您定义策略以更具体地满足您的要求。
在编码方面,请查看默认VS模板中的编码方式:
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
<NotAuthorized>
@if (!context.User.Identity.IsAuthenticated)
{
<RedirectToLogin />
}
else
{
<p>You are not authorized to access this resource.</p>
}
</NotAuthorized>
</AuthorizeRouteView>
同样,您发布的代码是不完整的,我只能猜测...但是我认为您的AuthorizeViewWithPermissions的设计存在问题,与我上面提到的有关。尝试以其他方式设计它。不要寻找解决方法,因为从长远来看,这可能会致命。只需根据了解系统的工作原理来更改设计即可。
希望这对您有帮助...