鉴权与身份

Known 的身份抽象 IAuthProvider、[Menu].Roles 按角色显隐,以及 Cookie / Bearer 两条接入路线的定位。

最后更新

概念层讲"身份是什么、放在哪、怎么按角色控制界面";动手接入见实战指南的 鉴权接入。

框架只认一个契约:IAuthProvider

Known 不把"登录"写死成某个具体方案(不假设你一定是 Cookie、也不假设你一定有账号密码表)。它把身份这件事收敛成一个接口 IAuthProvider,页面和布局只依赖这个接口,从不关心背后是 Cookie、令牌还是桌面进程。接口一共六个成员:

  • IsAuthenticated —— 当前有没有登录用户。
  • GetUserAsync() —— 取当前用户,返回一个 UserInfo。
  • SignInAsync(user) / SignOutAsync() —— 登录、登出。
  • BuildMenus(user) / BuildAppMenus(user) —— 依据当前用户装配出他看得见的侧边栏菜单 / 移动端底部 Tab。

最后两个是 Known 的一个设计取向:登录、身份、以及"这个人能看到哪些菜单"本质是同一件事——都是"按用户与角色决定可见性"——所以干脆放进同一个契约里。这意味着你想改"某角色隐藏某菜单",只需要在 IAuthProvider 的实现里做,页面和布局一行都不用动。

默认实现 AuthProvider 给了什么

框架自带一个 AuthProvider 作为默认实现,特点是"开箱即用、但刻意保持简单":

  • 身份走进程内静态用户——SignInAsync 把用户存进一个静态字段,SignOutAsync 清空,IsAuthenticated 就是判空。这对桌面单用户、示例、原型足够,但它不跨进程、不落库,Web 多用户场景显然不能直接用这份默认。
  • 登录时会顺手给会话签发 SessionId、记录 StartTime,并写进一张在线会话登记表 OnlineTracker(心跳键 / 强制下线吊销键都用它),让"在线用户"在各宿主下表现一致。
  • 菜单装配走 [Menu] / [AppMenu] 特性扫描 + 角色过滤,分组支持两种写法:[Menu].Group 引用 Config.AddMenuGroup 声明过的分组 id(分组元数据只声明一次,父子层级靠 parent 串联),或兼容旧的斜杠路径(如 "系统管理/权限")。同层一律按 Order、再按名称排序。
  • 所有名字都过一遍 LocalService 本地化,缺译回退中文原文。

AuthProvider 的每个成员都是 virtual。正确的用法是继承它、只覆写你需要换的那几个方法——比如把身份来源从"静态字段"换成"读 Cookie",而菜单装配逻辑原样复用。

落到不同运行形态:Cookie 与 Bearer 两条路线

IAuthProvider 是传输无关的,但不同宿主天然偏好不同落地方式,理解这两条定位能帮你选对方案:

  • 交互式同源宿主 → Cookie。Web 宿主(KnownWeb)的做法是:注册 ASP.NET Core 的 Cookie 认证(例如 7 天有效期、滑动过期、HttpOnly、SameSite=Lax),再写一个 CookieAuthProvider : AuthProvider,只覆写 IsAuthenticated / GetUserAsync / SignInAsync / SignOutAsync 四个方法去读写这个认证 Cookie,并配合一个签退门禁与会话心跳。菜单装配直接继承基类默认,什么都不改。同源、随请求自动带上 Cookie,浏览器最省心。
  • 跨源 / 机器对机器 → Bearer(令牌)。当前端和后端不同源、或是 CLI、第三方服务来调接口时,靠浏览器自动携带的 Cookie 就不合适了,改用请求头里显式带令牌。框架对客户端这侧的关键约定是:ServiceProxy 遇到 401 会自动跳登录,让"未登录 / 令牌过期"的体验统一收口。具体的令牌类型、作用域(scope)与公开 API 面属正在推进的规划,接入细节以鉴权接入指南为准——本页只在概念层点明"存在这两条路线、以及它们各自适合谁"。

身份的载体:UserInfo

贯穿始终的是 UserInfo 这个模型,GetUserAsync() 返回它,菜单的角色过滤读它的 Role。核心字段:

字段 含义
Id / UserName / Name 用户主键、登录名、显示名
Role 角色字符串,是 [Menu].Roles 过滤的比对对象
Token 身份令牌(走令牌方案时使用)
SessionId 每次登录分配的会话标识,随认证 Cookie 携带;在线登记与强制下线吊销都认它
Enabled 是否启用(默认 true)
CreatedTime / LastLoginTime 建号时间 / 上次登录时间
StartTime / LastTime / LastPage 会话开始 / 最近活动时刻 / 最近所在页
Agent / OSName / Device / Browser 从 UserAgent 解析出的客户端信息,用于"在线用户"详情
Status 派生只读:5 分钟内有过活动记"在线",否则"离开"
Duration 派生只读:本次会话持续时长

按角色控制界面显隐

这是日常最常用到的一环。给一个页面挂 [Menu],可以带上 Roles:

[Menu("用户管理", Icon = "users", Group = "system", Roles = "Admin")]
public class UserManagePage : PageBase { /* @page "/users" */ }

语义很简单:Roles 留空 = 所有已登录用户可见;写了值(逗号分隔多个,如 "Admin,Manager")就只有 user.Role 命中其一的人可见。AuthProvider.CanSee 做的就是这个判断,且它对侧边栏 [Menu] 与移动端 [AppMenu] 一致。因为过滤发生在 BuildMenus(user) 里,而 BuildMenus 是虚拟方法,将来要接入更细粒度的权限体系(权限点、数据范围等),只需覆写 CanSee / BuildMenus,装配与页面代码都不用动。

顶栏的自定义动作(Config.AddTopAction)也支持同样的 roles 参数,按角色显隐。

小结与下一步

一句话记牢 Known 鉴权的心智模型:界面只依赖 IAuthProvider;默认 AuthProvider 够简单也够可继承;Web 覆写成 Cookie、跨源用 Bearer;菜单和顶栏按 Role 过滤且过滤点集中在 BuildMenus 一处。 把这套抽象理顺,换登录方案、加权限维度都是"覆写个别方法"的量级,而不是重构。

  • 具体怎么在自己的宿主里接 Cookie / 令牌、怎么写 CookieAuthProvider,见鉴权接入指南。
  • 前后端如何强类型调用、ServiceProxy 遇到 401 怎么自动跳登录,见前后端契约。
  • 菜单分组、[Menu] 装配的更多细节,见插件式模块架构。
微信公众号二维码 扫码关注公众号
QQ 交流群二维码 扫码加入 QQ 群