部分视图的安全修整菜单
Security Trimmed Menu as Partial View
我正在 _Layout.cshtml 文件中呈现菜单。只有当用户处于管理员角色时,菜单的一部分才应该呈现。我们在我们的数据库中使用自定义角色。 Admin 字段以单个字符“Y”或“N”形式返回。这最初是一个遗留应用程序,正在迁移到 MVC 5。未使用 LINQ 或 Entity Framework。
我将菜单创建为局部视图,Menu.cshtml:
<ul class="nav" ui-nav>
...
@if ((bool)ViewData["Admin"] == true)
{
<li>
<a href="#"><span>Users and Roles</span></a>
</li>
}
</ul>
我的 _Layout.cshtml 文件:
...
<aside id="aside" class="ui-aside">
@Html.Action("Menu")
</aside>
...
控制器的操作方法是我遇到问题的地方。我的意图是 return 一个布尔值,并根据局部视图中的该值简单地切换菜单受保护部分的呈现。现在它在应该显示菜单的浏览器中呈现 True 或 False,因为该布尔值正在 returned 作为来自 action 方法的 HTML 字符串。
我知道我可以 return 菜单的那部分作为操作方法中的字符串,但我想避免这种情况,因为菜单的其他部分将根据角色值呈现。我不想以大量的局部视图结束。如果可能的话,我也想避免使用标签助手。我只是想根据操作方法中的布尔值切换部分视图中 HTML 菜单部分的呈现。
操作方法:
[ChildActionOnly]
public bool Menu()
{
using (connection...)
{
...
object adminObject = command.ExecuteScalar();
if (adminObject != null)
{
string admin = adminObject.ToString();
if (admin == "Y")
ViewData["Admin"] = true;
else
ViewData["Admin"] = false;
}
}
return (bool)ViewData["Admin"];
}
如您所知 Html.Action
return MvcHtmlString
这就是它显示 True
或 False
的原因
你喜欢这样的尝试吗?
_Layout.cshtml
<li>@if(Html.Action("Menu").ToString().ToLower()=="true") {
@Html.Partial("_Menu")
}</li>
局部视图
<ul class="nav" ui-nav>
<li>
<a href="#"><span>Users and Roles</span></a>
</li>
</ul>
您的局部视图可以声明一个与之配套的模型。而不是使用 @Html.Action()
到 return 结果作为 HTML 字符串,你可以使用 @Html.RenderAction()
到
- 运行 您获取当前登录用户角色状态的逻辑
- 为局部视图构建视图模型
- Return 部分视图连同视图模型直接响应
这样,你的如何获取用户角色状态的逻辑就“封装”在了child action方法中,而是否显示或隐藏菜单部分的逻辑则“封装”在了分部视图中.
Menu.cshtml
在您想要呈现仅供管理员使用的部分的菜单中,您使用 @Html.RenderAction()
在 menu
控制器中执行 buildUsersAndRoles
操作(我刚刚编造的):
<ul class="nav" ui-nav>
...
@Html.RenderAction("buildUsersAndRoles", "menu", new { area = "" })
...
</ul>
MenuController.cs
基本思想是呈现逻辑以获取所需的必要数据,包括显示用户是否为管理员的标志。基于这些,您可以确定要在视图模型中传回的内容。
我这里的做法是,如果用户不是管理员,则传递 NULL,因为没有可显示的内容,否则传递包含用户和角色信息的视图模型:
public class MenuController : Controller
{
...
[ChildActionOnly]
public ActionResult BuildUsersAndRoles()
{
using (connection...)
{
...
object adminObject = command.ExecuteScalar();
if (adminObject != null)
{
string admin = adminObject.ToString();
if (admin == "Y")
{
var vm = new UsersAndRolesViewModel
{
Users = new List<...>(),
Roles = new List<...>(),
...
};
return PartialView("_UsersAndRolesPartial", vm);
}
}
return PartialView("_UsersAndRolesPartial");
}
}
...
}
_UsersAndRolesPartial.cshtml
最后,在您只想在用户具有管理员角色时显示的部分中,您可以使用视图模型:
@model ...UsersAndRolesViewModel
@if (Model != null)
{
<h5>Users and Roles</h5>
...
}
我假设点击菜单的操作 link 将涉及获取控制器或页面,使用 asp.net 授权来控制访问。
所以这是我对 asp.net 核心的看法(它没有回答你的问题,但可能对未来很有趣)。编写一个标签助手来删除用户无权查看的标签。这样您就可以重复使用相同的授权策略名称来控制可见性和权限。
[HtmlTargetElement(Attributes = "policy")]
public class PolicyTagHelper : TagHelper
{
private readonly IAuthorizationService authService;
private readonly IHttpContextAccessor httpContextAccessor;
public PolicyTagHelper(IAuthorizationService authService, IHttpContextAccessor httpContextAccessor)
{
this.authService = authService;
this.httpContextAccessor = httpContextAccessor;
}
public string Policy { get; set; }
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
if (!(await authService.AuthorizeAsync(httpContextAccessor.HttpContext.User, Policy)).Succeeded)
output.SuppressOutput();
}
}
用法;
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages(options => {
options.Conventions
.AuthorizePage("/UserRole", "Admin");
}
services.AddAuthorization(options =>
{
options.AddPolicy("Admin", policy => ...);
}
}
<ul class="nav" ui-nav>
<li policy="Admin">
<a href="#"><span>Users and Roles</span></a>
</li>
</ul>
我正在 _Layout.cshtml 文件中呈现菜单。只有当用户处于管理员角色时,菜单的一部分才应该呈现。我们在我们的数据库中使用自定义角色。 Admin 字段以单个字符“Y”或“N”形式返回。这最初是一个遗留应用程序,正在迁移到 MVC 5。未使用 LINQ 或 Entity Framework。
我将菜单创建为局部视图,Menu.cshtml:
<ul class="nav" ui-nav>
...
@if ((bool)ViewData["Admin"] == true)
{
<li>
<a href="#"><span>Users and Roles</span></a>
</li>
}
</ul>
我的 _Layout.cshtml 文件:
...
<aside id="aside" class="ui-aside">
@Html.Action("Menu")
</aside>
...
控制器的操作方法是我遇到问题的地方。我的意图是 return 一个布尔值,并根据局部视图中的该值简单地切换菜单受保护部分的呈现。现在它在应该显示菜单的浏览器中呈现 True 或 False,因为该布尔值正在 returned 作为来自 action 方法的 HTML 字符串。
我知道我可以 return 菜单的那部分作为操作方法中的字符串,但我想避免这种情况,因为菜单的其他部分将根据角色值呈现。我不想以大量的局部视图结束。如果可能的话,我也想避免使用标签助手。我只是想根据操作方法中的布尔值切换部分视图中 HTML 菜单部分的呈现。
操作方法:
[ChildActionOnly]
public bool Menu()
{
using (connection...)
{
...
object adminObject = command.ExecuteScalar();
if (adminObject != null)
{
string admin = adminObject.ToString();
if (admin == "Y")
ViewData["Admin"] = true;
else
ViewData["Admin"] = false;
}
}
return (bool)ViewData["Admin"];
}
如您所知 Html.Action
return MvcHtmlString
这就是它显示 True
或 False
你喜欢这样的尝试吗?
_Layout.cshtml
<li>@if(Html.Action("Menu").ToString().ToLower()=="true") {
@Html.Partial("_Menu")
}</li>
局部视图
<ul class="nav" ui-nav>
<li>
<a href="#"><span>Users and Roles</span></a>
</li>
</ul>
您的局部视图可以声明一个与之配套的模型。而不是使用 @Html.Action()
到 return 结果作为 HTML 字符串,你可以使用 @Html.RenderAction()
到
- 运行 您获取当前登录用户角色状态的逻辑
- 为局部视图构建视图模型
- Return 部分视图连同视图模型直接响应
这样,你的如何获取用户角色状态的逻辑就“封装”在了child action方法中,而是否显示或隐藏菜单部分的逻辑则“封装”在了分部视图中.
Menu.cshtml
在您想要呈现仅供管理员使用的部分的菜单中,您使用 @Html.RenderAction()
在 menu
控制器中执行 buildUsersAndRoles
操作(我刚刚编造的):
<ul class="nav" ui-nav>
...
@Html.RenderAction("buildUsersAndRoles", "menu", new { area = "" })
...
</ul>
MenuController.cs
基本思想是呈现逻辑以获取所需的必要数据,包括显示用户是否为管理员的标志。基于这些,您可以确定要在视图模型中传回的内容。
我这里的做法是,如果用户不是管理员,则传递 NULL,因为没有可显示的内容,否则传递包含用户和角色信息的视图模型:
public class MenuController : Controller
{
...
[ChildActionOnly]
public ActionResult BuildUsersAndRoles()
{
using (connection...)
{
...
object adminObject = command.ExecuteScalar();
if (adminObject != null)
{
string admin = adminObject.ToString();
if (admin == "Y")
{
var vm = new UsersAndRolesViewModel
{
Users = new List<...>(),
Roles = new List<...>(),
...
};
return PartialView("_UsersAndRolesPartial", vm);
}
}
return PartialView("_UsersAndRolesPartial");
}
}
...
}
_UsersAndRolesPartial.cshtml
最后,在您只想在用户具有管理员角色时显示的部分中,您可以使用视图模型:
@model ...UsersAndRolesViewModel
@if (Model != null)
{
<h5>Users and Roles</h5>
...
}
我假设点击菜单的操作 link 将涉及获取控制器或页面,使用 asp.net 授权来控制访问。
所以这是我对 asp.net 核心的看法(它没有回答你的问题,但可能对未来很有趣)。编写一个标签助手来删除用户无权查看的标签。这样您就可以重复使用相同的授权策略名称来控制可见性和权限。
[HtmlTargetElement(Attributes = "policy")]
public class PolicyTagHelper : TagHelper
{
private readonly IAuthorizationService authService;
private readonly IHttpContextAccessor httpContextAccessor;
public PolicyTagHelper(IAuthorizationService authService, IHttpContextAccessor httpContextAccessor)
{
this.authService = authService;
this.httpContextAccessor = httpContextAccessor;
}
public string Policy { get; set; }
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
if (!(await authService.AuthorizeAsync(httpContextAccessor.HttpContext.User, Policy)).Succeeded)
output.SuppressOutput();
}
}
用法;
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages(options => {
options.Conventions
.AuthorizePage("/UserRole", "Admin");
}
services.AddAuthorization(options =>
{
options.AddPolicy("Admin", policy => ...);
}
}
<ul class="nav" ui-nav>
<li policy="Admin">
<a href="#"><span>Users and Roles</span></a>
</li>
</ul>