如何在Laravel查询的WHERE子句中使用Crypt解密实现搜索?
解决方案
要实现加密登录信息的搜索,核心是在数据库查询阶段完成解密并匹配关键词,因为through()是在查询结果返回后处理,无法作用于WHERE子句。以下是基于MySQL数据库的实现方案:
1. 核心思路
Laravel的Crypt::encrypt()生成的密文是包含iv、加密内容的JSON字符串的Base64编码,因此可以通过数据库的JSON解析和AES解密函数,在WHERE子句中直接解密password_login字段并匹配搜索关键词。
2. 修改后的查询代码
// 解析APP_KEY,去掉base64:前缀后解码为原始密钥 $appKey = base64_decode(substr(config('app.key'), 7)); $passwords = Password::query() ->when($filter_category, function($q) use ($filter_category){ $q->where('category_id', $filter_category); }) ->when($filter_search, function($q) use ($filter_search, $appKey){ $q->whereHas('category', function ($q) use ($filter_search){ $q->where('category_name', 'LIKE', "%$filter_search%"); }) // 保留原有的密码名称搜索 ->orWhere('password_name', 'LIKE', "%$filter_search%") // 添加登录信息解密搜索 ->orWhereRaw( "AES_DECRYPT(FROM_BASE64(JSON_UNQUOTE(JSON_EXTRACT(FROM_BASE64(password_login), '$.value'))), ?, FROM_BASE64(JSON_UNQUOTE(JSON_EXTRACT(FROM_BASE64(password_login), '$.iv')))) LIKE ?", [$appKey, "%$filter_search%"] ); }) ->where('user_id', Auth::id()) ->paginate(10) ->through(function ($password) { if ($password->password_login) { $password->password_login = $this->formatLogin(Crypt::decrypt($password->password_login)); } return $password; });
3. 关键说明
- 数据库版本要求:需要MySQL 5.7.4+或MariaDB 10.2+,确保支持JSON解析函数和带IV参数的AES_DECRYPT(CBC模式)。
- 性能问题:该方案需要对每一行数据进行解密操作,无法利用索引,数据量大时查询速度会变慢。如果数据量较大,建议:
- 使用确定性加密存储一个可搜索的额外字段(如
searchable_login),但需注意相同明文会生成相同密文的安全风险; - 改用支持字段级加密的数据库特性(如MySQL透明数据加密)。
- 使用确定性加密存储一个可搜索的额外字段(如
- 密钥安全:直接使用Laravel的
APP_KEY即可,不要硬编码密钥,确保APP_KEY的保密性。
内容的提问来源于stack exchange,提问作者LordzSpectron
相关产品推荐
相关产品推荐

