You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Blazor Server未登录时隐藏页面元素并禁用URL访问的实现方案

问题描述

使用VS2022 + C#开发Blazor Server应用,已完成以下基础工作:

  • 通过VS2022模板创建带独立账户的Blazor Server应用
  • 使用EF将数据库迁移至PostgreSQL
  • 登录/登出功能正常运行

需解决两个核心需求:

  1. 未登录时隐藏网页所有元素,仅显示居中的登录窗口(包含用户名、密码输入框和登录按钮)
  2. 未登录时禁止通过URL直接访问私有页面(如https://localhost:7164/counter)
解决方案

1. 控制布局显示(修改MainLayout.razor)

通过AuthorizeView组件控制布局元素的渲染逻辑:未登录时仅展示@Body(即登录页面),登录成功后显示完整的侧边栏、顶部栏及页面内容。

@inherits LayoutComponentBase
@using Microsoft.AspNetCore.Components.Authorization
@inject AuthenticationStateProvider AuthenticationStateProvider

<PageTitle>msgsolutions</PageTitle>

<AuthorizeView>
    <Authorized>
        <div class="page">
            <div class="sidebar">
                <NavMenu />
            </div>
            <main>
                <div class="top-row px-4 auth">
                    <LoginDisplay />
                    <a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
                </div>
                <article class="content px-4">
                    @Body
                </article>
            </main>
        </div>
    </Authorized>
    <NotAuthorized>
        @Body
    </NotAuthorized>
</AuthorizeView>

2. 保护私有页面(添加授权特性)

给所有需要登录才能访问的页面添加[Authorize]特性,禁止未登录用户直接访问。以Counter.razor为例:

@page "/counter"
@attribute [Authorize]

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}

对所有私有页面重复此操作,首页/作为公开登录入口无需添加该特性。

3. 替换首页为居中登录页(修改Index.razor)

将Index.razor替换为以下代码,实现居中登录窗口及完整的登录验证逻辑:

@page "/"
@inject NavigationManager NavigationManager
@inject IJSRuntime jsRuntime
@inject IHttpContextAccessor httpContext
@using Microsoft.AspNetCore.Identity
@using System.Threading;

<PageTitle>OurLogin</PageTitle>

<style>
    #login .container #login-row #login-column #login-box {
        margin-top: 120px;
        max-width: 400px;
        height: 280px;
        border: 1px solid #9C9C9C;
        background-color: #EAEAEA;
        margin-left: auto;
        margin-right: auto;
    }

    input[type=submit] {
        background: #00BBFF;
    }

    #login .container #login-row #login-column #login-box #login-form {
        padding: 20px;
    }

    #login .container #login-row #login-column #login-box #login-form #register-link {
        margin-top: -85px;
    }
</style>

<div id="login">
    <h3 class="text-center text-white pt-5">Login form</h3>
    <div class="container">
        <div id="login-row" class="row justify-content-center align-items-center">
            <div id="login-column" class="col-md-6">
                <div id="login-box" class="col-md-12">
                    <form id="login-form" class="form" action="" method="post">
                        <h3 class="text-center text-info">Login</h3>
                        <EditForm Model=@login>
                            <div class="form-group">
                                <label for="Username" class="text-info">Username:</label><br>
                                <InputText @bind-Value=@login.user type="text" name="username" id="username" class="form-control" />
                            </div>
                            <div class="form-group">
                                <label for="Password" class="text-info">Password:</label><br>
                                <InputText @bind-Value=@login.password type="password" name="password" id="password" class="form-control" />
                            </div>
                            <div class="form-group">
                                <input type="submit" name="submit" class="btn btn-primary btn-outline-primary" value="Authenticate" @onclick="@(()=>IsLoginValid())" />
                            </div>
                        </EditForm>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
<p>@login.successmsg</p>

@code {
    private LoginModel login = new LoginModel();

    public async Task IsLoginValid()
    {
        string status = await UserAuth(login.password, login.user);
        string successmsg = "";
        if (status == "Authenticated!")
        {
            successmsg = "Authentication successful! Redirecting...";
            Redirect();
        }
        else if (status == "Not Authenticated!")
        {
            successmsg = "Wrong username or password! Please try again!";
        }
        else
        {
            successmsg = status;
        }
        await Task.Delay(500);
        login.successmsg = successmsg;
        StateHasChanged();
    }

    private async Task<string> UserAuth(string password, string username)
    {
        var signInManager = httpContext.HttpContext.RequestServices.GetRequiredService<SignInManager<IdentityUser>>();
        var user = await signInManager.UserManager.FindByNameAsync(username);
        if (user == null)
        {
            return "Not Authenticated!";
        }
        var result = await signInManager.CheckPasswordSignInAsync(user, password, lockoutOnFailure: false);
        if (result.Succeeded)
        {
            await signInManager.SignInAsync(user, isPersistent: false);
            return "Authenticated!";
        }
        return "Not Authenticated!";
    }

    private void Redirect()
    {
        NavigationManager.NavigateTo("/", forceLoad: true);
    }

    public class LoginModel
    {
        public string user { get; set; } = string.Empty;
        public string password { get; set; } = string.Empty;
        public string successmsg { get; set; } = string.Empty;
    }
}

4. 补充依赖注入(修改Program.cs)

确保在Program.cs中注册IHttpContextAccessor服务:

builder.Services.AddHttpContextAccessor();
效果验证
  • 未登录状态下,访问任意私有页面会自动跳转到首页的登录窗口,且无布局元素显示
  • 登录成功后自动刷新页面,显示完整的应用布局及授权内容
  • 直接输入私有页面URL会被拦截,强制返回登录页

内容的提问来源于stack exchange,提问作者BosnianFlajter2

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 15:25:10