目前我们的MainLayout还是默认的,这里我们需要修改为BootstrapBlazor的Layout,并且处理一下菜单。

修改MainLayout

BootstrapBlazor已经自带了一个Layout组件,这个组件里常用功能已经很全了,所以我们直接使用这个组件即可。

<Layout SideWidth="0" IsPage="true" IsFullSide="true" IsFixedHeader="true" IsFixedFooter="true"
ShowFooter="true" ShowCollapseBar="true" OnCollapsed="@OnCollapsed" Menus="@_menuItems">
<Header>
<span class="ms-3 flex-sm-fill d-none d-sm-block">BlazorLearn</span>
<div class="flex-fill d-sm-none">
</div>
<Logout ImageUrl="images/argo-c.png" DisplayName="@_user.Name" UserName="@_user.UserName">
<LinkTemplate>
<LogoutLink Url="/api/account/logout"></LogoutLink>
</LinkTemplate>
</Logout>
</Header>
<Side>
<div class="layout-banner">
<img class="layout-logo" src="data:images/Argo.png" />
<div class="layout-title">
<span>BlazorLearn</span>
</div>
</div>
</Side>
<Main>
<CascadingValue Value="this" IsFixed="true">
@Body
</CascadingValue>
</Main>
<Footer>
<div class="text-center flex-fill">
<a href="/" target="_blank">BlazorLearn</a>
</div>
</Footer>
</Layout> @code
{
private bool IsCollapsed { get; set; } private List<MenuItem>? _menuItems; [NotNull]
private UserEntity? _user; private Task OnCollapsed(bool collapsed)
{
IsCollapsed = collapsed;
return Task.CompletedTask;
} protected override void OnInitialized()
{
base.OnInitialized();
_user = UserEntity.Where(x => x.UserName == Furion.App.User.FindFirstValue(ClaimTypes.Name)).First();
if (_user == null)
{
return;
}
_menuItems = CreateMenuItems(MenuEntity.Where(x => x.Roles!.Any(y => y.Id == _user.RoleId)).ToList(), 0);
} private List<MenuItem> CreateMenuItems(List<MenuEntity> menus, int parentId)
{
var selectedMenus = new List<MenuItem>();
var selectedMenuEntities = menus.Where(x => x.ParentId == parentId).ToList(); foreach (var menuEntity in selectedMenuEntities)
{
var menuItem = new MenuItem(menuEntity.Name!, menuEntity.Url, menuEntity.Icon);
menuItem.Items = CreateMenuItems(menus, menuEntity.Id);
selectedMenus.Add(menuItem);
}
return selectedMenus;
}
}

这里没什么需要多说的,每个参数的意义在文档里都比较清楚,如果需要查询具体的含义,可以看这里

这里需要注意的是,Menus里面必须要定义一个变量,不能直接放一个方法,否则此方法每次跳转都会执行,导致菜单不正常。

另外Logout是一个独立的组件,这个组件其实叫Logout并不贴切,它是一个带有头像,欢迎信息以及下拉菜单的用户信息组件。

这里我们只放一个LogoutLink登出菜单。

修改AdminHandler

如果你直接启动项目,会发现Layout不见了,因为我们的Layout里面做了比较多的处理,会有一个需要权限验证的请求。这个请求不会携带Resource,所以不会返回true。就导致Layout一直不显示,所以我们需要处理这种情况,我们目前修改为Resource里不是RouteData的全部都通过验证。

    public override Task<bool> PipelineAsync(AuthorizationHandlerContext context, DefaultHttpContext httpContext)
{
if (!int.TryParse(context.User.FindFirst(ClaimTypes.Role)?.Value, out var roleId))
{
return Task.FromResult(false);
}
if (context.Resource is RouteData routeData)
{
var routeAttr = routeData.PageType.CustomAttributes.FirstOrDefault(x =>
x.AttributeType == typeof(RouteAttribute));
if (routeAttr == null)
{
return Task.FromResult(true);
}
else
{
var url = routeAttr.ConstructorArguments[0].Value as string;
var permission = MenuEntity
.Where(x => x.Roles!.Any(y => y.Id == roleId) && x.Url == url).First();
if (permission != null)
{
return Task.FromResult(true);
}
}
}
else
{
return Task.FromResult(true);
} return Task.FromResult(false);
}

这样我们再启动应该就可以看到Layout了。

修改LoginController

我们之前只有一个登录,所以我们写了一个LoginController,现在我们需要加入登出,所以我们直接把LoginController改为AccountController,然后内容改为PostLoginGetLogout

public class AccountController: IDynamicApiController
{
public async Task<object> PostLogin([FromBody]LoginVo loginVo)
{
if (string.IsNullOrEmpty(loginVo.UserName))
{
return new { code = 50000, message = "用户名不能为空" };
}
if (string.IsNullOrEmpty(loginVo.Password))
{
return new { code = 50000, message = "密码不能为空" };
} var password = MD5Encryption.Encrypt(loginVo.Password);
var user = await UserEntity.Where(x =>
x.UserName == loginVo.UserName && x.Password == password).Include(x => x.Role).FirstAsync();
if (user != null)
{
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
identity.AddClaim(new Claim(ClaimTypes.Name, user.UserName!));
identity.AddClaim(new Claim(ClaimTypes.Role, user.Role!.Id.ToString()));
await Furion.App.HttpContext.SignInAsync(new ClaimsPrincipal(identity), new AuthenticationProperties(){IsPersistent = true, ExpiresUtc = loginVo.RememberMe? DateTimeOffset.Now.AddDays(5): DateTimeOffset.Now.AddMinutes(30)}); return new { code = 20000, message = "登录成功" };
}
return new { code = 50000, message = "用户名或密码错误" };
} [Authorize]
public async Task<IActionResult> GetLogout()
{
await Furion.App.HttpContext.SignOutAsync();
return new RedirectResult("/Login");
}
}

这里我们直接给Logout[Authorize],只有登录以后才能访问。

代码在github:https://github.com/j4587698/BlazorLearnj,分支lesson9。

从零开始Blazor Server(9)--修改Layout的更多相关文章

  1. 从零开始Blazor Server(14)--修改密码

    目前,我们只做了在用户管理里强行修改密码,而没有做用户自行修改密码的功能,今天我们来实现它. 首先,我们的用户密码修改最好的位置应该就是在头像下面的下拉菜单里,所以我们在那里的LinkTemplate ...

  2. 从零开始Blazor Server(1)--项目搭建

    项目介绍 本次项目准备搭建一个使用Furion框架,Blazor的UI使用BootstrapBlazor.数据库ORM使用Freesql的后台管理系统. 目前的规划是实现简单的注册,登录.增加管理员跟 ...

  3. 从零开始Blazor Server(3)--添加cookie授权

    认证方式简述 Blazor Server微软官方还是推荐直接使用Cookie授权,因为本来Blazor Server就是前后端不分离的.不存在Cookie跨域等一系列问题. 只要不是使用SSO之类的统 ...

  4. 从零开始Blazor Server(15)--总结

    我们用了14篇文章,基本上把一个后台管理系统需要的UI部分都说的差不多了.所以这套文章也该到了结束的时候了. 这里面有很多问题,比如我们直接使用UI来拉数据库信息而没有使用service,再比如我们大 ...

  5. 从零开始Blazor Server(6)--基于策略的权限验证

    写这个的原因 现在BootstrapBlazor处于大更新时期,Menu组件要改为泛型模式. 本来我们的这一篇应该是把Layout改了,但是改Layout肯定要涉及到菜单,如果现在写了呢,就进入一个发 ...

  6. 从零开始Blazor Server(4)--登录系统

    说明 上一篇文章中我们添加了Cookie授权,可以跳转到登录页了.但是并没有完成登录,今天我们来完成它. 我们添加Cookie授权的时候也说了,这套跟MVC一模一样,所以我们登录也是跟MVC一模一样. ...

  7. 从零开始Blazor Server(5)--权限验证

    序 之前我们一直使用的是微软自带的身份验证方式,即使用[Authorize]标签来做. 但是这种方式十分不灵活,微软推荐的方式是加Policy,但是这种方式对我们来说还是不够灵活. 所以本节我们用完全 ...

  8. 从零开始Blazor Server(8)--增加菜单以及调整位置

    这篇干啥 这篇文章主要是把前面的一些东西稍微调整一下,使其更适合后面的内容. 主要是两个事,一个是把原来的PermissionEntity直接变成MenuEntity,直接让最后一级是菜单,这样后面就 ...

  9. 从零开始Blazor Server(10)--编辑角色

    例图 目前的样式是这样的: 其中角色在一个table里,然后可以增删改查,并且可以给指定的用户分配权限. 创建文件 首先我们在Pages/Admin目录下新建一个Role.razor.因为我们的Adm ...

随机推荐

  1. 【Axure】母版引发事件

    引发事件是指你将母版中某一元件的事件从母版中提升出来,以使其在页面的级别可用. 通过引发事件,可以对在不同页面上母版实例的同一个元件设置不同的交互. 设置引发事件 打开一个母版: 选择其中一个组件: ...

  2. 【Windbg】记一次线程卡主的问题

    测试告诉我们定时任务没有执行了,排查相关日志,只有开始执行,没有执行结束.估计是什么地方卡主了. 所以dump分析日志 先检查一下加载情况 !eeversion 线程卡主了,先看线程 !runaway ...

  3. Java_Scanner的使用

    目录 Scanner对象 scanner.next()和scanner.nextln()的区别 scanner.hasNext()和scanner.hasNextln() Scanner拓展 视频课程 ...

  4. pandas:多层索引

    多层索引是指在行或者列轴上有两个及以上级别的索引,一般表示一个数据的几个分项. 1.创建多层索引 1.1通过分组产生多层索引 1.2由序列创建 1.3由元组创建 1.4可迭代对象的笛卡尔积 1.5将D ...

  5. 从零开始实现lmax-Disruptor队列(二)多消费者、消费者组间消费依赖原理解析

    MyDisruptor V2版本介绍 在v1版本的MyDisruptor实现单生产者.单消费者功能后.按照计划,v2版本的MyDisruptor需要支持多消费者和允许设置消费者组间的依赖关系. 由于该 ...

  6. 设置C#启动进程但不显示命令行窗口

    设置一下Process类型相关的配置属性即可,直接上代码. //记得引入命名空间 //using System.Diagnostics; //获得当前环境的基路径 string basePath = ...

  7. JS数组at函数(获取最后一个元素的方法)介绍

    本文介绍js中数组的at函数,属于比较简单的知识普及性文章,难度不大. 0x00 首先,我们可以思考如下一个问题,如果要获取一个数组的最后一个元素(这是很常用的操作),我们应该怎么做? 相信大部分人能 ...

  8. 关于vue打包上线遇到的坑

    打包上线经常会经常遇到路径找不到,页面空白,那么下面我们就解决一下. 第一步.先找到config目录的index.js 改成如上图红框标注所示 第二步.找到build下的utils.js文件 加上如上 ...

  9. MySQL-2-DQL

    DQL:数据查询语言 SQLyog中格式化某段语句片段:CTRL+F12 基础查询 语法: select 查询列表 from 表名: 特点: ① 查询列表可以是:表中的字段.常量值.表达式.函数 ② ...

  10. 6. RDD综合练习:更丰富的操作

    集合运算练习 union(), intersection(),subtract(), cartesian() 内连接与外连接 join(), leftOuterJoin(), rightOuterJo ...