安全修剪菜单作为局部视图

Posted

技术标签:

【中文标题】安全修剪菜单作为局部视图【英文标题】:Security Trimmed Menu as Partial View 【发布时间】:2021-02-22 20:57:55 【问题描述】:

我正在 _Layout.cshtml 文件中呈现一个菜单。仅当用户处于管理员角色时才应呈现菜单的一部分。我们在数据库中使用自定义角色。 Admin 字段以单个字符“Y”或“N”返回。这最初是一个遗留应用程序,正在迁移到 MVC 5。未使用 LINQ 或实体框架。

我将菜单创建为部分视图,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>
...

控制器的操作方法是我遇到问题的地方。我的意图是返回一个布尔值,并根据部分视图中的该值简单地切换菜单受保护部分的呈现。现在它在应该显示菜单的浏览器中呈现 True 或 False,因为该布尔值作为 HTML 字符串从 action 方法返回。

我知道我可以将菜单的那部分作为字符串从操作方法返回,但我想避免这种情况,因为菜单的其他部分会根据角色值呈现。我不想以一连串的片面观点告终。如果可能的话,我还想避免使用标签助手。我只是想根据 action 方法中的布尔值来切换部分视图中 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"];

【问题讨论】:

【参考方案1】:

您的局部视图可以声明一个与之配套的模型。您可以使用@Html.RenderAction() 来代替使用@Html.Action() 将结果作为HTML 字符串返回

    运行逻辑以获取当前登录用户的角色状态 为局部视图构建视图模型 将部分视图连同视图模型直接返回给响应

这样,你如何获取用户角色状态的逻辑就被“封装”在了子action方法中,菜单部分是显示还是隐藏的逻辑被“封装”在了局部视图中。

菜单.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>

    ...

【讨论】:

感谢大卫,这很有效。我对布局文件中的 Html.RenderAction 调用的语法有一些问题。这最终奏效了:@Html.RenderAction("CheckUserAdmin"); ...“CheckUserAdmin”是您称为“BuildUsersAndRoles”的操作的名称。 @IrishChieftain:对不起,这些都是我亲手写的。我想我会使用@Html.Action() 代替,因为我不喜欢将; 放在剃须刀代码的中间。我可能应该改为调用BuildUsersAndRolesMenuSection 操作。我不喜欢CheckUserAdmin 的名字。我不喜欢将其视为检查用户是否具有管理员角色的逻辑。构建一些局部视图确实是逻辑。 好电话,我重命名了 ;-)【参考方案2】:

如您所知 Html.Action 返回 MvcHtmlString 这就是它显示 TrueFalse 的原因

你乐于尝试这样的事情吗?

_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>

【讨论】:

感谢 Raju,将整个菜单作为 HTML 字符串发回是我的 B 计划。但我需要一些可以更好地扩展的东西。【参考方案3】:

我假设单击菜单链接的操作将涉及获取控制器或页面,使用 asp.net 授权来控制访问。

这是我对 asp.net core 的看法(它不能回答你的问题,但将来可能会很有趣)。编写一个标签助手,删除用户无权查看的标签。这样您就可以重复使用相同的授权策略名称来控制可见性和权限。

    [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>

【讨论】:

我认为 OP 在 ASP.NET MVC 5 中还不是 ASP.NET Core MVC,但我不得不承认这确实是一种有趣的方法,我没有想到 :) 是的,我发现标签丢失了。

以上是关于安全修剪菜单作为局部视图的主要内容,如果未能解决你的问题,请参考以下文章

使用 Aspx 页面作为剃刀视图的局部视图

在菜单选择上渲染部分视图

将变量从视图传递到布局的局部视图

MVC的局部视图

自定义菜单作为子视图

InvalidOperationException:未找到局部视图。 MVC3.1