概念层讲"身份是什么、放在哪、怎么按角色控制界面";动手接入见实战指南的 鉴权接入。
框架只认一个契约: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 一处。 把这套抽象理顺,换登录方案、加权限维度都是"覆写个别方法"的量级,而不是重构。
扫码关注公众号
扫码加入 QQ 群