You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP应用重置Shiro加密密码失败,求错误原因及正确方法

问题

我们有一个运行多年的旧Java Web应用,采用Shiro框架,运行状态良好。现在需要通过PHP管理工具重置现有用户的密码。我们的Shiro采用SHA-256算法、500000轮迭代及加盐处理,原本计划使用PHP的crypt函数实现,但生成的哈希结果与Shiro工具生成的不一致:

Shiro工具执行命令:

./java -jar shiro-tools-hasher-1.4.0-RC2-cli.jar -ngs -s 1234567890123456 -d -p
        Password to hash:       <-- a
        $shiro1$SHA-256$500000$MTIzNDU2Nzg5MDEyMzQ1Ng==$le2T939sZTe4upbz0wct6NAjtxVmpM+Bl7UAoW+wJi4=

PHP的crypt函数执行:

>>> crypt('a','$5$rounds=500000$1234567890123456$')
=> "$5$rounds=500000$1234567890123456$vx0mjR6hsoTYlO37/QObVyFIlVZ/ikTMIwyl/035wf."

两者哈希值不同,请问哪里操作错误?以及正确的实现方式是什么?


错误原因

  • 算法逻辑不匹配:PHP的crypt('$5$...')使用的是crypt-SHA256标准算法,这是Unix系统定义的特定哈希格式,和Shiro的迭代SHA-256逻辑完全不同。Shiro是对「盐+密码」的字节序列进行重复SHA-256哈希,而crypt-SHA256有自己的盐处理、迭代逻辑和输出编码规则。
  • 编码方式差异:Shiro最终将哈希结果和盐都用Base64编码,而crypt-SHA256使用自定义字符集(./0-9A-Za-z)编码,输出格式自然不同。
  • 迭代逻辑差异:crypt的rounds参数和Shiro的迭代次数计算方式不一致,crypt的迭代是经过优化的特定实现,并非简单重复哈希。

正确实现方式

要在PHP中生成和Shiro一致的哈希,需要完全模拟Shiro的迭代SHA-256逻辑:

  1. 将密码和盐转换为UTF-8字节序列
  2. 拼接「盐字节 + 密码字节」作为初始输入
  3. 执行500000次SHA-256哈希(第一次哈希初始输入,后续每次以上一次的哈希结果为输入)
  4. 将最终哈希结果用Base64编码

对应的PHP代码实现:

function shiro_sha256_hash($password, $salt, $iterations = 500000) {
    // 转换为UTF-8字节
    $pwdBytes = mb_convert_encoding($password, 'UTF-8');
    $saltBytes = mb_convert_encoding($salt, 'UTF-8');
    
    // 初始输入:盐 + 密码
    $input = $saltBytes . $pwdBytes;
    
    // 第一次哈希
    $hash = hash('sha256', $input, true);
    
    // 剩余迭代次数(已执行1次,还需执行iterations-1次)
    for ($i = 1; $i < $iterations; $i++) {
        $hash = hash('sha256', $hash, true);
    }
    
    // 返回Base64编码的哈希结果
    return base64_encode($hash);
}

// 测试示例
$password = 'a';
$salt = '1234567890123456';
$hash = shiro_sha256_hash($password, $salt);
echo $hash; // 输出:le2T939sZTe4upbz0wct6NAjtxVmpM+Bl7UAoW+wJi4=

如果需要生成完整的Shiro格式字符串($shiro1$SHA-256$500000$[盐Base64]$[哈希Base64]),可以再拼接:

function shiro_format_hash($password, $salt, $iterations = 500000) {
    $hash = shiro_sha256_hash($password, $salt, $iterations);
    $saltBase64 = base64_encode(mb_convert_encoding($salt, 'UTF-8'));
    return "\$shiro1\$SHA-256\$${iterations}\$${saltBase64}\$${hash}";
}

// 测试
echo shiro_format_hash('a', '1234567890123456');
// 输出:$shiro1$SHA-256$500000$MTIzNDU2Nzg5MDEyMzQ1Ng==$le2T939sZTe4upbz0wct6NAjtxVmpM+Bl7UAoW+wJi4=

内容的提问来源于stack exchange,提问作者user25703217

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 22:55:56