vault backup: 2026-08-09 19:06:40
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
---
|
||||
tags: [test/review, auth, pattern, oauth2]
|
||||
create time: 2026-08-09 12:00
|
||||
---
|
||||
|
||||
# OAuth2 与 JWT — 测试题
|
||||
|
||||
## 概述
|
||||
本测试覆盖 OAuth2 四种授权模式、JWT 结构(Header/Payload/Signature)、HS256 vs RS256、Refresh Token 轮换机制以及 Token 黑名单策略。共 10 道题目(6 选择 + 3 填空 + 1 简答)。
|
||||
|
||||
---
|
||||
|
||||
## 一、选择题(6道,由浅入深)
|
||||
|
||||
> **难度阶梯**: Q1-Q2 基础概念 → Q3-Q4 核心原理 → Q5-Q6 深入应用/边界场景
|
||||
|
||||
### Q1(基础)— 考察定义层面
|
||||
|
||||
OAuth2 中目前最推荐的安全授权模式是:
|
||||
|
||||
A. Authorization Code + PKCE
|
||||
B. Implicit
|
||||
C. Password
|
||||
D. Client Credentials
|
||||
|
||||
### Q2(基础)→
|
||||
|
||||
以下哪种 OAuth2 授权模式**不需要用户参与**?
|
||||
|
||||
A. Authorization Code
|
||||
B. Implicit
|
||||
C. Password
|
||||
D. Client Credentials
|
||||
|
||||
### Q3(进阶)— 核心原理
|
||||
|
||||
JWT 的 Header + Payload → Signature 的计算过程是:
|
||||
|
||||
A. base64UrlEncode(header) + "." + base64UrlEncode(payload),然后用 secret 或私钥签名
|
||||
B. 直接将三个部分拼接后编码为 Base64
|
||||
C. 将 payload 用私钥加密
|
||||
D. 将 header 和 payload 分别签名后拼接
|
||||
|
||||
### Q4(进阶)— 比较/辨析
|
||||
|
||||
微服务架构为什么推荐 RS256(非对称)而不是 HS256(对称)?
|
||||
|
||||
A. RS256 性能更好
|
||||
B. 每个微服务只需验证公钥而不需要持有签发私钥,即使某个服务被攻陷也无法伪造其他服务的 token
|
||||
C. HS256 不支持自定义 claim
|
||||
D. RS256 不需要设置 exp 过期时间
|
||||
|
||||
### Q5(深入)— 场景推理
|
||||
|
||||
在 Refresh Token 轮换机制中,如果同一个 Refresh Token RT1 被使用了两次(第一次正常刷新生成了 RT2+AT2,第二次攻击者重放了旧的 RT1),第二次的处理策略是什么?
|
||||
|
||||
A. 正常刷新生成新的 RT3+AT3
|
||||
B. 忽略重复请求返回之前的 AT2
|
||||
C. 级联销毁该用户的所有 token 并要求重新登录
|
||||
D. 只冻结 RT1 但不影响其他 token
|
||||
|
||||
### Q6(深入)— 源码级/边界场景
|
||||
|
||||
关于 JWT 的使用,以下哪个做法是错误的?
|
||||
|
||||
A. Access Token 设置为短生命周期(如 5~15 分钟)
|
||||
B. Refresh Token 存放在 HttpOnly Cookie 而非 localStorage
|
||||
C. 把用户密码放在 JWT payload 中以方便验签时读取
|
||||
D. RS256 模式下验证方只用公钥本地验签零网络调用
|
||||
|
||||
---
|
||||
|
||||
## 二、填空题(3道)
|
||||
|
||||
### F1 — 填空1
|
||||
|
||||
JWT 标准保留声明(Registered Claims)包括六个:`iss`(_____)、`sub`(主题/用户标识)、`aud`(_____)、`exp`(过期时间)、`iat`(签发时间)、`jti`(JWT ID 唯一标识支持撤销)。请填写其中两个中文名称。
|
||||
|
||||
> **提示**: iss = Issuer, aud = Audience。
|
||||
|
||||
### F2 — 填空2
|
||||
|
||||
Token 管理常用的混合策略是:**短生命周期 Access Token(5 分钟)** + 本地校验(无需网络查询);**Refresh Token 存储在_____**每次刷新做一次性校验;特殊场景下的**黑名单**用于主动登出和密码修改后的批量踢人。
|
||||
|
||||
> **提示**: 回忆原文 Token 管理混合策略的描述。
|
||||
|
||||
### F3 — 填空3
|
||||
|
||||
PKCE(Proof Key for Code Exchange)通过 `code_verifier` / `code_challenge` 对替代 `client_secret`,使 Authorization Code 对所有客户端类型(包括无法安全存储 client_secret 的_____ App 和 SPA)都是安全的。
|
||||
|
||||
> **提示**: PKCE 解决的问题是让没有秘密的客户端也能安全使用 Authorization Code 模式。
|
||||
|
||||
---
|
||||
|
||||
## 三、简答题(1道)
|
||||
|
||||
### S1
|
||||
|
||||
面试官问:"某系统的认证架构如下:前端收到 AT(Access Token)存在 localStorage 中,后端用 HS256 签名,RT(Refresh Token)也放在 localStorage 里每次刷新再生成新 RT。请指出这个设计中的至少三个安全问题并给出改进方案。"
|
||||
|
||||
> **答题框架提示**:
|
||||
> 1. Token 存储位置的安全性
|
||||
> 2. 签名算法的选择
|
||||
> 3. Refresh Token 的轮换安全性
|
||||
> 4. 补充:是否还有其他可改进之处
|
||||
|
||||
---
|
||||
|
||||
## 参考答案与解析
|
||||
|
||||
### 选择题答案
|
||||
|
||||
| 题号 | 正确答案 | 解析 |
|
||||
|------|---------|------|
|
||||
| Q1 | A | Authorization Code + PKCE 是当前最推荐的方案。Code 在后端交换(浏览器拿不到 token),PKCE 进一步解决了 SPA/Mobile 无法保护 client_secret 的问题。Implicit 已废弃,Password 仅限第一方应用。 |
|
||||
| Q2 | D | Client Credentials 没有用户参与——客户端用自己的身份(client_id + client_secret)申请令牌。典型场景:后台定时任务、微服务间内部调用、CI/CD 流水线。 |
|
||||
| Q3 | A | 标准的 JWT 签名计算流程:base64UrlEncode(header) + "." + base64UrlEncode(payload),然后 HMACSHA256 或用 RSA 私钥签名。Base64 是可逆编码不是加密。 |
|
||||
| Q4 | B | RS256 的核心优势是每个服务只需公钥就能验证——即使某台服务被攻陷,攻击者只有公钥无法伪造 token(因为签名需要私钥)。HS256 要求所有服务共享同一个密钥,任何一台泄漏导致全局崩溃。 |
|
||||
| Q5 | C | 原文明确:如果同一个 Refresh Token 出现两次,第二次判定为窃听攻击,同时**级联销毁该用户的所有 token 并要求重新登录**。这是最安全的防御策略。 |
|
||||
| Q6 | C | JWT 只是 Base64 编码而非加密,任何人都可以解码阅读 payload。**绝对不要把敏感信息放在 JWT payload 中**。如果需要保密应使用 JWE(加密型 JWT)。 |
|
||||
|
||||
### 填空题答案
|
||||
|
||||
| 题号 | 答案 | 解析 |
|
||||
|------|------|------|
|
||||
| F1 | `Issuer(签发者)`;`Audience(接收方)` | 六大标准声明中,iss 标识谁签发的,aud 标识谁应该接收这个 token。配合 exp(过期时间)实现完整的生命周期控制。jti 提供撤销能力。 |
|
||||
| F2 | `Redis` | 混合策略的三段论:① 短 AT 本地验证;② RT 存 Redis 单次校验防重放;③ 黑名单应对特殊情况。这种组合兼顾性能和安全性。 |
|
||||
| F3 | `移动`(或"Mobile") | RFC 7636 定义的 PKCE 让 Authorization Code 模式适用于所有客户端类型——不仅是传统的有后端的服务,也包括无法安全存储 client_secret 的移动 App 和 SPA。 |
|
||||
|
||||
### 简答题参考答案
|
||||
|
||||
S1:**参考答案要点**:
|
||||
1. **localStorage 安全风险**:AT 和 RT 都存在 localStorage 中容易被 XSS 窃取。改进:AT 可以放在内存变量中(页面刷新需重新登录),RT 必须放在 HttpOnly Cookie(JS 无法读取)。
|
||||
2. **HS256 密钥泄露风险**:所有微服务共享同一个 secret,一旦某台服务器被攻陷全局信任崩溃。改进:改用 RS256,各服务用不同公钥独立验证。
|
||||
3. **Refresh Token 未旋转**:虽然每次生成新 RT,但如果缺少"同一 RT 出现两次触发级联吊销"的检测,攻击者可以用旧 RT 无限次重放而不会被发现。改进:增加 RT 消费记录检查逻辑。
|
||||
4. **补充 - 缺少 state 防护**:Authorization Code 回调时未校验 state 参数可能导致 CSRF 攻击。改进:生成随机 state 存 cookie 中,回调时比对。
|
||||
5. **补充 - 无 token 黑名单**:用户登出或改密码后无法立即吊销已发出的 AT。改进:引入 Redis 黑名单或利用短生命周期 + RT 强制重新认证缓解。
|
||||
|
||||
**评分标准**:答出任意 2 个要点即可得满分;完全正确需覆盖全部要点。
|
||||
|
||||
## 关联笔记
|
||||
- [[RBAC 权限模型]]
|
||||
@@ -0,0 +1,142 @@
|
||||
---
|
||||
tags: [test/review, auth, pattern, rbac]
|
||||
create time: 2026-08-09 12:00
|
||||
---
|
||||
|
||||
# RBAC 权限模型 — 测试题
|
||||
|
||||
## 概述
|
||||
本测试覆盖 RBAC 五张表设计、用户→角色→权限两层映射、超级管理员继承机制、互斥职责分离(SoD)以及动态权限加载流程。共 10 道题目(6 选择 + 3 填空 + 1 简答)。
|
||||
|
||||
---
|
||||
|
||||
## 一、选择题(6道,由浅入深)
|
||||
|
||||
> **难度阶梯**: Q1-Q2 基础概念 → Q3-Q4 核心原理 → Q5-Q6 深入应用/边界场景
|
||||
|
||||
### Q1(基础)— 考察定义层面
|
||||
|
||||
RBAC 的核心思想是:
|
||||
|
||||
A. 直接将用户与权限绑定
|
||||
B. 将用户与角色关联,而非将用户与权限直接绑定
|
||||
C. 根据用户的部门自动分配所有权限
|
||||
D. 基于用户的行为模式动态生成权限
|
||||
|
||||
### Q2(基础)→
|
||||
|
||||
以下哪个表**不是**标准 RBAC 五张表中的一部分?
|
||||
|
||||
A. users(用户表)
|
||||
B. roles(角色表)
|
||||
C. user_roles(用户-角色关联表)
|
||||
D. permissions(权限表)
|
||||
E. role_hierarchy(角色层级表)
|
||||
|
||||
### Q3(进阶)— 核心原理
|
||||
|
||||
RBAC 引入角色层相比传统 ACL(Access Control List)的最大优势是什么?
|
||||
|
||||
A. 权限检查更快
|
||||
B. 批量授权更简单——只需改角色的权限映射不影响用户
|
||||
C. 支持更多种权限码格式
|
||||
D. 不需要数据库支持
|
||||
|
||||
### Q4(进阶)— 比较/辨析
|
||||
|
||||
关于 RBAC 中的互斥职责分离(Separation of Duty),以下说法哪个是正确的?
|
||||
|
||||
A. 应该在每次鉴权时检查互斥约束
|
||||
B. 应该在分配角色层校验,而不是每次鉴权时检查
|
||||
C. 只在创建角色时检查一次就够了
|
||||
D. SoD 只在财务系统中需要,其他系统不需要
|
||||
|
||||
### Q5(深入)— 场景推理
|
||||
|
||||
某中型业务系统有约 500 个用户、20 个角色、每种角色 10~30 个权限。以下哪种部署方案最合理?
|
||||
|
||||
A. 简单角色 + 硬编码判断即可
|
||||
B. 标准五表 RBAC + Redis 缓存(O(1) 鉴权)
|
||||
C. 平台型 SaaS 的每租户自定义角色
|
||||
D. 只做 ACL 逐个绑定用户和权限
|
||||
|
||||
### Q6(深入)— 源码级/边界场景
|
||||
|
||||
在查询某用户全部权限并展开角色继承时,如果该用户有一个 `is_super = 1` 的角色,函数应该返回什么?
|
||||
|
||||
A. 空列表表示需要进一步展开
|
||||
B. 该角色的直接权限码列表
|
||||
C. `["*"]` 表示拥有所有权限
|
||||
D. 报错"超级管理员不应直接查询权限"
|
||||
|
||||
---
|
||||
|
||||
## 二、填空题(3道)
|
||||
|
||||
### F1 — 填空1
|
||||
|
||||
RBAC 五张标准表的 ER 关系是:users 多对多通过 _____ 关联到 roles;roles 多对多通过 _____ 关联到 permissions。这两个关联表是 RBAC 灵活性的关键。
|
||||
|
||||
> **提示**: 回想五个表的名称。
|
||||
|
||||
### F2 — 填空2
|
||||
|
||||
登录时将完整权限树缓存在_____中,鉴权接口只需 O(1) 读取缓存。权限变更时主动删除对应用户的缓存条目实现最终一致。**不要每次都查库**。建议 TTL 设为 _____。
|
||||
|
||||
> **提示**: 第一个空填存储技术;第二个空填时间长度。
|
||||
|
||||
### F3 — 填空3
|
||||
|
||||
金融类强合规系统的 RBAC 部署应包含:标准五表 RBAC + SoD 互斥约束 + 完整的_____日志。审计日志记录每次权限操作以供合规审查。
|
||||
|
||||
> **提示**: 四个字的名词,描述安全相关的记录行为。
|
||||
|
||||
---
|
||||
|
||||
## 三、简答题(1道)
|
||||
|
||||
### S1
|
||||
|
||||
面试官问:"一个电商后台系统有以下角色需求:运营人员可以查看商品列表但不能编辑;编辑可以修改商品信息但不可删除;管理员拥有所有权限但受 SoD 限制——同一个人不能同时拥有'采购'和'审核'角色。请设计一套 RBAC 方案,包括表结构、权限码命名规范、互斥规则配置以及运行时鉴权流程。"
|
||||
|
||||
> **答题框架提示**:
|
||||
> 1. 角色与权限码设计
|
||||
> 2. 互斥约束的配置方式
|
||||
> 3. 鉴权流程与缓存策略
|
||||
> 4. 异常场景处理
|
||||
|
||||
---
|
||||
|
||||
## 参考答案与解析
|
||||
|
||||
### 选择题答案
|
||||
|
||||
| 题号 | 正确答案 | 解析 |
|
||||
|------|---------|------|
|
||||
| Q1 | B | RBAC 的核心是将用户与角色关联(而非直接绑定权限)。这层间接大幅降低管理复杂度——调整某个角色的权限只改一处映射,不需要遍历所有用户。 |
|
||||
| Q2 | E | 标准五表是:users、roles、permissions、user_roles、role_permissions。role_hierarchy 不是独立表——原文用 parent_id 字段在 roles 表中实现层级关系。 |
|
||||
| Q3 | B | ACL 的问题是批量授权需逐一修改每个用户;RBAC 只需改角色的权限映射。速度不是主要差异,灵活性才是核心价值。 |
|
||||
| Q4 | B | 原文明确指出:互斥约束应在**分配角色层**校验,而不是每次鉴权时检查。前者是策略问题后者是执行问题——入口处挡住比到处拦击效率高得多。 |
|
||||
| Q5 | B | 小型系统(<100 用户)可用硬编码;中型系统用标准五表+Redis 缓存;SaaS 需要租户隔离;ACL 不适合超过几个人的规模。500 用户属于中型。 |
|
||||
| Q6 | C | 当 is_super=1 时直接返回 `["*"]` 表示拥有所有权限。这是递归展开权限时的短路优化——不需要再去查每条具体的权限记录。 |
|
||||
|
||||
### 填空题答案
|
||||
|
||||
| 题号 | 答案 | 解析 |
|
||||
|------|------|------|
|
||||
| F1 | `user_roles`;`role_permissions` | user_roles 是多对中间表(user_id + role_id 联合主键);role_permissions 也是联合主键。没有这两个表用户和权限之间的传递关联就无法建立。 |
|
||||
| F2 | `Redis`;`2h` | 登录时将完整权限树写入 Redis(TTL=2h),之后每次鉴权 O(1) 读取。权限变更时主动删除对应缓存实现最终一致。不查库是关键性能优化。 |
|
||||
| F3 | `审计` | 强合规系统必须完整记录所有权限相关操作(谁在什么时候获得了什么权限),审计日志不可篡改且长期保存,用于第三方合规审查。 |
|
||||
|
||||
### 简答题参考答案
|
||||
|
||||
S1:**参考答案要点**:
|
||||
1. **角色与权限码设计**:定义三个角色 Admin/Edit/Ops。权限码采用 `{resource}:{action}` 格式,如 `product:read`、`product:update`、`product:delete`、`purchase:create`、`purchase:approve`。Admin 获得 product 全量权限,Edit 只有 update,Ops 只有 read。
|
||||
2. **互斥约束**:在 conflict_roles 表中插入 `(purchase_role_id, approve_role_id)` 一条互斥规则。用户在分配角色时先查询冲突表,如果已持有其中一个就不能分配另一个。
|
||||
3. **鉴权流程**:登录时查五表获取用户全部权限码(含角色继承展开),写入 Redis(TTL=2h)。后续请求通过 HTTP Middleware 从 Redis 读取做 O(1) 比对。权限变更时主动删除 Redis 缓存。
|
||||
4. **异常处理**:SoD 冲突检测失败时返回明确错误信息告知用户哪两个角色互斥。Redis 宕机时 fallback 到查库但不作为常规路径。
|
||||
|
||||
**评分标准**:答出任意 2 个要点即可得满分;完全正确需覆盖全部要点。
|
||||
|
||||
## 关联笔记
|
||||
- [[OAuth2 与 JWT]]
|
||||
@@ -0,0 +1,144 @@
|
||||
---
|
||||
tags: [test/review, fsm, pattern, workflow-engine]
|
||||
create time: 2026-08-09 12:00
|
||||
---
|
||||
|
||||
# 有限状态机在业务中的应用 — 测试题
|
||||
|
||||
## 概述
|
||||
本测试覆盖 FSM 的三种典型业务场景:审批流、订单生命周期和资源交付八阶段,以及非法转移防护和并发安全要求。共 10 道题目(6 选择 + 3 填空 + 1 简答)。
|
||||
|
||||
---
|
||||
|
||||
## 一、选择题(6道,由浅入深)
|
||||
|
||||
> **难度阶梯**: Q1-Q2 基础概念 → Q3-Q4 核心原理 → Q5-Q6 深入应用/边界场景
|
||||
|
||||
### Q1(基础)— 考察定义层面
|
||||
|
||||
以下哪条订单状态转换是**非法**的?
|
||||
|
||||
A. PendingPayment → Paid(用户支付)
|
||||
B. Paid → Cancelled(已支付的订单直接取消)
|
||||
C. Shipped → Completed(用户确认收货)
|
||||
D. Refunding → Refunded(退款完成)
|
||||
|
||||
### Q2(基础)→
|
||||
|
||||
审批流中"驳回"的语义可以是:
|
||||
|
||||
A. 只有一种——终止流程回到终态
|
||||
B. 只有一种——退回上一步到前置状态
|
||||
C. 两种都有可能——终止或退回,需要在模型中明确区分
|
||||
D. 不是有效的状态转换事件
|
||||
|
||||
### Q3(进阶)— 核心原理
|
||||
|
||||
资源交付八阶段中,为什么删除必须是**两阶段**的?
|
||||
|
||||
A. 因为存储 API 不支持一次性删除
|
||||
B. 先标记 CleanupPending 再执行物理删除,避免误删且支持回滚
|
||||
C. 因为需要等待 CDN 节点同步
|
||||
D. 因为需要先通知用户
|
||||
|
||||
### Q4(进阶)— 比较/辨析
|
||||
|
||||
以下关于三种业务场景 FSM 的对比描述哪个是正确的?
|
||||
|
||||
A. 审批流的循环转移最多(有退回修改)
|
||||
B. 订单生命周期的并发安全要求最低(单人审批)
|
||||
C. 资源交付是线性流程无循环转移
|
||||
D. 三种场景的状态数量都在 5~8 之间
|
||||
|
||||
### Q5(深入)— 场景推理
|
||||
|
||||
某电商平台的订单出现了一个状态转换请求:`Paid → Cancelled`(已支付后申请取消)。但系统校验发现这个转移是非法的。最合理的处理方式是:
|
||||
|
||||
A. 静默忽略,不报错也不执行
|
||||
B. 返回错误 "invalid transition: PAID -> [CANCELLED]"
|
||||
C. 自动将订单转为 Shipping 状态
|
||||
D. 将该请求丢入后台慢慢处理
|
||||
|
||||
### Q6(深入)— 源码级/边界场景
|
||||
|
||||
原文提到审批链动态变化(A 请假了由 B 代审)时不应使用什么方案?
|
||||
|
||||
A. FSM 框架
|
||||
B. 策略模式 + 规则引擎
|
||||
C. 硬编码审批人
|
||||
D. 数据库持久化
|
||||
|
||||
---
|
||||
|
||||
## 二、填空题(3道)
|
||||
|
||||
### F1 — 填空1
|
||||
|
||||
审批流审计追踪中每次状态转换必须记录三个关键字段:_____、timestamp、comment。这些信息不可删除,用于事后合规审查。
|
||||
|
||||
> **提示**: 回想原文表格中的字段名。
|
||||
|
||||
### F2 — 填空2
|
||||
|
||||
资源交付场景中,"流量统计"被建模为_____而不是事件,因为流量统计是一个持续监控行为而非瞬时动作。在实际工程中它可能通过 sidecar 或事件总线持续运行,不受单用户的控制流影响。
|
||||
|
||||
> **提示**: 回忆 FSM 的四个核心概念中哪个对应这个描述。
|
||||
|
||||
### F3 — 填空3
|
||||
|
||||
订单生命周期并发安全要求_____,因为多人同时操作同一订单的场景频繁发生。解决方案是每步写 DB + 事件日志,必要时加乐观锁或悲观锁。
|
||||
|
||||
> **提示**: 原文中的形容词,表示一种程度级别。
|
||||
|
||||
---
|
||||
|
||||
## 三、简答题(1道)
|
||||
|
||||
### S1
|
||||
|
||||
面试官问:"在一个在线学习平台中,一个课程工单从创建到完成会经历多个阶段:CREATED → ENROLLED → IN_PROGRESS → COMPLETED / DROPPED / REFUNDED。其中 REFUNDING 子流程包含 REFUND_REVIEW → REFUND_SUCCESS / REFUND_REJECT。如果 REFUND_REJECT 则回退到 IN_PROGRESS。请设计一个 FSM 状态图,并回答以下问题:
|
||||
1. 哪些转换是非法的?
|
||||
2. 如何防止用户在退款审核中途重复提交退款申请?
|
||||
3. 如何处理退款失败后的状态恢复?"
|
||||
|
||||
> **答题框架提示**:
|
||||
> 1. 画出完整状态转换图(用文字描述节点和边)
|
||||
> 2. 分析非法转换场景
|
||||
> 3. 讨论幂等保护机制
|
||||
> 4. 异常路径的设计
|
||||
|
||||
---
|
||||
|
||||
## 参考答案与解析
|
||||
|
||||
### 选择题答案
|
||||
|
||||
| 题号 | 正确答案 | 解析 |
|
||||
|------|---------|------|
|
||||
| Q1 | B | 原文列出的非法转移示例:Paid → Cancelled(已支付的订单不能直接取消),应该走 Refunding → Refunded 路径。Completed → Shipped 和 PendingPayment → Shipped 也是非法的。 |
|
||||
| Q2 | C | 原文明确指出:"驳回"可以是终止(回终态 Rejected)也可以是退回上一步(回到前置状态 ManagerRevise → Draft)。需要在模型中明确区分,不能混为一谈。 |
|
||||
| Q3 | B | 原文警告:资源删除必须是两阶段的——先标记 CleanupPending 再执行物理删除。跳过这个阶段是数据丢失的头号原因。这提供了误删回滚的安全网。 |
|
||||
| Q4 | C | A 错:订单生命周期也有循环(Refunding → Paid 退款拒绝→重新支付);B 错:订单并发安全要求极高;D 错:资源交付是 8 个状态不在 5~8 的下限范围内。只有 C 正确——资源交付是线性流程无循环。 |
|
||||
| Q5 | B | 非法转移应返回明确的错误信息而非静默忽略。原文明确说:"非法转移应返回错误或被拒绝,而非静默忽略"。这样调用方能知道哪里出了问题。 |
|
||||
| Q6 | C | 原文指出:审批链动态变化时硬编码审批人在生产环境中是不可接受的。应引入策略模式 + 规则引擎,让审批人分配变成可配置的。 |
|
||||
|
||||
### 填空题答案
|
||||
|
||||
| 题号 | 答案 | 解析 |
|
||||
|------|------|------|
|
||||
| F1 | `operator` | 审批流审计追踪的三个核心字段:operator(谁操作的)、timestamp(何时操作)、comment(什么理由)。这些数据不可删除以保障合规性。 |
|
||||
| F2 | `状态` | 原文原话:"流量统计是一个持续的监控行为,不是一个瞬时动作。FSM 在这里只记录'是否已进入此阶段'"。它是一个持续的行为标记。 |
|
||||
| F3 | `极高` | 订单场景多人同时操作(如用户支付、客服退款、仓库发货可能在极短时间内相继发生),必须有强并发保护(乐观锁、事务、事件日志)。 |
|
||||
|
||||
### 简答题参考答案
|
||||
|
||||
S1:**参考答案要点**:
|
||||
1. **完整状态转换图**:CREATED(初始) → ENROLLED(报名) → IN_PROGRESS(进行中)。IN_PROGRESS → COMPLETED(完成) / DROPPED(退出) / REFUNDING(申请退款)。REFUNDING → REFUND_REVIEW(审核中) → REFUND_SUCCESS(成功) → IN_PROGRESS(恢复) / REFUND_REJECT(拒绝) → IN_PROGRESS(继续学习)。
|
||||
2. **非法转换**:CREATED → COMPLETED(未报名就完成);ENROLLED → DROPPED(需要先开始才允许退出);COMPLETED → IN_PROGRESS(已完成不能倒回去继续学);IN_PROGRESS → CREATED(只能前进不能后退到更早状态)。
|
||||
3. **防重复退款**:在 REFUNDING 状态下设置屏蔽——只有在 IN_PROGRESS 才能发起退款。收到退款请求时检查当前状态,如果不是 IN_PROGRESS 直接拒绝。配合数据库唯一约束做最终兜底。
|
||||
4. **恢复设计**:REFUND_REJECT 回到 IN_PROGRESS 是因为退款失败不代表要退出课程。但如果是 REFUND_SUCCESS 则直接进入已完成状态(因为已经退款,服务关系终结)。
|
||||
|
||||
**评分标准**:答出任意 2 个要点即可得满分;完全正确需覆盖全部要点。
|
||||
|
||||
## 关联笔记
|
||||
- [[有限状态机核心概念]]
|
||||
@@ -0,0 +1,141 @@
|
||||
---
|
||||
tags: [test/review, fsm, pattern, finite-state-machine]
|
||||
create time: 2026-08-09 12:00
|
||||
---
|
||||
|
||||
# 有限状态机核心概念 — 测试题
|
||||
|
||||
## 概述
|
||||
本测试覆盖 FSM 四大核心概念(State/Event/Transition/Action)、DFA vs NFA、状态转移方程以及三种工程实现方式(switch-case/map-of-function/State接口)。共 10 道题目(6 选择 + 3 填空 + 1 简答)。
|
||||
|
||||
---
|
||||
|
||||
## 一、选择题(6道,由浅入深)
|
||||
|
||||
> **难度阶梯**: Q1-Q2 基础概念 → Q3-Q4 核心原理 → Q5-Q6 深入应用/边界场景
|
||||
|
||||
### Q1(基础)— 考察定义层面
|
||||
|
||||
FSM 的四元组模型 `(States, Events, Transitions, Actions)` 中缺少了哪个原文提到的第五元素?
|
||||
|
||||
A. StartState(初始状态)
|
||||
B. EndState(终止状态)
|
||||
C. CurrentState(当前状态)
|
||||
D. DefaultAction(默认动作)
|
||||
|
||||
### Q2(基础)→
|
||||
|
||||
以下关于 DFA 和 NFA 的描述哪个是正确的?
|
||||
|
||||
A. DFA 允许一个状态对同一事件转移到多个后继状态
|
||||
B. NFA 的行为可预测,适合工程实现
|
||||
C. 工程实践中的 FSM 几乎总是 DFA
|
||||
D. NFA 比 DFA 表达能力更强
|
||||
|
||||
### Q3(进阶)— 核心原理
|
||||
|
||||
关于 FSM 的状态转移函数 δ(current_state, event) → next_state,以下说法哪个是正确的?
|
||||
|
||||
A. 转移函数必须是全函数——所有事件在所有状态下都必须有定义
|
||||
B. 转移函数必须是部分函数——并非所有事件在所有状态下都有效
|
||||
C. 非法转移应该静默忽略而非报错
|
||||
D. 转移函数可以返回多个后继状态以支持并行执行
|
||||
|
||||
### Q4(进阶)— 比较/辨析
|
||||
|
||||
在 Go 中实现 FSM 时,如果状态数少于 10 且逻辑简单,最推荐的实现方式是:
|
||||
|
||||
A. State 接口模式
|
||||
B. map-of-function-map
|
||||
C. switch-case 枚举
|
||||
D. 手写一个完整的 FSM 框架库
|
||||
|
||||
### Q5(深入)— 场景推理
|
||||
|
||||
某系统有 60 个不同的业务状态,每个状态的转换行为复杂且涉及大量副作用(发送邮件、写审计日志、调用外部 API)。以下哪种 FSM 实现方式最适合?
|
||||
|
||||
A. switch-case 枚举(60+ case 分支)
|
||||
B. map-of-function-map(闭包传递上下文繁琐)
|
||||
C. State 接口 + Context 模式(单一职责,面向对象)
|
||||
D. if-else 链
|
||||
|
||||
### Q6(深入)— 源码级/边界场景
|
||||
|
||||
原文的警告中提到:"常见误区是_____。"这个误区具体指什么?
|
||||
|
||||
A. 用数字枚举做状态校验
|
||||
B. 把 FSM 当成普通的 if-else
|
||||
C. 忘记设置初始状态
|
||||
D. 没有为每个状态实现 Enter 方法
|
||||
|
||||
---
|
||||
|
||||
## 二、填空题(3道)
|
||||
|
||||
### F1 — 填空1
|
||||
|
||||
FSM 的五元组是:`(States, Events, Transitions, Actions, _____)`。当转移存在 Action 时,转移函数扩展为 `δ(current_state, event) → (next_state, _____)`。
|
||||
|
||||
> **提示**: 回忆五元组和扩展转移函数的原句。
|
||||
|
||||
### F2 — 填空2
|
||||
|
||||
面试常考点:不建议用数字枚举 + 硬编码数组来做状态校验,因为数字枚举不具备_____表达能力,编译期无法捕获非法转移。
|
||||
|
||||
> **提示**: 形容词,表示状态名称能传达的信息含义。
|
||||
|
||||
### F3 — 填空3
|
||||
|
||||
Switch-case 方案适用状态数 ≤ _____;map-of-function 适用 _____~50;State 接口适用 > _____。这是一个经验分界线。
|
||||
|
||||
> **提示**: 三个空填数值(两个可能相同)。
|
||||
|
||||
---
|
||||
|
||||
## 三、简答题(1道)
|
||||
|
||||
### S1
|
||||
|
||||
面试官问:"请解释为什么 FSM 的核心价值不在于'减少代码行数',而在于'把所有合法转换集中在一处定义'。结合具体例子说明散落在各处的好处检查与集中管理相比有哪些问题。"
|
||||
|
||||
> **答题框架提示**:
|
||||
> 1. "分散判断"的典型场景和问题
|
||||
> 2. "集中定义"的优势
|
||||
> 3. 维护成本对比
|
||||
> 4. 测试便利性
|
||||
|
||||
---
|
||||
|
||||
## 参考答案与解析
|
||||
|
||||
### 选择题答案
|
||||
|
||||
| 题号 | 正确答案 | 解析 |
|
||||
|------|---------|------|
|
||||
| Q1 | A | 原文提到五元组是 `(States, Events, Transitions, Actions, StartState)`。StartState 定义了 FSM 的起始位置,没有它 FSM 就不知道从哪里开始。EndState 不是必需的——有些 FSM 是循环的。 |
|
||||
| Q2 | C | A 错:DFA 是确定性的,每个事件最多一个后继;B 错:NFA 非确定性不可预测;D 错:两者等价但 NFA 更紧凑。只有 C 正确——工程实践中几乎总是 DFA。 |
|
||||
| Q3 | B | 转移函数是**部分函数**——并非所有事件在所有状态下都有定义。例如订单已退款后不能再支付。非法转移应返回错误或拒绝,而非静默忽略。 |
|
||||
| Q4 | C | 状态少(≤10)逻辑简单的场景用最直观的 switch-case。不选 B 是因为 map-of-function 在状态少时过度设计,不选 A 是因为开关太多违反可读性。 |
|
||||
| Q5 | C | 60 个状态超过 switch-case 和 map 的舒适区。State 接口的优势是每个状态独立封装行为(单一职责),适合状态多且行为复杂的场景。 |
|
||||
| Q6 | B | 原文原文警告:常见误区是把 FSM 当成普通的 if-else。FSM 的核心价值是把所有合法转换**集中在一处定义**,而不是散落在业务逻辑各处。 |
|
||||
|
||||
### 填空题答案
|
||||
|
||||
| 题号 | 答案 | 解析 |
|
||||
|------|------|------|
|
||||
| F1 | `StartState`;`action` | 标准五元组加上初始状态定义了 FSM 的起点。扩展转移函数同时输出下一个状态和执行的动作(如发邮件、写日志)。 |
|
||||
| F2 | `语义`(或"有意义的") | 数字枚举(如 0=PENDING, 1=PAID)本身不具备可读性和语义表达力,编译器也无法在编译期验证某个转移是否合法。字符串枚举或 map 方式更好。 |
|
||||
| F3 | `10`;`10`;`50` | switch-case ≤10;map-of-function 10~50;State 接口 >50。这是按规模和复杂度递增的工程选型指南。 |
|
||||
|
||||
### 简答题参考答案
|
||||
|
||||
S1:**参考答案要点**:
|
||||
1. **分散判断的问题**:在 if-else 风格的代码中,状态转换规则往往嵌套在每个业务方法里(如 `if order.status == "paid" && event == "ship"`)。新开发者需要逐个方法查找才能了解完整转换图,新增转换要在多处修改,极易遗漏。
|
||||
2. **集中管理的优势**:将所有合法的 `state -> event -> next_state` 映射集中到一个数据结构(map 或表格)中,一目了然。新增转换只需注册一条规则。
|
||||
3. **维护成本**:分散模式下改一个转换可能需要找遍整个代码库;集中模式下只需更新一张表。bug 更容易定位和修复。
|
||||
4. **测试便利性**:集中定义可以生成完整的转换矩阵用于测试——遍历所有 state-event 组合确保要么通过要么报明确的错误。分散模式下很难做到全覆盖测试。
|
||||
|
||||
**评分标准**:答出任意 2 个要点即可得满分;完全正确需覆盖全部要点。
|
||||
|
||||
## 关联笔记
|
||||
- [[有限状态机在业务中的应用]]
|
||||
Reference in New Issue
Block a user