Yii2中字段权限需通过rules()的on/only/except配合scenarios()定义业务场景,并结合RBAC在模型层显式控制;前端须同步校验权限并避免仅用CSS隐藏敏感字段,safe规则不可替代权限控制。

Yii2 中如何用 rules() 和 scenarios() 控制字段可写权限
字段权限不是靠“开关”控制的,而是靠模型层对属性的显式放行。Yii 不会在数据库或表单渲染时自动过滤字段,必须在 rules() 里声明哪些字段允许被批量赋值(massive assignment),否则即使前端传了,也会被静默丢弃。
常见错误现象:ActiveRecord::load() 返回 true,但某些字段始终没更新;或者用户提交了非法字段(如 is_admin=1)却意外生效。
- 每个业务场景应定义独立
scenario,比如'create'、'update'、'profile_edit' - 在
rules()中用on指定场景,用except或only明确字段白名单 - 敏感字段(如
status、created_by)不要放在only列表里,除非当前场景确实需要修改 - 调用
load()时务必传入场景:$model->load($post, '')(空字符串表示使用默认场景)或$model->load($post, 'profile_edit')
示例:
public function rules()
{
return [
[['username', 'email'], 'required', 'on' => ['create', 'update']],
[['nickname'], 'string', 'max' => 50, 'on' => 'profile_edit'],
[['status'], 'in', 'range' => [0, 1], 'on' => 'admin_update'],
];
}
前端表单怎么动态显示/隐藏字段(基于用户权限)
不能只靠后端校验,前端也要同步控制可见性,否则体验割裂且易被绕过。Yii 本身不提供“字段级权限模板”,需手动组合 Yii::$app->user->can() 和字段元信息。
使用场景:管理员能看到 status 下拉框,普通用户看不到;编辑页中,只有创建者才能修改 category_id。
- 把权限判断逻辑从视图提到控制器或模型的辅助方法里,避免视图中堆砌
if (Yii::$app->user->can('...')) - 字段是否渲染,取决于「当前用户是否有该字段的操作权限」+「当前业务场景是否允许」,两个条件缺一不可
- 别用 CSS
display: none隐藏敏感字段——它不阻止 DOM 操作和 JS 提交,应直接不 render 对应ActiveField - 若字段多且权限规则复杂,可封装一个
getFieldConfig($field)方法,返回['visible' => true, 'readonly' => true]等配置
示例:
// 在视图中
if ($model->canShowField('status')) {
echo $form->field($model, 'status')->dropDownList([...]);
}
为什么 safe 规则不能当权限开关用
safe 规则只是告诉 Yii “这个字段可以参与批量赋值”,它不校验值是否合法,也不关联 RBAC 权限。把它当成权限控制手段,等于把门锁换成贴纸。
容易踩的坑:[['status'], 'safe'] 看似放行了字段,但任何用户只要提交该字段就能改;更糟的是,如果后续加了 required 或 in 校验,又没配 on,会导致非预期的全局拦截。
-
safe应仅用于需要赋值但无显式校验的字段(如富文本内容、JSON 字段),且必须配合on限定场景 - 权限类字段(如
role、is_deleted)永远不要用safe,而要用明确的in+on+ RBAC 前置检查 - 调试时可用
var_dump($model->getDirtyAttributes())查看哪些字段实际被加载并变更了
RBAC 权限节点和字段名之间没有自动映射关系
Yii 的 AuthManager 管理的是“操作权限”(如 updatePost),不是“字段权限”。你得自己定义规则(Rule 类)来桥接二者,比如判断当前用户是否为记录创建者,才允许修改 title。
性能影响:每次字段级权限判断都可能触发一次 DB 查询(如查 owner_id),别在循环中反复调用 canShowField()。
- 字段权限本质是业务规则,应在模型的
canModifyField($attribute)方法里实现,而非硬编码在视图或控制器 - 规则类(
Rule)适合复用,但注意它只接收$user、$item、$params,拿不到当前模型实例 —— 所以字段级判断仍要回退到模型方法 - 如果字段权限粒度极细(如“仅允许编辑自己昨天创建的 post 的前 50 字”),建议把逻辑下沉到 Repository 层,避免污染模型
复杂点在于:字段是否可见、是否可编辑、是否可提交、是否被校验,这四个状态可能彼此独立,得分别处理,不能指望一个 can() 调用全搞定。











