最近在做一个和拥有TurboCRM相关的项目,其中需要取出客户信息的联系人字段,经过查看,这个字段在存入时用Base64加密过了
这个功能在应用层实现是很方便的,但是由于一些特殊原因,只能放到SqlServer里面进行解码,语句比较简单,但是花了很长时间,特此记录以避坑
1.创建Base64解码函数
CREATE FUNCTION [dbo].[base64_decode] ( @encoded_text varchar(max) ) RETURNS varbinary(max) AS BEGIN DECLARE @output varbinary(max), @block_start int, @encoded_length int, @decoded_length int, @mapr binary(122) IF LEN(@encoded_text) & 3 > 0 OR @encoded_text LIKE '%[^ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=]%' COLLATE Latin1_General_Bin RETURN NULL SET @output = 0x -- The nth byte of @mapr contains the base64 value of the character with an ASCII value of n. -- eg. 65th byte = 0x00 = 0 = value of 'A' SET @mapr = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -- 1-33 + 0xFFFFFFFFFFFFFFFFFFFF3EFFFFFF3F3435363738393A3B3C3DFFFFFF00FFFFFF -- 33-64 + 0x000102030405060708090A0B0C0D0E0F10111213141516171819FFFFFFFFFFFF -- 65-96 + 0x1A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233 -- 97-122 --get the number of blocks to be decoded SET @encoded_length = LEN(@encoded_text) SET @decoded_length = @encoded_length / 4 * 3 --for each block SET @block_start = 1 WHILE @block_start < @encoded_length BEGIN --decode the block and add to output --BINARY values between 1 and 4 bytes can be implicitly cast to INT SET @output = @output + CAST(CAST( SUBSTRING(@mapr, ASCII(SUBSTRING(@encoded_text, @block_start , 1)), 1) * 262144 + SUBSTRING(@mapr, ASCII(SUBSTRING(@encoded_text, @block_start + 1, 1)), 1) * 4096 + SUBSTRING(@mapr, ASCII(SUBSTRING(@encoded_text, @block_start + 2, 1)), 1) * 64 + SUBSTRING(@mapr, ASCII(SUBSTRING(@encoded_text, @block_start + 3, 1)), 1) AS int) AS binary(3)) SET @block_start = @block_start + 4 END IF RIGHT(@encoded_text, 2) = '==' SET @decoded_length = @decoded_length - 2 ELSE IF RIGHT(@encoded_text, 1) = '=' SET @decoded_length = @decoded_length - 1 RETURN SUBSTRING(@output, 1, @decoded_length) END
2.创建UTF8转换函数,转换后的字符为UTF8,如果直接转换会造成乱码的情况
CREATE FUNCTION [dbo].[utf8_to_ucs2]( @utf8 varbinary(max) ) RETURNS varbinary(max) AS BEGIN DECLARE @output varbinary(max), @i int, @next int, @code int, @tmp varbinary(1) SET @output = 0x SET @i = 1 SET @next = 0 WHILE 1 = 1 BEGIN SET @tmp = SUBSTRING(@utf8,@i,1) IF @tmp = 0x BREAK IF @tmp BETWEEN 0x01 AND 0x7F SET @output = @output + @tmp + 0x00 ELSE IF @tmp BETWEEN 0xC0 AND 0xDF BEGIN SET @code = (CAST(@tmp AS int) & 0x1F) * 64 SET @next = 1 END ELSE IF @tmp BETWEEN 0xE0 AND 0xEF BEGIN SET @code = (CAST(@tmp AS int) & 0x0F) * 4096 SET @next = 2 END ELSE IF @tmp BETWEEN 0x80 AND 0xBF AND @next IN (1,2) BEGIN IF @next = 1 BEGIN SET @code = @code + (CAST(@tmp AS int) & 0x3F) SET @output = @output + CAST(NCHAR(@code) AS binary(2)) END IF @next = 2 SET @code = @code + (CAST(@tmp AS int) & 0x3F) * 64 SET @next = @next - 1 END ELSE RETURN NULL SET @i = @i + 1 END RETURN @output END
3.查询语句使用方式如下,此处要注意如果是中文必须使用NVARCHAR否则会乱码
SELECT CONVERT(nvarchar(max), dbo.utf8_to_ucs2(dbo.base64_decode('6Zm25oC7')))