Blazor Server未登录时隐藏页面元素并禁用URL访问的实现方案
问题描述
使用VS2022 + C#开发Blazor Server应用,已完成以下基础工作:
- 通过VS2022模板创建带独立账户的Blazor Server应用
- 使用EF将数据库迁移至PostgreSQL
- 登录/登出功能正常运行
需解决两个核心需求:
- 未登录时隐藏网页所有元素,仅显示居中的登录窗口(包含用户名、密码输入框和登录按钮)
- 未登录时禁止通过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
相关产品推荐
相关产品推荐

