如何禁止Laravel查询构造器执行INSERT语句,仅允许SELECT语句
嘿,这个需求太常见了——不管是做只读数据服务、权限隔离,还是怕误操作搞坏数据,都得限制Laravel查询构造器只执行SELECT语句。我给你分享几个实用的实现方案,你可以根据自己的场景挑选:
方案1:给查询构造器打「只读补丁」(自定义宏)
这种方式最简单快速,直接拦截查询构造器的所有写操作方法,一旦调用就抛出异常或者返回错误提示。
你只需要在App\Providers\AppServiceProvider的boot方法里注册几个宏,覆盖掉insert、update、delete这些写操作的方法:
use Illuminate\Database\Query\Builder; public function boot() { // 拦截INSERT操作 Builder::macro('insert', function () { throw new \RuntimeException('禁止执行INSERT操作,当前仅允许SELECT查询'); }); // 拦截UPDATE操作 Builder::macro('update', function () { throw new \RuntimeException('禁止执行UPDATE操作,当前仅允许SELECT查询'); }); // 拦截DELETE操作 Builder::macro('delete', function () { throw new \RuntimeException('禁止执行DELETE操作,当前仅允许SELECT查询'); }); // 按需拦截其他写操作,比如insertGetId、updateOrInsert等 Builder::macro('insertGetId', function () { throw new \RuntimeException('禁止执行INSERT操作,当前仅允许SELECT查询'); }); }
优缺点:
- 优点:零侵入,不用修改现有模型或查询代码,几分钟就能搞定。
- 缺点:只能拦截查询构造器的方法,如果有人直接用
DB::statement()执行原生写SQL,这个拦不住。
方案2:从数据库底层锁死(只读用户)
这是最安全的方案——直接给应用分配一个只有SELECT权限的数据库账号,从根源上禁止写操作。
以MySQL为例,创建只读用户的SQL语句如下:
-- 创建只读用户 CREATE USER 'laravel_readonly'@'%' IDENTIFIED BY 'your_strong_password'; -- 给该用户分配指定数据库的SELECT权限 GRANT SELECT ON your_database_name.* TO 'laravel_readonly'@'%'; -- 刷新权限 FLUSH PRIVILEGES;
然后在.env文件里把数据库账号换成这个只读用户:
DB_USERNAME=laravel_readonly DB_PASSWORD=your_strong_password
优缺点:
- 优点:绝对安全,不管代码里写什么写操作,数据库都会直接拒绝,连原生SQL也拦得住。
- 缺点:如果你的应用部分功能需要写操作,就得配置双数据库连接(一个只读、一个读写),稍微麻烦一点。
方案3:给特定模型定制只读查询构造器
如果只是部分模型需要只读(比如日志、统计类模型),可以给这些模型单独定制一个只读的查询构造器。
首先创建一个自定义的只读构造器类:
namespace App\Database; use Illuminate\Database\Query\Builder as BaseBuilder; class ReadOnlyBuilder extends BaseBuilder { // 重写INSERT方法 public function insert(array $values) { throw new \RuntimeException('该模型仅允许执行SELECT查询,禁止写操作'); } // 重写UPDATE方法 public function update(array $values) { throw new \RuntimeException('该模型仅允许执行SELECT查询,禁止写操作'); } // 重写DELETE方法 public function delete() { throw new \RuntimeException('该模型仅允许执行SELECT查询,禁止写操作'); } // 按需重写其他写操作方法 }
然后在需要只读的模型里,指定使用这个构造器:
use App\Database\ReadOnlyBuilder; use Illuminate\Database\Eloquent\Model; class Log extends Model { /** * 创建自定义查询构造器实例 */ protected function newBaseQueryBuilder() { $connection = $this->getConnection(); return new ReadOnlyBuilder( $connection, $connection->getQueryGrammar(), $connection->getPostProcessor() ); } }
优缺点:
- 优点:精准控制,只影响指定模型,其他模型正常使用写操作。
- 缺点:需要给每个只读模型单独配置,或者创建一个基类模型让所有只读模型继承。
总结一下
- 如果整个应用都是只读的:优先用数据库只读用户,安全可靠。
- 如果只是部分查询需要只读:用自定义宏快速实现。
- 如果只是部分模型需要只读:用自定义查询构造器精准控制。
内容的提问来源于stack exchange,提问作者paranoid
相关产品推荐
相关产品推荐

