在Hyperf模型中,通过定义getXXXAttribute访问器实现字段脱敏是最轻量可控的方式,支持按字段、角色、场景差异化处理,且仅在读取时触发,不影响存储与写入。

Hyperf模型中定义Getter方法实现字段脱敏
在Hyperf中,直接在模型的 getXXXAttribute 方法里做脱敏是最轻量、最可控的方式。它不依赖中间件或全局钩子,避免误脱敏或漏脱敏,也方便按字段、按角色、按场景差异化处理。
注意:Hyperf 的 Eloquent 模型(Hyperf\Database\Model\Model)完全兼容 Laravel 的访问器机制,但需确保模型继承自该基类,且未禁用访问器(默认开启)。
- 脱敏逻辑只在读取属性时触发,不影响数据库存储和写入
- JSON 序列化(如 API 返回)会自动调用访问器,无需额外处理
- 若字段被
select()显式排除,或使用toArray()但未访问该属性,脱敏不会执行 - 不要在访问器里调用
$this->attributes['xxx']以外的属性读取——可能引发递归调用
手机号、身份证号等常见字段脱敏示例
以用户模型 User 为例,对 phone 和 id_card 字段做掩码处理:
class User extends Model
{
// 手机号:138****1234
public function getPhoneAttribute($value)
{
if (! $value || ! is_string($value)) {
return $value;
}
return preg_replace('/(\d{3})\d{4}(\d{4})/', '$1****$2', $value);
}
// 身份证号:110101****12345678
public function getIdCardAttribute($value)
{
if (! $value || ! is_string($value) || strlen($value) !== 18) {
return $value;
}
return substr($value, 0, 6) . '****' . substr($value, -4);
}
}
关键点:
- 务必先判空和类型,避免
null或数字类型传入正则导致警告 - 身份证校验长度(18位)比正则更可靠,防止脏数据崩掉脱敏逻辑
- 不要用
str_replace等无上下文替换,易误伤(比如把邮箱里的数字也“脱敏”)
按角色/请求上下文动态控制是否脱敏
有些字段对管理员应明文展示,对普通用户才脱敏。此时不能硬编码在 Getter 里,需引入运行时判断。
推荐方式:通过 Container 获取当前请求实例(需确保在 HTTP 生命周期内),或注入上下文服务:
use Hyperf\Context\Context;
use Hyperf\HttpServer\Contract\RequestInterface;
public function getPhoneAttribute($value)
{
$request = Context::get(RequestInterface::class);
if (! $request || ! $request->hasHeader('x-user-role')) {
return $value;
}
$role = $request->header('x-user-role')[0] ?? '';
if ($role === 'admin') {
return $value;
}
return preg_replace('/(\d{3})\d{4}(\d{4})/', '$1****$2', $value);
}
更健壮的做法是封装一个 DataMasker 服务,统一管理脱敏策略和权限判定,Getter 中只负责调用:
- 避免在模型中耦合 HTTP 请求细节(测试困难、CLI 场景报错)
- 可将角色信息提前存入
Context,而非每次从 header 解析 - CLI 或队列任务中无 Request 上下文,必须 fallback 到安全默认(即脱敏)
Getter脱敏与 cast / mutator / accessor 的区别和选型
容易混淆的是:cast 做类型转换(如 string → array),mutator(setXXXAttribute)影响写入,而 accessor(getXXXAttribute)才是读取时的脱敏入口。三者职责分明,不可混用。
- 不要在
cast里做脱敏——它只管类型,且对 null/empty 处理不一致 - 避免在
mutator中反向脱敏(如存库前就加密),那属于数据加密范畴,和展示层脱敏不是一回事 - 如果字段需要「始终脱敏」且不区分角色,Getter 是最简方案;若需多级策略(如字段级开关、租户级配置),建议抽离为独立服务 + 模型 trait
真正麻烦的不是写几行正则,而是脱敏后还要支持模糊搜索、导出 Excel、关联查询字段透出——这些场景 Getter 无法覆盖,得回退到 SQL 层或视图处理。


















