pigxCloud微服务项目03——服务端——腾讯云即时通信IM,创建聊天群

腾讯云即时通信IM,创建聊天群,返回群ID给客户端使用

说明

当创建群组前需要导入账号,否则指定群主和导入群成员会失败;
所以实际是得调用3个腾讯云官方接口:导入单个帐号、创建群组、导入群成员;之后才能正常聊天。

服务端接口代码

参考官方资料:

导入单个帐号
创建群组(群组类型:临时会议群 (Meeting))
导入群成员

接口类

package com.cxbdapp.hbhtyyBackground.utils;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.List;

/**
 * 群组管理相关接口
 */
@Slf4j
public class GroupSystemUtils {

	/**
	 * 导入单个帐号
	 *
	 * @param identifier 帐号
	 * @param nick       昵称
	 * @param faceUrl    头像
	 * @param role       0,主诊医生 1、会诊医生 2、患者
	 * @return boolean
	 */
	public static boolean accountImport(String identifier, String nick, String faceUrl, Integer role) {
		try {
			String userSig = GenerateUserSigUtil.getUserSig(AppletUtils.identifier);
			String random = GenerateUserSigUtil.get32Random();
			String url = String.format(AppletUtils.ACCOUNT_IMPORT, AppletUtils.SDKAPPID, AppletUtils.identifier, userSig, random);
			JSONObject requestPackage = new JSONObject();
			requestPackage.put("Identifier", identifier);
			requestPackage.put("Nick", nick);
			requestPackage.put("FaceUrl", faceUrl);
			if (role == 0) {
				requestPackage.put("role", "Owner");
			} else if (role == 1) {
				requestPackage.put("role", "Admin");
			} else {
				requestPackage.put("role", "Member");
			}
			String body = HttpUtil.post(url, requestPackage.toString());
			log.info("导入单个帐号应答包体报文:{}", body);
			if (StrUtil.isEmpty(body)) {
				return false;
			}
			// {"ActionStatus":"FAIL","ErrorCode":70402,"ErrorInfo":"Invaild parameters. Please make sure that all required fields are filled with acceptable and accurare data.","FailAccounts":[]}
			// {"ActionStatus":"OK","ErrorCode":0,"ErrorInfo":"","FailAccounts":[]}
			Integer code = (Integer) JSONUtil.parseObj(body).get("ErrorCode");
			if (code == 0) {
				return true;
			}
			return false;
		} catch (Exception e) {
			log.error("导入单个帐号异常", e);
			return false;
		}
	}

	/**
	 * 创建群组
	 * 群组类型:Private/Public/ChatRoom/AVChatRoom
	 * 群组类型:好友工作群 (Work)/陌生人社交群 (Public)/临时会议群 (Meeting)/直播群 (AVChatRoom)
	 *
	 * @param ownerAccount 群主
	 * @param groupName    群名称
	 * @return R
	 */
	public static String createGroup(String groupName, String ownerAccount) {
		try {
			String userSig = GenerateUserSigUtil.getUserSig(AppletUtils.identifier);
			String random = GenerateUserSigUtil.get32Random();
			String url = String.format(AppletUtils.CREATE_GROUP, AppletUtils.SDKAPPID, AppletUtils.identifier, userSig, random);
			JSONObject requestPackage = new JSONObject();
			requestPackage.put("Owner_Account", ownerAccount);
			requestPackage.put("Name", groupName);
			requestPackage.put("Type", "ChatRoom");
			String body = HttpUtil.post(url, requestPackage.toString());
			log.info("创建群组应答包体报文:{}", body);
			if (StrUtil.isEmpty(body)) {
				return null;
			}
			return body;
			// {"ActionStatus":"FAIL","ErrorCode":60008,"ErrorInfo":"service timeout or request format error,please check and try again"}
			// {"ActionStatus":"OK","ErrorCode":0,"ErrorInfo":"","GroupId":"@TGS#3EQ2HHZGZ"}
			/*Integer code = (Integer) JSONUtil.parseObj(body).get("ErrorCode");
			if (code == 0) {
				String groupId = (String) JSONUtil.parseObj(body).get("GroupId");
				if (StrUtil.isEmpty(groupId)) {
					return null;
				}
				return groupId;
			}
			return null;*/
		} catch (Exception e) {
			log.error("创建群组异常", e);
			return null;
		}
	}
	
	/**
	 * 导入群成员
	 *
	 * @param groupId     群组ID
	 * @param doctorList  医生角色的群成员
	 * @param patientUser 患者角色的群成员
	 * @return boolean
	 */
	public static boolean importGroupMember(String groupId, List<String> doctorList, String patientUser) {
		JSONArray memberList = new JSONArray();
		for (String member : doctorList) {
			JSONObject object = new JSONObject();
			object.put("Member_Account", member);
			object.put("Role", "Admin");
			memberList.add(object);
		}
		JSONObject object = new JSONObject();
		object.put("Member_Account", patientUser);
		memberList.add(object);
		try {
			String userSig = GenerateUserSigUtil.getUserSig(AppletUtils.identifier);
			String random = GenerateUserSigUtil.get32Random();
			String url = String.format(AppletUtils.IMPORT_GROUP_MEMBER, AppletUtils.SDKAPPID, AppletUtils.identifier, userSig, random);
			JSONObject requestPackage = new JSONObject();
			requestPackage.put("GroupId", groupId);
			requestPackage.put("MemberList", memberList);
			String body = HttpUtil.post(url, requestPackage.toString());
			log.info("导入群成员应答包体报文:{}", body);
			if (StrUtil.isEmpty(body)) {
				return false;
			}
			// 加人结果:0 为失败;1 为成功;2 为已经是群成员
			// {"ActionStatus":"OK","ErrorCode":0,"ErrorInfo":"","MemberList":[{"Member_Account":"account3","Result":1},{"Member_Account":"account1","Result":1},{"Member_Account":"account2","Result":2}]}
			Integer code = (Integer) JSONUtil.parseObj(body).get("ErrorCode");
			if (code == 0) {
				return true;
			}
			return false;
		} catch (Exception e) {
			log.error("导入群成员程序异常", e);
			return false;
		}
	}

}

即时通信IM辅助工具类(腾讯云SDK拷贝的)

package com.cxbdapp.hbhtyyBackground.utils;

import android.util.Base64;
import cn.hutool.core.util.StrUtil;
import org.json.JSONException;
import org.json.JSONObject;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Random;
import java.util.zip.Deflater;

/**
 * Module:   GenerateTestUserSig
 * <p>
 * Function: 用于生成测试用的 UserSig,UserSig 是腾讯云为其云服务设计的一种安全保护签名。
 * 其计算方法是对 SDKAppID、UserID 和 EXPIRETIME 进行加密,加密算法为 HMAC-SHA256。
 * <p>
 * Attention: 请不要将如下代码发布到您的线上正式版本的 App 中,原因如下:
 * <p>
 * 本文件中的代码虽然能够正确计算出 UserSig,但仅适合快速调通 SDK 的基本功能,不适合线上产品,
 * 这是因为客户端代码中的 SECRETKEY 很容易被反编译逆向破解,尤其是 Web 端的代码被破解的难度几乎为零。
 * 一旦您的密钥泄露,攻击者就可以计算出正确的 UserSig 来盗用您的腾讯云流量。
 * <p>
 * 正确的做法是将 UserSig 的计算代码和加密密钥放在您的业务服务器上,然后由 App 按需向您的服务器获取实时算出的 UserSig。
 * 由于破解服务器的成本要高于破解客户端 App,所以服务器计算的方案能够更好地保护您的加密密钥。
 * <p>
 * Reference:https://cloud.tencent.com/document/product/269/32688#Server
 */
public class GenerateUserSigUtil {
    /*public static void main(String[] args) {
        System.out.println(gen32Random());
    }*/

	public static String get32Random() {
		Random rand = new Random();
		StringBuffer sb = new StringBuffer();
		for (int i = 1; i <= 32; i++) {
			int randNum = rand.nextInt(9) + 1;
			String num = randNum + "";
			sb = sb.append(num);
		}
		String random = String.valueOf(sb);
		return random;
	}

	/**
	 * 计算 UserSig 签名
	 * <p>
	 * 函数内部使用 HMAC-SHA256 非对称加密算法,对 SDKAPPID、userId 和 EXPIRETIME 进行加密。
	 *
	 * @note: 请不要将如下代码发布到您的线上正式版本的 App 中,原因如下:
	 * <p>
	 * 本文件中的代码虽然能够正确计算出 UserSig,但仅适合快速调通 SDK 的基本功能,不适合线上产品,
	 * 这是因为客户端代码中的 SECRETKEY 很容易被反编译逆向破解,尤其是 Web 端的代码被破解的难度几乎为零。
	 * 一旦您的密钥泄露,攻击者就可以计算出正确的 UserSig 来盗用您的腾讯云流量。
	 * <p>
	 * 正确的做法是将 UserSig 的计算代码和加密密钥放在您的业务服务器上,然后由 App 按需向您的服务器获取实时算出的 UserSig。
	 * 由于破解服务器的成本要高于破解客户端 App,所以服务器计算的方案能够更好地保护您的加密密钥。
	 * <p>
	 * 文档:https://cloud.tencent.com/document/product/269/32688#Server
	 */
	public static String getUserSig(String userId) {
		return getTlsSignature(AppletUtils.SDKAPPID, userId, AppletUtils.EXPIRETIME, null, AppletUtils.SECRETKEY);
	}

	/**
	 * 生成 tls 票据
	 *
	 * @param sdkappid      应用的 appid
	 * @param userId        用户 id
	 * @param expire        有效期,单位是秒
	 * @param userbuf       默认填写null
	 * @param priKeyContent 生成 tls 票据使用的私钥内容
	 * @return 如果出错,会返回为空,或者有异常打印,成功返回有效的票据
	 */
	private static String getTlsSignature(long sdkappid, String userId, long expire, byte[] userbuf, String priKeyContent) {
		if (StrUtil.isEmpty(priKeyContent)) {
			return "";
		}
		long currTime = System.currentTimeMillis() / 1000;
		JSONObject sigDoc = new JSONObject();
		try {
			sigDoc.put("TLS.ver", "2.0");
			sigDoc.put("TLS.identifier", userId);
			sigDoc.put("TLS.sdkappid", sdkappid);
			sigDoc.put("TLS.expire", expire);
			sigDoc.put("TLS.time", currTime);
		} catch (JSONException e) {
			e.printStackTrace();
		}

		String base64UserBuf = null;
		if (null != userbuf) {
			base64UserBuf = Base64.encodeToString(userbuf, Base64.NO_WRAP);
			try {
				sigDoc.put("TLS.userbuf", base64UserBuf);
			} catch (JSONException e) {
				e.printStackTrace();
			}
		}
		String sig = hmacSha256(sdkappid, userId, currTime, expire, priKeyContent, base64UserBuf);
		if (sig.length() == 0) {
			return "";
		}
		try {
			sigDoc.put("TLS.sig", sig);
		} catch (JSONException e) {
			e.printStackTrace();
		}
		Deflater compressor = new Deflater();
		compressor.setInput(sigDoc.toString().getBytes(Charset.forName("UTF-8")));
		compressor.finish();
		byte[] compressedBytes = new byte[2048];
		int compressedBytesLength = compressor.deflate(compressedBytes);
		compressor.end();
		return new String(base64EncodeUrl(Arrays.copyOfRange(compressedBytes, 0, compressedBytesLength)));
	}


	private static String hmacSha256(long sdkappid, String userId, long currTime, long expire, String priKeyContent, String base64Userbuf) {
		String contentToBeSigned = "TLS.identifier:" + userId + "\n"
				+ "TLS.sdkappid:" + sdkappid + "\n"
				+ "TLS.time:" + currTime + "\n"
				+ "TLS.expire:" + expire + "\n";
		if (null != base64Userbuf) {
			contentToBeSigned += "TLS.userbuf:" + base64Userbuf + "\n";
		}
		try {
			byte[] byteKey = priKeyContent.getBytes("UTF-8");
			Mac hmac = Mac.getInstance("HmacSHA256");
			SecretKeySpec keySpec = new SecretKeySpec(byteKey, "HmacSHA256");
			hmac.init(keySpec);
			byte[] byteSig = hmac.doFinal(contentToBeSigned.getBytes("UTF-8"));
			return new String(Base64.encode(byteSig, Base64.NO_WRAP));
		} catch (UnsupportedEncodingException e) {
			return "";
		} catch (NoSuchAlgorithmException e) {
			return "";
		} catch (InvalidKeyException e) {
			return "";
		}
	}

	private static byte[] base64EncodeUrl(byte[] input) {
		byte[] base64 = new String(Base64.encode(input, Base64.NO_WRAP)).getBytes();
		for (int i = 0; i < base64.length; ++i)
			switch (base64[i]) {
				case '+':
					base64[i] = '*';
					break;
				case '/':
					base64[i] = '-';
					break;
				case '=':
					base64[i] = '_';
					break;
				default:
					break;
			}
		return base64;
	}

}

静态变量工具类

package com.cxbdapp.hbhtyyBackground.utils;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * @Author cgp/zxy
 * @Date 2020/11/02 11:40
 * @Description 小程序公用静态变量工具类
 */
@Component
public class AppletUtils {
	/**
	 * 导入单个帐号
	 *
	 * @author zxy
	 */
	public static final String ACCOUNT_IMPORT = "https://console.tim.qq.com/v4/im_open_login_svc/account_import?sdkappid=%s&identifier=%s&usersig=%s&random=%s&contenttype=json";
	/**
	 * 创建群组
	 *
	 * @author zxy
	 */
	public static final String CREATE_GROUP = "https://console.tim.qq.com/v4/group_open_http_svc/create_group?sdkappid=%s&identifier=%s&usersig=%s&random=%s&contenttype=json";
	/**
	 * 导入群成员
	 *
	 * @author zxy
	 */
	public static final String IMPORT_GROUP_MEMBER = "https://console.tim.qq.com/v4/group_open_http_svc/import_group_member?sdkappid=%s&identifier=%s&usersig=%s&random=%s&contenttype=json";

	/**
	 * App 管理员帐号
	 *
	 * @author zxy
	 */
	public static String identifier;

	@Value("${im.identifier}")
	public void setIdentifier(String identifier) {
		this.identifier = identifier;
	}

	/**
	 * 腾讯云 SDKAppId,需要替换为您自己账号下的 SDKAppId。
	 * <p>
	 * 进入腾讯云云通信[控制台](https://console.cloud.tencent.com/avc ) 创建应用,即可看到 SDKAppId,
	 * 它是腾讯云用于区分客户的唯一标识。
	 *
	 * @author zxy
	 */
	public static int SDKAPPID;

	@Value("${tencent.cloud.sdkAppId}")
	public void setSDKAPPID(int SDKAPPID) {
		this.SDKAPPID = SDKAPPID;
	}

	/**
	 * 签名过期时间,建议不要设置的过短
	 * <p>
	 * 时间单位:秒
	 * 默认时间:7 x 24 x 60 x 60 = 604800 = 7 天
	 *
	 * @author zxy
	 */
	public static int EXPIRETIME;

	@Value("${im.expireTime}")
	public void setEXPIRETIME(int EXPIRETIME) {
		this.EXPIRETIME = EXPIRETIME;
	}

	/**
	 * 计算签名用的加密密钥,获取步骤如下:
	 * <p>
	 * step1. 进入腾讯云云通信[控制台](https://console.cloud.tencent.com/avc ) ,如果还没有应用就创建一个,
	 * step2. 单击“应用配置”进入基础配置页面,并进一步找到“帐号体系集成”部分。
	 * step3. 点击“查看密钥”按钮,就可以看到计算 UserSig 使用的加密的密钥了,请将其拷贝并复制到如下的变量中
	 * <p>
	 * 注意:该方案仅适用于调试Demo,正式上线前请将 UserSig 计算代码和密钥迁移到您的后台服务器上,以避免加密密钥泄露导致的流量盗用。
	 * 文档:https://cloud.tencent.com/document/product/269/32688#Server
	 *
	 * @author zxy
	 */
	public static String SECRETKEY;

	@Value("${im.secretKey}")
	public void setSECRETKEY(String SECRETKEY) {
		this.SECRETKEY = SECRETKEY;
	}
}

配置文件application.properties

spring.profiles.active=dev
#spring.profiles.active=test
#spring.profiles.active=online

# App 管理员帐号
im.identifier=administrator
# 签名过期时间,时间单位:秒;7 x 24 x 60 x 60 = 604800 = 7 天
im.expireTime=604800
# 腾讯云 SDKAppId
tencent.cloud.sdkAppId=1234123412
# 计算签名用的加密密钥
im.secretKey=...

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小言W

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值