关于腾讯 IM 开发
腾讯微信已经成为当今绝大多数用户必不可少的聊天和通讯工具,腾讯也是国内最早也是最大的即时通信开发商 。腾讯云将高并发、高可靠的即时通信能力以 SDK 和 REST API的形式进行开放,推出即时通信 IM 产品,可以通过简易的方式将腾讯云提供的 IM SDK 集成进自有应用中,配合服务端 REST API 调用,即可轻松拥有微信一样强大的即时通信能力。
关于 UserSig
UserSig 是用户登录即时通信 IM 的密码,其本质是对 UserID 等信息加密后得到的密文,采用服务端计算 UserSig,可以最大限度地保障计算 UserSig 所用的密钥信息不被泄露。本文将介绍如何使用 C# 通过 TLSSigAPIv2 类计算 UserSig 的方法。
范例运行环境
操作系统: Windows Server 2019 DataCenter
.net版本: .netFramework4.0 或以上
开发工具:VS2019 C#
TLSSigAPIv2 类
设计
TLSSigAPIv2 类提供了一系列方法,用于生成UserSig,其关键属性方法说明如下:
序号 | 项 | 类型 | 说明 |
---|---|---|---|
1 | private readonly int sdkappid | 属性 | sdk开发者id,如何获取请参照如下链接: 腾讯IM即时通信控制台 |
2 | private readonly string key | 属性 | sdk开发者key ,如何获取请参照如下链接: 腾讯IM即时通信控制台 |
3 | private string HMACSHA256(string identifier, long currTime, int expire, string base64UserBuf, bool userBufEnabled) | 方法 | HMAC-SHA256 算法方法。即时通信 IM 服务自2019.07.19开始启用新的签名算法,从之前的 ECDSA-SHA256 升级为 HMAC-SHA256。 2019.07.19以后创建的 SDKAppID 均会采用新的 HMAC-SHA256 算法。 |
4 | public string GenSig(string identifier, int expire = 180 * 86400) | 方法 | 生成UserSig方法。 参数:Identifier为用户 ID; 参数:expire为有效期,单位为秒。建议 UserSig 有效期最短不小于24小时,最长不超过50年。为了您的账号安全,建议将 UserSig 有效期设置为两个月。 |
实现
实现代码如下:
public class TLSSigAPIv2 { private readonly int sdkappid; private readonly string key;
public TLSSigAPIv2(int sdkappid, string key) { this.sdkappid = sdkappid; this.key = key; } public byte[] GetUserBuf(string account, uint authId, uint expireTime, uint privilegeBitMap, uint accountType) { int length = 1 + 2 + account.Length + 20; int offset = 0; byte[] userBuf = new byte[length]; userBuf[offset++] = 0; userBuf[offset++] = (byte)((account.Length & 0xFF00) >> 8); userBuf[offset++] = (byte)(account.Length & 0x00FF); byte[] accountByte = System.Text.Encoding.UTF8.GetBytes(account); accountByte.CopyTo(userBuf, offset); offset += account.Length; userBuf[offset++] = (byte)((sdkappid & 0xFF000000) >> 24); userBuf[offset++] = (byte)((sdkappid & 0x00FF0000) >> 16); userBuf[offset++] = (byte)((sdkappid & 0x0000FF00) >> 8); userBuf[offset++] = (byte)(sdkappid & 0x000000FF); userBuf[offset++] = (byte)((authId & 0xFF000000) >> 24); userBuf[offset++] = (byte)((authId & 0x00FF0000) >> 16); userBuf[offset++] = (byte)((authId & 0x0000FF00) >> 8); userBuf[offset++] = (byte)(authId & 0x000000FF); userBuf[offset++] = (byte)((expireTime & 0xFF000000) >> 24); userBuf[offset++] = (byte)((expireTime & 0x00FF0000) >> 16); userBuf[offset++] = (byte)((expireTime & 0x0000FF00) >> 8); userBuf[offset++] = (byte)(expireTime & 0x000000FF); userBuf[offset++] = (byte)((privilegeBitMap & 0xFF000000) >> 24); userBuf[offset++] = (byte)((privilegeBitMap & 0x00FF0000) >> 16); userBuf[offset++] = (byte)((privilegeBitMap & 0x0000FF00) >> 8); userBuf[offset++] = (byte)(privilegeBitMap & 0x000000FF); userBuf[offset++] = (byte)((accountType & 0xFF000000) >> 24); userBuf[offset++] = (byte)((accountType & 0x00FF0000) >> 16); userBuf[offset++] = (byte)((accountType & 0x0000FF00) >> 8); userBuf[offset++] = (byte)(accountType & 0x000000FF); return userBuf; } private static byte[] CompressBytes(byte[] sourceByte) { MemoryStream inputStream = new MemoryStream(sourceByte); Stream outStream = CompressStream(inputStream); byte[] outPutByteArray = new byte[outStream.Length]; outStream.Position = 0; outStream.Read(outPutByteArray, 0, outPutByteArray.Length); return outPutByteArray; } private static Stream CompressStream(Stream sourceStream) { MemoryStream streamOut = new MemoryStream(); ZOutputStream streamZOut = new ZOutputStream(streamOut, zlibConst.Z_DEFAULT_COMPRESSION); CopyStream(sourceStream, streamZOut); streamZOut.finish(); return streamOut; } public static void CopyStream(System.IO.Stream input, System.IO.Stream output) { byte[] buffer = new byte[2000]; int len; while ((len = input.Read(buffer, 0, 2000)) > 0) { output.Write(buffer, 0, len); } output.Flush(); } private string HMACSHA256(string identifier, long currTime, int expire, string base64UserBuf, bool userBufEnabled) { string rawContentToBeSigned = "TLS.identifier:" + identifier + "\n" + "TLS.sdkappid:" + sdkappid + "\n" + "TLS.time:" + currTime + "\n" + "TLS.expire:" + expire + "\n"; if (true == userBufEnabled) { rawContentToBeSigned += "TLS.userbuf:" + base64UserBuf + "\n"; } using (HMACSHA256 hmac = new HMACSHA256()) { UTF8Encoding encoding = new UTF8Encoding(); Byte[] textBytes = encoding.GetBytes(rawContentToBeSigned); Byte[] keyBytes = encoding.GetBytes(key); Byte[] hashBytes; using (HMACSHA256 hash = new HMACSHA256(keyBytes)) hashBytes = hash.ComputeHash(textBytes); return Convert.ToBase64String(hashBytes); } } private string GenSig(string identifier, int expire, byte[] userbuf, bool userBufEnabled) { DateTime epoch = new DateTime(1970, 1, 1); // unix 时间戳 Int64 currTime = (Int64)(DateTime.UtcNow - epoch).TotalMilliseconds / 1000; string base64UserBuf; string jsonData; if (true == userBufEnabled) { base64UserBuf = Convert.ToBase64String(userbuf); string base64sig = HMACSHA256(identifier, currTime, expire, base64UserBuf, userBufEnabled); // 没有引入 json 库,所以这里手动进行组装, // 这里如果用户 identifier 中出现 json 元字符将会出错 jsonData = String.Format("{{" + "\"TLS.ver\":" + "\"2.0\"," + "\"TLS.identifier\":" + "\"{0}\"," + "\"TLS.sdkappid\":" + "{1}," + "\"TLS.expire\":" + "{2}," + "\"TLS.time\":" + "{3}," + "\"TLS.sig\":" + "\"{4}\"," + "\"TLS.userbuf\":" + "\"{5}\"" + "}}", identifier, sdkappid, expire, currTime, base64sig, base64UserBuf); } else { // 没有引入 json 库,所以这里手动进行组装 string base64sig = HMACSHA256(identifier, currTime, expire, "", false); jsonData = String.Format("{{" + "\"TLS.ver\":" + "\"2.0\"," + "\"TLS.identifier\":" + "\"{0}\"," + "\"TLS.sdkappid\":" + "{1}," + "\"TLS.expire\":" + "{2}," + "\"TLS.time\":" + "{3}," + "\"TLS.sig\":" + "\"{4}\"" + "}}", identifier, sdkappid, expire, currTime, base64sig); } byte[] buffer = Encoding.UTF8.GetBytes(jsonData); return Convert.ToBase64String(CompressBytes(buffer)) .Replace('+', '*').Replace('/', '-').Replace('=', '_'); } public string GenSig(string identifier, int expire = 180 * 86400) { return GenSig(identifier, expire, null, false); } public string GenSigWithUserBuf(string identifier, int expire, byte[] userBuf) { return GenSig(identifier, expire, userBuf, true); }
}
小结
服务端的调用引用代码如下:
string SDKAppId="申请的SDKAppID";
string SDKAppIdSecret="申请的SDKAppIdSecret";
string AppAdminId="IM平台超级管理员UserID";
TLSSigAPIv2 sig = new TLSSigAPIv2(int.Parse(SDKAppId),SDKAppIdSecret);
string _sig = sig.GenSig(AppAdminId);
后续我们将介绍如何通过UserSig 来实现 IM 服务端 REST API,REST API 是即时通信 IM 提供给 App 后台的 HTTP 管理接口,是一组原始的且管理功能强大的API。
本文代码仅供您参考使用,感谢您的阅读,希望本文能够对您有所帮助。