readonly仅防键盘输入,不防粘贴、js赋值或开发者工具删属性;仅对text、password、email、tel、url、search、date、number、datetime-local及textarea生效;checkbox、radio、hidden、select等无效;混用disabled会导致字段不提交;后端必须校验。

readonly 不能防篡改,它只防键盘输入,不防粘贴、JS 赋值、开发者工具删属性——后端必须校验,否则字段被改了你也收不到告警。
哪些 input 类型加 readonly 才真正生效
readonly 只对文本类控件起作用:text、password、email、tel、url、search、date、number、datetime-local,以及 textarea。
以下写法等于白写,浏览器直接忽略:input type="checkbox"、input type="radio"、input type="hidden"、select、button、fieldset。
常见误操作:<input type="hidden" value="123" readonly> —— hidden 本就不该被编辑,加 readonly 既无意义也不生效。
readonly 和 disabled 混用会导致后端收不到字段
同时加 readonly 和 disabled,disabled 会完全压制前者:字段不可编辑、不可聚焦、不可复制,且不会出现在 FormData、serializeArray() 或表单 URL 参数中。
典型后果包括:order_id 字段用了 disabled,后端收不到值,接口返回 400;用户 ID 字段缺失导致空指针异常;表单验证逻辑因字段不存在而中断。
选型依据只有一条:
– 需要值参与后端校验?→ 必须用 readonly
– 字段纯属占位、临时屏蔽、不参与业务流程?→ 才考虑 disabled
动态控制 readonly 的 JS 写法避坑指南
原生 JS 中,element.readOnly = false 在 Safari 15 及更早版本中支持极差,推荐统一用 DOM 属性操作:
- 设为只读:
element.setAttribute("readonly", "readonly")(比element.readOnly = true更稳妥) - 解除只读:
element.removeAttribute("readonly")(比element.readOnly = false可靠)
React 中必须写成 readOnly={isLocked}(注意是驼峰 readOnly,不是 readonly);Vue 中绑定 :readonly="isLocked" 可行,但需确保 isLocked 是响应式数据,且初始值准确(比如从 API 拿到“已核对”状态后才设为 true)。
后端必须校验 readonly 字段是否被篡改
用户打开开发者工具删掉 readonly 属性、改完值再提交,浏览器照发——这是设计使然,不是漏洞。所以所有前端只读控制,都必须配套后端校验。
ThinkPHP 的 $readonly 属性也一样:它只在模型 save() 和 update() 时过滤字段,对原生 SQL、强制赋值(如 data($data, true))、大小写不匹配等场景完全无效。
真正防篡改得靠签名验证 + 中间件拦截:在请求进入控制器前,就完成参数排序、rawurlencode、剔除 sign、拼接密钥、时间戳 ±300 秒校验、nonce 去重等动作——readonly 管的是“不该被覆盖的值”,签名管的是“这个请求到底是不是合法客户端发的”。











