Tencent Cloud >> Cloud Communication >> TLS background API is built on mac with JAVA DEMO


Cloud communication related data download address


https://share.weiyun.com/2b3abe0e3f185c440cf455647455f661


Java demo


Adjusted according to the official demo, which is more in line with the style of spring-boot


import java.util.Map;

/**
 * Created by saleson on 2017/10/24.
 */
public class IMActionResult {
    private Map<String, Object> result;

    public IMActionResult(Map<String, Object> result) {
        this.result = result;
    }

    public boolean isSuccess() {
        return "OK".equals(getActionStatus());
    }

    public String getActionStatus() {
        return (String) result.get("ActionStatus");
    }


    public String getErrorInfo() {
        return (String) result.get("ErrorInfo");
    }


    public int getErrorCode() {
        return (int) result.get("ErrorCode");
    }


    public Map<String, Object> getResult() {
        return result;
    }

    public Object getObject(String name) {
        return result.get(name);
    }

    public <T> T get(String name) {
        return (T) result.get(name);
    }
}
/**
 * Created by saleson on 2017/10/23.
 */
public class TencentIMConfig {
    private String sdkAppid;
    private String jnisigcheckLibPath;
    private String privateKeyPath;
    private String privateKey;
    private String defaultImAdminAccount;

    public String getSdkAppid() {
        return sdkAppid;
    }

    public void setSdkAppid(String sdkAppid) {
        this.sdkAppid = sdkAppid;
    }

    public String getJnisigcheckLibPath() {
        return jnisigcheckLibPath;
    }

    public void setJnisigcheckLibPath(String jnisigcheckLibPath) {
        this.jnisigcheckLibPath = jnisigcheckLibPath;
    }

    public String getPrivateKeyPath() {
        return privateKeyPath;
    }

    public void setPrivateKeyPath(String privateKeyPath) {
        this.privateKeyPath = privateKeyPath;
    }

    public String getPrivateKey() {
        return privateKey;
    }

    public void setPrivateKey(String privateKey) {
        this.privateKey = privateKey;
    }

    public String getDefaultImAdminAccount() {
        return defaultImAdminAccount;
    }

    public void setDefaultImAdminAccount(String defaultImAdminAccount) {
        this.defaultImAdminAccount = defaultImAdminAccount;
    }
}

import com.fasterxml.jackson.annotation.JsonProperty;

/**
 * Created by saleson on 2017/10/23.
 */
public class IMActionResponse {
    @JsonProperty("ActionStatus")
    private String ActionStatus;
    @JsonProperty("ErrorInfo")
    private String ErrorInfo;
    @JsonProperty("ErrorCode")
    private int ErrorCode;

    public boolean isSuccess() {
        return "OK".equals(ActionStatus);
    }

    public String getActionStatus() {
        return ActionStatus;
    }

    public void setActionStatus(String actionStatus) {
        ActionStatus = actionStatus;
    }

    public String getErrorInfo() {
        return ErrorInfo;
    }

    public void setErrorInfo(String errorInfo) {
        ErrorInfo = errorInfo;
    }

    public int getErrorCode() {
        return ErrorCode;
    }

    public void setErrorCode(int errorCode) {
        ErrorCode = errorCode;
    }
}

import com.fasterxml.jackson.annotation.JsonProperty;

import java.util.List;

/**
 * Created by saleson on 2017/10/23.
 */
public class IMMultiAccImportResponse extends IMActionResponse {

    @JsonProperty("FailAccounts")
    private List<String> FailAccounts;


    public List<String> getFailAccounts() {
        return FailAccounts;
    }

    public void setFailAccounts(List<String> failAccounts) {
        FailAccounts = failAccounts;
    }
}

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.tls.sigcheck.tls_sigcheck;
import org.apache.commons.lang3.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * Created by saleson on 2017/10/23.
 */
public class TencentIMHelper {
    private static final Logger log = LoggerFactory.getLogger(TencentIMHelper.class);
    private TencentIMConfig config;
    private RestTemplate rest;
    private ObjectMapper objectMapper;

    private Joiner.MapJoiner joiner = Joiner.on("&").withKeyValueSeparator("=");


    public TencentIMHelper(TencentIMConfig config, RestTemplate rest, ObjectMapper objectMapper) {
        this.config = config;
        this.rest = rest;
        this.objectMapper = objectMapper;
    }

    /**
     * 生成usersig
     *
     * @param identifier
     * @return
     */
    public String genUsersig(String identifier) {
        tls_sigcheck demo = new tls_sigcheck();
        // 使用前请修改动态库的加载路径
        demo.loadJniLib(config.getJnisigcheckLibPath());
        int ret = demo.tls_gen_signature_ex2(config.getSdkAppid(), identifier, config.getPrivateKey());
        if (0 != ret) {
            log.error("ret: {}, errMsg:{}", ret, demo.getErrMsg());
            throw new ClientException(ResultCode.IM_ACCOUNT_LOGIN_FAILD);
        } else {
            String usersig = demo.getSig();
            System.out.println("sig:\n" + demo.getSig());
            log.debug("identifier '{}' take usersig is {}", identifier, usersig);
            return usersig;
        }
    }


    /**
     * 获取默认设置的im admin 账号的usersig
     *
     * @return
     */
    public String getIMAdminUsersig() {
        if (StringUtils.isEmpty(config.getDefaultImAdminAccount())) {
            log.error("TencentIMConfig.defaultImAdminAccount不存在");
            throw new ServerException(ResultCode.IM_ACCOUNT_LOGIN_FAILD.getResultCode(), "默认的IM admin账号不存在");
        }
        return getIMAdminUsersig(config.getDefaultImAdminAccount());
    }


    /**
     * 获取im admin 账号的usersig
     *
     * @param identifier
     * @return
     */
    public String getIMAdminUsersig(String identifier) {
        String usersig = _getIMAdminUsersig(identifier);
        if (StringUtils.isEmpty(usersig)) {
            usersig = genUsersig(identifier);
            cacheIMAdminUsersig(identifier, usersig);
        }
        return usersig;
    }


    /**
     * 导入账号
     *
     * @param accountId
     */
    public void accountImport(String accountId) {
        String url = "https://console.tim.qq.com/v4/im_open_login_svc/account_import?";
        String queryString = joiner.join(getDefaultParams());
        Map<String, String> requestBody = ImmutableMap.of("Identifier", accountId);
        IMActionResponse res = request(url + queryString, requestBody, IMActionResponse.class);
        if (!res.isSuccess()) {
            log.error("导入'{}'到腾讯云IM失败, response message is: {}", toString(res));
            throw new ClientException(ResultCode.IM_ACCOUNT_CREATE_FAILD);
        }
    }


    /**
     * 帐号批量导入
     *
     * @param accountIds 用户名,单个用户名长度不超过 32 字节,单次最多导入100个用户名
     * @return 返回导入失败的帐号列表
     */
    public List<String> multiAccountImport(String... accountIds) {
        String url = "https://console.tim.qq.com/v4/im_open_login_svc/multiaccount_import?";
        String queryString = joiner.join(getDefaultParams());
        Map<String, List<String>> requestBody = ImmutableMap.of("Accounts", Arrays.asList(accountIds));

        IMMultiAccImportResponse res = request(url + queryString, requestBody, IMMultiAccImportResponse.class);
        if (!res.isSuccess()) {
            log.error("帐号批量导入失败:{}, response message is: {}",
                    StringUtils.join(accountIds, ", "),
                    toString(res));
            throw new ClientException(ResultCode.IM_ACTION_FAILD);
        }
        return res.getFailAccounts();
    }


    /**
     * 设置用户资料(IM)
     *
     * @param userDTO
     */
    public void setAccountProfile(UserDTO userDTO) {
        String url = "https://console.tim.qq.com/v4/profile/portrait_set?";
        String queryString = joiner.join(getDefaultParams());
        String nikename = userDTO.getNickName()
        int gder = ObjectUtils.defaultIfNull(userDTO.getGender(), 0);
        String gender = gder == 1 ? "Gender_Type_Male" : gder == 2 ? "Gender_Type_Female" : "Gender_Type_Unknown";
        Map<String, Object> requestBody = ImmutableMap.of("From_Account", userDTO.getLogin(),
                "ProfileItem", ImmutableList.of(
                        ofProPortItem("Tag_Profile_IM_Nick", nikename),

                        ofProPortItem("Tag_Profile_IM_Gender", gender),

                        ofProPortItem("Tag_Profile_IM_Image",
                                ResourceUtils.getFullResourceUrl(userDTO.getAvatar()))
//                        ofProPortItem("", ""),
//                        ofProPortItem("", ""),
//                        ofProPortItem("", ""),
//                        ofProPortItem("", "")
                ));

        IMActionResult res = request(url + queryString, requestBody);
        if (!res.isSuccess()) {
            log.error("调用'{}'的账号信息到腾讯云IM失败, response message is: {}",
                    userDTO.getLogin(), toString(res.getResult()));
            throw new ClientException(ResultCode.IM_ACCOUNT_INFO_SET_FAILD);
        }
    }


    /**
     * @param tag
     * @param value
     * @return
     */
    private Map<String, Object> ofProPortItem(String tag, Object value) {
        return ImmutableMap.of("tag", tag, "value", value);
    }

    /**
     * 默认值
     *
     * @param obj
     * @param def
     * @return
     */
    private Object defNull(Object obj, Object def) {
        return obj == null ? def : obj;
    }


    /**
     * 返回默认的参数
     *
     * @return
     */
    private Map<String, String> getDefaultParams() {
        Map<String, String> pathParams = Maps.newHashMap();
        pathParams.put("usersig", getIMAdminUsersig());
        pathParams.put("identifier", config.getDefaultImAdminAccount());
        pathParams.put("sdkappid", config.getSdkAppid());
        pathParams.put("random", StringUtils.uuid());
        pathParams.put("contenttype", "json");
        return pathParams;
    }

    private void initPrivateKey() {
        if (StringUtils.isNotEmpty(config.getPrivateKey())) {
            return;
        }
        if (StringUtils.isEmpty(config.getPrivateKeyPath())) {
            throw new ServerException(ResultCode.OPERATION_FAILD.getResultCode(), "腾讯云通讯无法找到privateKey");
        }
        //todo
    }

    private <T> T request(String url, Object params, Class<T> cls) {
        return toBean(requestInvoke(url, params), cls);
    }

    private IMActionResult request(String url, Object params) {
        Map<String, Object> map = toBean(requestInvoke(url, params),
                new TypeReference<Map<String, Object>>() {
                });
        return new IMActionResult(map);

    }

    private String requestInvoke(String url, Object params) {
        String json = rest.postForObject(url, params, String.class);
        try {
            log.info("request url {}, the params is: {}, and response: {}", url, objectMapper.writeValueAsString(params), json);
        } catch (IOException e) {

        }
        return json;
    }

    private <T> T toBean(String json, Class<T> cls) {
        try {
            return objectMapper.readValue(json, cls);
        } catch (IOException e) {
            log.error("json:{} 转换类型失败: {} ", json, cls);
            log.error(e.getMessage(), e);
            throw new ServerException(ResultCode.OPERATION_FAILD.getResultCode(), "转换json失败");
        }
    }

    private <T> T toBean(String json, TypeReference<T> type) {
        try {
            return objectMapper.readValue(json, type);
        } catch (IOException e) {
            log.error("json:{} 转换类型失败: {} ", json, type.getType());
            log.error(e.getMessage(), e);
            throw new ServerException(ResultCode.OPERATION_FAILD.getResultCode(), "转换json失败");
        }
    }


    private String _getIMAdminUsersig(String identifier) {
        //todo
        return null;
    }

    private void cacheIMAdminUsersig(String identifier, String usersig) {
        //todo
    }


    private String toString(Object obj) {
        if (obj == null) {
            return null;
        } else if ("".equals(obj)) {
            return "";
        }
        try {
            return objectMapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            return obj.toString();
        }
    }


}

Test


import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tls.sigcheck.tls_sigcheck;
import org.junit.Test;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;

/**
 * Created by saleson on 2017/10/23.
 */
public class TencentIMTest {


    @Test
    public void testHelper() throws IOException {
        TencentIMConfig config = new TencentIMConfig();
        config.setDefaultImAdminAccount("testadmin");
        config.setSdkAppid("1400046172");
        //config.setJnisigcheckLibPath("tencent_im/tls_sig_api-src/src/jnisigcheck.so");
        config.setJnisigcheckLibPath("tencent_im/jnisigcheck_mt_x64.so");

        InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("thirdparty/tencent/im/private_key");
        BufferedReader br = new BufferedReader(new InputStreamReader(is));

        StringBuilder strBuilder = new StringBuilder();
        String s = "";
        while ((s = br.readLine()) != null) {
            strBuilder.append(s + '\n');
        }
        br.close();
        String priKey = strBuilder.toString();


        config.setPrivateKey(priKey);
//        config.setPrivateKeyPath("");
//        RestTemplate rest = new RestTemplate();
//        rest.setMessageConverters(Arrays.asList(new MappingJackson2HttpMessageConverter()));
        RestTemplate rest = jsonRestTemplate();
        TencentIMHelper helper = new TencentIMHelper(config, rest, new ObjectMapper());
        String adminUsersig = helper.getIMAdminUsersig();
        System.out.println("adminUsersig:\n" + adminUsersig);
        helper.accountImport("test10");
        List<String> list = helper.multiAccountImport("test2jalsdkfjaoisdufo32jkleoflkjasdkfjasdiufowerquwlkj0", "test21", "test22", "test23");
        System.out.println(list);

    }


    public RestTemplate jsonRestTemplate() {
        RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory());
        restTemplate.getMessageConverters().add(0, new FastJsonHttpMessageConverter());
        restTemplate.getInterceptors().add((request, body, execution) -> {
            System.out.println(new String(body));
            ClientHttpResponse response = execution.execute(request, body);
            response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
            return response;
        });
        return restTemplate;
    }

    private ClientHttpRequestFactory clientHttpRequestFactory() {
        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
        factory.setReadTimeout(30000);
        factory.setConnectTimeout(30000);
        return factory;
    }
}

The above code may work normally in a linux environment.
But to run debugging on mac, an error will be reported:

java.lang.UnsatisfiedLinkError: /Users/saleson/Downloads/tencent_im/jnisigcheck_mt_x64.so: dlopen(/Users/saleson/Downloads/tencent_im/jnisigcheck_mt_x64.so, 1): no suitable image found.  Did find:
    /Users/saleson/Downloads/tencent_im/jnisigcheck_mt_x64.so: unknown file type, first eight bytes: 0x7F 0x45 0x4C 0x46 0x02 0x01 0x01 0x00
    /Users/saleson/Downloads/tencent_im/jnisigcheck_mt_x64.so: unknown file type, first eight bytes: 0x7F 0x45 0x4C 0x46 0x02 0x01 0x01 0x00

    at java.lang.ClassLoader$NativeLibrary.load(Native Method)
    at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1941)
    at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1824)
    at java.lang.Runtime.load0(Runtime.java:809)
    at java.lang.System.load(System.java:1086)
    at com.tls.sigcheck.tls_sigcheck.loadJniLib(tls_sigcheck.java:9)
    at com.thirdparty.tencent.im.TencentIMHelper.genUsersig(TencentIMHelper.java:48)
    at com.thirdparty.tencent.im.TencentIMHelper.getIMAdminUsersig(TencentIMHelper.java:85)
    at com.thirdparty.tencent.im.TencentIMHelper.getIMAdminUsersig(TencentIMHelper.java:72)
    at com.thirdparty.TencentIMTest.testHelper(TencentIMTest.java:123)

Therefore, on mac, you need to recompile to generate the jnisigcheck.so file.


Compile jnisigcheck.so


Download and unzip tls_sig_api-src.tar.gz
into the src directory and view the Makefile

$ tar -zxvf tls_sig_api_src.tar.gz
$ cd tls_sig_api-src/src
$ vim Makefile

Makefile

CPP = g++
CC = gcc
ARCH=$(shell getconf LONG_BIT)
CFLAGS= -g -I../include -I../include/tls_sig_api -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux -Wall -fPIC
LIBS= ../linux/$(ARCH)/lib/jsoncpp/libjsoncpp.a ../openssl-dynamic/lib/libcrypto.a -ldl -lz
TARGETS = libtlsignature.a sigcheck.so signature

all: $(TARGETS)

libtlsignature.a: tls_signature.o multi_thread.o base64.o base64_url.o
    ar -rs $@ $^

# jni 库编译前请安装 jdk,并且配置 JAVA_HOME 环境变量
jni: com_tls_sigcheck_tls_sigcheck.o libtlsignature.a
    g++ -shared -o jnisigcheck.so $^ ../linux/$(ARCH)/lib/jsoncpp/libjsoncpp.a ../openssl-dynamic/lib/libcrypto.a -lz

signature: signature.o libtlsignature.a
    g++ -o $@ $^ ../linux/$(ARCH)/lib/jsoncpp/libjsoncpp.a ../openssl-dynamic/lib/libcrypto.a -ldl -lz

sigcheck.so: sigcheck.o libtlsignature.a sigcheck.h
    g++ -shared -fPIC -o $@ $^ ../linux/$(ARCH)/lib/jsoncpp/libjsoncpp.a ../openssl-dynamic/lib/libcrypto.a -ldl -lz

.cpp.o:
    $(CPP) $(CFLAGS) -c $*.cpp

.c.o:
    $(CC) $(CFLAGS) -c $*.c

clean:  
    -rm -f *.o *.so *.a tls_licence_tools TAGS $(TARGETS)

You can see that jnisigcheck.so is generated using make jni. After executing the command, jnisigcheck.so was not generated as we imagined, and some dependency libraries need to be resolved: jni_md.h, in the code ../linux/ $ (ARCH)/lib/jsoncpp/libjsoncpp.a ( $ ( ARCH)=64) and missing ../openssl-dynamic/lib/libcrypto.a files.


Solve the problem

Solve the problem of jni_md.h (copy jni_md.h)

$ cd $JAVA_HOME/include
$ sudo cp darwin/jni_md.h .

compile jsoncpp

$ brew install scons
$ cd tls_sig_api-src/jsoncpp-src-0.5.0
$ python /usr/local/bin/scons platform=linux-gcc

The result of the execution is that
linux-gcc-4.2.1/libjson_linux-gcc-4.2.1_libmt.a
linux-gcc-4.2.1/libjson_linux-gcc-4.2.1_libmt.dylib is generated in the libs directory

compile openssl

// 进入tls_sig_api-src目录
$ tar -zxvf openssl-1.0.2a.tar.gz
$ mv openssl-1.0.2a openssl_x86_64
$ cd openssl_x86_64
$ ./Configure darwin64-x86_64-cc -shared
$ make

This generates libcrypto.a in the current directory

Create a soft chain and
enter tls_sig_api-src/src

$ mkdir -p ../linux/64/lib/jsoncpp
$ ln -s `cd ..;pwd`/jsoncpp-src-0.5.0/libs/linux-gcc-4.2.1/libjson_linux-gcc-4.2.1_libmt.a ../linux/64/lib/jsoncpp/libjsoncpp.a
$ mkdir -p ../openssl-dynamic/lib/
$ ln -s `cd ..;pwd`/openssl_x86_64/libcrypto.a ../openssl-dynamic/lib/libcrypto.a

When creating a link, the source needs to fill in the absolute path, so there is the above cd ..;pwd.

Compile jnisigcheck.so

//在src目录下,执行一下命令,则生成`jnisigcheck.so`
$ make clean
$ make jni

At this time, there is a jnisigcheck.so file in the src directory. Change
jnisigcheckLibPath to "tls_sig_api-src/src/jnisigcheck.so", and
you can run it on mac.

Reprint: http://www.cnblogs.com/javaDeveloper/p/6098145.html

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326507257&siteId=291194637