SQL Server存储过程生成密码哈希乱码及类型转换错误求助
问题成因
- 隐式转换报错:
HASHBYTES函数的返回值为Varbinary二进制类型,如果你的Administrative.Users表中PasswordHash字段设置为varchar/nvarchar字符类型,插入时SQL Server会尝试做二进制到字符的隐式转换,规则不匹配时就会抛出257号类型转换错误;反之如果你把字符类型的哈希值直接往二进制字段插入,也会触发反向的隐式转换报错。 - 乱码问题:如果隐式转换侥幸执行成功,二进制哈希的字节会按照数据库默认编码解析为字符,大部分哈希字节不属于可打印字符范围,就会显示为你看到的类中文乱码。
解决方案
提供两种可选方案,推荐优先使用方案1:
方案1:存储二进制哈希(性能、空间最优)
直接用二进制类型存储原生哈希值,无需额外转换:
- 先修正表字段类型:
ALTER TABLE Administrative.Users ALTER COLUMN PasswordHash Varbinary(Max) NOT NULL;
- 你的原存储过程不需要修改,直接调用即可。如果需要可视化查看哈希值,查询时转成十六进制字符串即可:
SELECT CONVERT(Varchar(Max), PasswordHash, 2) AS PasswordHashHex FROM Administrative.Users;
方案2:存储可读的十六进制哈希字符串
如果需要直接存储人眼可读的哈希值,调整存储过程逻辑即可:
- 先将表的
PasswordHash字段类型改为Varchar(128)(SHA2_512哈希转十六进制后固定为128位) - 修改存储过程对应逻辑:
ALTER PROCEDURE [Administrative].[spInsertUser] (@Name Nvarchar(50), @Surname Nvarchar(Max), @Email Nvarchar(1000), @PasswordHash Nvarchar(Max), @EmailConfirmed Bit, @Lockout Bit, @TimeZone Nvarchar(6)) AS BEGIN DECLARE @Id Bigint; -- 将二进制哈希直接转成无前缀的十六进制字符串 DECLARE @NewPasswordHash Varchar(128) = CONVERT(Varchar(128), HASHBYTES('SHA2_512', @PasswordHash), 2); INSERT INTO Administrative.Users ([Name], Surname, Email, PasswordHash, EmailConfirmed, Lockout, TimeZone, Active) VALUES (@Name, @Surname, @Email, @NewPasswordHash, 1, 0, @TimeZone, 1) SET @Id = @@Identity SELECT @Id AS Id END
原理说明
HASHBYTES('SHA2_512', 输入值)固定返回64字节的二进制结果,二进制本身没有字符含义,直接转字符串就会出现乱码。CONVERT函数第三个参数设为2时,会把二进制值转换为无前缀的大写十六进制字符串,是行业内通用的二进制哈希可视化方案。
内容的提问来源于stack exchange,提问作者Thiago Corrêa
相关产品推荐
相关产品推荐

