java釘釘通訊錄同步


釘釘做了好好幾個項目了,和阿里雲還有阿里釘釘合作也挺不錯。因為之前就做過微信公眾號,接觸釘釘感覺還是比較順手的,雖然也有一些不一樣的地方。

因為之前寫了一個微信公眾號的開發文檔,一直想寫一個釘釘的開發文檔,一直沒有時間,先寫個釘釘通訊錄同步的吧~~

廢話不多說,先上菜吧~~

1.ORACLE官方網站下載JCE無限制權限策略文件:因為釘釘的通訊錄同步是通過回調來實現的,而回調信息是加密過的需要解密,先要替換jdk/jre里security文件夾內的兩個jar包:local_policy.jar和US_export_policy.jar

我用的是jdk8,其他版本請對應下載

替換方式:

(此文件夾的local_policy.jar和US_export_policy.jar是JDK8的,若是其他版本的請按照下放地址下載)
異常java.security.InvalidKeyException:illegal Key Size和『計算解密文字錯誤』的解決方案:

在官方網站下載JCE無限制權限策略文件
JDK6的下載地址:http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html

JDK7的下載地址:http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html

JDK8的下載地址:http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html

下載后解壓,可以看到local_policy.jar和US_export_policy.jar以及readme.txt。
如果安裝的是JRE,將兩個jar文件放到%JRE_HOME% \lib\security目錄下覆蓋原來的文件,
如果安裝的是JDK,將兩個jar文件放到%JDK_HOME%\jre\lib\security目錄下覆蓋原來文件。

2.通訊錄回調:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletInputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.taobao.api.ApiException;
import com.alibaba.fastjson.JSONObject;

/**
 * <p>通訊錄事件回調<p>
 * @version 1.0
 * @author li_hao
 * @date 2017年12月21日
 */
@WebServlet("/callbackreceive")
public class CallBackServlet extends HttpServlet {
    
    private static final long serialVersionUID = -1785796919047156450L;

    public CallBackServlet() {
        super();
    }
    
    protected void doPost(HttpServletRequest request, HttpServletResponse response) {
        doGet(request, response);
    }
    
    /*
     * 接收釘釘服務器的回調數據
     * 
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response){
        try {
            /** url中的簽名 **/
            String msgSignature = request.getParameter("signature");
            /** url中的時間戳 **/
            String timeStamp = request.getParameter("timestamp");
            /** url中的隨機字符串 **/
            String nonce = request.getParameter("nonce");
            /** 取得JSON對象中的encrypt字段     **/
            String encrypt = "";
            
            /** 獲取post數據包數據中的加密數據 **/
            ServletInputStream sis = request.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(sis));
            String line = null;
            StringBuilder sb = new StringBuilder();
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
            JSONObject jsonEncrypt = JSONObject.parseObject(sb.toString());
            encrypt = jsonEncrypt.getString("encrypt");
            
            String decodeEncrypt = decodeEncrypt(msgSignature, timeStamp, nonce, encrypt); //密文解密
            JSONObject decodeEncryptJson = JSONObject.parseObject(decodeEncrypt);
            
            String eventType = decodeEncryptJson.getString("EventType");  //回調類型
            String UserIds = decodeEncryptJson.getString("UserId");  //用戶發生變更的userid列表
            String DeptIds = decodeEncryptJson.getString("DeptId");  //部門發生變更的deptId列表
            String res = "success";  //res是需要返回給釘釘服務器的字符串,一般為success;"check_create_suite_url"和"check_update_suite_url"事件為random字段;(具體請查看文檔或者對應eventType的處理步驟)
            
            JSONObject jsonObjectData = new JSONObject();
            //根據不同的回調類型,進行相應的操作
            switch (eventType) {
            case AddressListRegister.USER_ADD_ORG :
                //通訊錄用戶增加
                
                break;
            case AddressListRegister.USER_MODIFY_ORG :
                //通訊錄用戶更改
                
                break;
            case AddressListRegister.USER_LEAVE_ORG :
                //通訊錄用戶離職
                
                break;
            case AddressListRegister.ORG_ADMIN_ADD :
                //通訊錄用戶被設為管理員
                
                break;
            case AddressListRegister.ORG_ADMIN_REMOVE :
                //通訊錄用戶被取消設置管理員
                
                break;
            case AddressListRegister.ORG_DEPT_CREATE :
                //通訊錄企業部門創建
                
                break;
            case AddressListRegister.ORG_DEPT_MODIFY :
                //通訊錄企業部門修改
                
                break;
            case AddressListRegister.ORG_DEPT_REMOVE :
                //通訊錄企業部門刪除
                
                break;
            case AddressListRegister.ORG_REMOVE :
                //企業被解散
                
                break;
            case AddressListRegister.ORG_CHANGE :
                //企業信息發生變更
                
                break;
            case AddressListRegister.LABEL_USER_CHANGE :
                //員工角色信息發生變更
                
                break;
            case AddressListRegister.LABEL_CONF_ADD :
                //增加角色或者角色組
                
                break;
            case AddressListRegister.LABEL_CONF_DEL :
                //刪除角色或者角色組
                
                break;
            case AddressListRegister.LABEL_CONF_MODIFY :
                //修改角色或者角色組
                
                break;
            case AddressListRegister.CHECK_URL :
                //測試回調接口事件類型
                
                System.out.println("測試回調接口!");
                break;
            default: // do something
                break;
            }
            response.getWriter().append(codeEncrypt(res, timeStamp, nonce).toString()); //返回加密后的數據
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    
    /**
     * 創建加密/解密 類
     * @return
     */
    public DingTalkEncryptor createDingTalkEncryptor(){
        DingTalkEncryptor dingTalkEncryptor = null;  //加密方法類
        try {
            dingTalkEncryptor = new DingTalkEncryptor(AddressListRegister.TOKEN, AddressListRegister.AES_KEY,AddressListRegister.CORPID);  //創建加解密類
        } catch (DingTalkEncryptException e) {
            e.printStackTrace();
        }
        return dingTalkEncryptor;
    }
    
    /**
     * encrypt解密
     * @param msgSignature
     * @param timeStamp
     * @param nonce
     * @param encrypt  密文
     * @return decodeEncrypt 解密后的明文
     */
    public String decodeEncrypt(String msgSignature,String timeStamp,String nonce,String encrypt){
            String decodeEncrypt = null;
            try {
                decodeEncrypt = createDingTalkEncryptor().getDecryptMsg(msgSignature, timeStamp, nonce, encrypt); //encrypt解密
            } catch (DingTalkEncryptException e) {
                e.printStackTrace();
            }
        return decodeEncrypt;
    }
    
    
    /**
     *  對返回信息進行加密
     * @param res
     * @param timeStamp
     * @param nonce
     * @return
     */
    public JSONObject codeEncrypt(String res,String timeStamp,String nonce){
        long timeStampLong = Long.parseLong(timeStamp);
        Map<String, String> jsonMap = null;
        try {
            jsonMap = createDingTalkEncryptor().getEncryptedMap(res, timeStampLong, nonce); //jsonMap是需要返回給釘釘服務器的加密數據包
        } catch (DingTalkEncryptException e) {
            System.out.println(e.getMessage());
            e.printStackTrace();
        }
        JSONObject json = new JSONObject();
        json.putAll(jsonMap);
        return json;
    }
    
    
    //測試方法
    public static void main(String[] args) throws ApiException {
        String accesstoken = "xxxxxxxxxxxxxxxxxxxxxxxxx";
        String token = AddressListRegister.TOKEN;
        String aesKey = AddressListRegister.AES_KEY;
        String callBackUrl = "http://xxxx/callbackreceive";
        
        List<String> listStr = new ArrayList<String>();
        listStr.add("user_add_org");
        listStr.add("user_modify_org");
        listStr.add("user_leave_org");
        
        listStr.add("org_dept_create");
        listStr.add("org_dept_modify");
        listStr.add("org_dept_remove");
        
        JSONObject registerCallBack = DingTalkUtil.updateCallBack(accesstoken, listStr, token, aesKey, callBackUrl);
        System.out.println("注冊事件返回:"+registerCallBack);
        
        JSONObject callBack = DingTalkUtil.getCallBack(accesstoken);
        System.out.println("查詢注冊事件:"+callBack);
        
        
        
    }
    
}

幾個參數和通訊錄注冊 需要監聽的事件類型:

/**
 * <p>幾個參數和通訊錄注冊 需要監聽的事件類型<p>
 * @version 1.0
 * @author li_hao
 * @date 2017年12月15日
 */
public class AddressListRegister{
    
    /**企業的corpid */
    public static final String CORPID = "xxxxxxxxxx";
    /**釘釘開放平台上,開發者設置的token */
    public static final String TOKEN = "token";
    /**數據加密密鑰。用於回調數據的加密,長度固定為43個字符,從a-z, A-Z, 0-9共62個字符中選取,您可以隨機生成,ISV(服務提供商)推薦使用注冊套件時填寫的EncodingAESKey */
    public static final String AES_KEY = "xxxxx7p5qnb6zs3xxxxxlkfmxqfkv23d40yd0xxxxxx";
    
    /**通訊錄用戶增加 */
    public static final String USER_ADD_ORG = "user_add_org";
    /**通訊錄用戶更改*/
    public static final String USER_MODIFY_ORG = "user_modify_org";
    /** 通訊錄用戶離職 */
    public static final String USER_LEAVE_ORG = "user_leave_org";
    /** 通訊錄用戶被設為管理員 */
    public static final String ORG_ADMIN_ADD = "org_admin_add";
    /** 通訊錄用戶被取消設置管理員 */
    public static final String ORG_ADMIN_REMOVE = "org_admin_remove";
    /**通訊錄企業部門創建*/
    public static final String ORG_DEPT_CREATE = "org_dept_create";
    /** 通訊錄企業部門修改 */
    public static final String ORG_DEPT_MODIFY = "org_dept_modify";
    /**通訊錄企業部門刪除*/
    public static final String ORG_DEPT_REMOVE = "org_dept_remove";
    /**企業被解散*/
    public static final String ORG_REMOVE = "org_remove";
    /**企業信息發生變更*/
    public static final String ORG_CHANGE = "org_change";
    /**員工角色信息發生變更*/
    public static final String LABEL_USER_CHANGE = "label_user_change";
    /**增加角色或者角色組*/
    public static final String LABEL_CONF_ADD = "label_conf_add";
    /**刪除角色或者角色組*/
    public static final String LABEL_CONF_DEL = "label_conf_del";
    /**修改角色或者角色組*/
    public static final String LABEL_CONF_MODIFY = "label_conf_modify";
    /**測試回調接口事件類型*/
    public static final String CHECK_URL = "check_url";
}

加解密方法:

import java.io.ByteArrayOutputStream;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.util.*;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;


/**
 * 加解密方法
 * 在ORACLE官方網站下載JCE無限制權限策略文件
 *     JDK6的下載地址:http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html
 *     JDK7的下載地址: http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html
 */
public class DingTalkEncryptor {

    private static final Charset CHARSET = Charset.forName("utf-8");
    private static final Base64         base64  = new Base64();
    private byte[]         aesKey;
    private String         token;
    private String         corpId;
    /**ask getPaddingBytes key固定長度**/
    private static final Integer AES_ENCODE_KEY_LENGTH = 43;
    /**加密隨機字符串字節長度**/
    private static final Integer RANDOM_LENGTH = 16;

    /**
     * 構造函數
     * @param token             釘釘開放平台上,開發者設置的token
     * @param encodingAesKey  釘釘開放台上,開發者設置的EncodingAESKey
     * @param corpId           ISV進行配置的時候應該傳對應套件的SUITE_KEY(第一次創建時傳的是默認的CREATE_SUITE_KEY),普通企業是Corpid
     * @throws DingTalkEncryptException 執行失敗,請查看該異常的錯誤碼和具體的錯誤信息
     */
    public DingTalkEncryptor(String token, String encodingAesKey, String corpId) throws DingTalkEncryptException{
        if (null==encodingAesKey ||  encodingAesKey.length() != AES_ENCODE_KEY_LENGTH) {
            throw new DingTalkEncryptException(DingTalkEncryptException.AES_KEY_ILLEGAL);
        }
        this.token = token;
        this.corpId = corpId;
        aesKey = Base64.decodeBase64(encodingAesKey + "=");
    }

    /**
     * 將和釘釘開放平台同步的消息體加密,返回加密Map
     * @param plaintext     傳遞的消息體明文
     * @param timeStamp      時間戳
     * @param nonce           隨機字符串
     * @return
     * @throws DingTalkEncryptException
     */
    public Map<String,String> getEncryptedMap(String plaintext, Long timeStamp, String nonce) throws DingTalkEncryptException {
        if(null==plaintext){
            throw new DingTalkEncryptException(DingTalkEncryptException.ENCRYPTION_PLAINTEXT_ILLEGAL);
        }
        if(null==timeStamp){
            throw new DingTalkEncryptException(DingTalkEncryptException.ENCRYPTION_TIMESTAMP_ILLEGAL);
        }
        if(null==nonce){
            throw new DingTalkEncryptException(DingTalkEncryptException.ENCRYPTION_NONCE_ILLEGAL);
        }
        // 加密
        String encrypt = encrypt(Utils.getRandomStr(RANDOM_LENGTH), plaintext);
        String signature = getSignature(token, String.valueOf(timeStamp), nonce, encrypt);
        Map<String,String> resultMap = new HashMap<String, String>();
        resultMap.put("msg_signature", signature);
        resultMap.put("encrypt", encrypt);
        resultMap.put("timeStamp", String.valueOf(timeStamp));
        resultMap.put("nonce", nonce);
        return  resultMap;
    }

    /**
     * 密文解密
     * @param msgSignature     簽名串
     * @param timeStamp        時間戳
     * @param nonce             隨機串
     * @param encryptMsg       密文
     * @return                  解密后的原文
     * @throws DingTalkEncryptException
     */
    public String getDecryptMsg(String msgSignature, String timeStamp, String nonce, String encryptMsg)throws DingTalkEncryptException {
        //校驗簽名
        String signature = getSignature(token, timeStamp, nonce, encryptMsg);
        if (!signature.equals(msgSignature)) {
            throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_SIGNATURE_ERROR);
        }
        // 解密
        String result = decrypt(encryptMsg);
        return result;
    }


    /*
     * 對明文加密.
     * @param text 需要加密的明文
     * @return 加密后base64編碼的字符串
     */
    private String encrypt(String random, String plaintext) throws DingTalkEncryptException {
        try {
            byte[] randomBytes = random.getBytes(CHARSET);
            byte[] plainTextBytes = plaintext.getBytes(CHARSET);
            byte[] lengthByte = Utils.int2Bytes(plainTextBytes.length);
            byte[] corpidBytes = corpId.getBytes(CHARSET);
            ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
            byteStream.write(randomBytes);
            byteStream.write(lengthByte);
            byteStream.write(plainTextBytes);
            byteStream.write(corpidBytes);
            byte[] padBytes = PKCS7Padding.getPaddingBytes(byteStream.size());
            byteStream.write(padBytes);
            byte[] unencrypted = byteStream.toByteArray();
            byteStream.close();
            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
            IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16);
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
            byte[] encrypted = cipher.doFinal(unencrypted);
            String result = base64.encodeToString(encrypted);
            return result;
        } catch (Exception e) {
            throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_ENCRYPT_TEXT_ERROR);
        }
    }

    /*
     * 對密文進行解密.
     * @param text 需要解密的密文
     * @return 解密得到的明文
     */
    private String decrypt(String text) throws DingTalkEncryptException {
        byte[] originalArr;
        try {
            // 設置解密模式為AES的CBC模式
            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
            IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));
            cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
            // 使用BASE64對密文進行解碼
            byte[] encrypted = Base64.decodeBase64(text);
            // 解密
            originalArr = cipher.doFinal(encrypted);
        } catch (Exception e) {
            throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_DECRYPT_TEXT_ERROR);
        }

        String plainText;
        String fromCorpid;
        try {
            // 去除補位字符
            byte[] bytes = PKCS7Padding.removePaddingBytes(originalArr);
            // 分離16位隨機字符串,網絡字節序和corpId
            byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20);
            int plainTextLegth = Utils.bytes2int(networkOrder);
            plainText = new String(Arrays.copyOfRange(bytes, 20, 20 + plainTextLegth), CHARSET);
            fromCorpid = new String(Arrays.copyOfRange(bytes, 20 + plainTextLegth, bytes.length), CHARSET);
        } catch (Exception e) {
            throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_DECRYPT_TEXT_LENGTH_ERROR);
        }

        // corpid不相同的情況
        if (!fromCorpid.equals(corpId)) {
            throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_DECRYPT_TEXT_CORPID_ERROR);
        }
        return plainText;
    }

    /**
     * 數字簽名
     * @param token         isv token
     * @param timestamp     時間戳
     * @param nonce          隨機串
     * @param encrypt       加密文本
     * @return
     * @throws DingTalkEncryptException
     */
    public String getSignature(String token, String timestamp, String nonce, String encrypt) throws DingTalkEncryptException {
        try {
            String[] array = new String[] { token, timestamp, nonce, encrypt };
            Arrays.sort(array);
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < 4; i++) {
                sb.append(array[i]);
            }
            String str = sb.toString();
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            md.update(str.getBytes());
            byte[] digest = md.digest();

            StringBuffer hexstr = new StringBuffer();
            String shaHex = "";
            for (int i = 0; i < digest.length; i++) {
                shaHex = Integer.toHexString(digest[i] & 0xFF);
                if (shaHex.length() < 2) {
                    hexstr.append(0);
                }
                hexstr.append(shaHex);
            }
            return hexstr.toString();
        } catch (Exception e) {
            throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_SIGNATURE_ERROR);
        }
    }

}

加解密異常類:

import java.util.HashMap;
import java.util.Map;

/**
 * 加解密異常類
 */
public class DingTalkEncryptException extends Exception {
    /**成功**/
    public static final int SUCCESS = 0;
    /**加密明文文本非法**/
    public final static int  ENCRYPTION_PLAINTEXT_ILLEGAL = 900001;
    /**加密時間戳參數非法**/
    public final static int  ENCRYPTION_TIMESTAMP_ILLEGAL = 900002;
    /**加密隨機字符串參數非法**/
    public final static int  ENCRYPTION_NONCE_ILLEGAL = 900003;
    /**不合法的aeskey**/
    public final static int AES_KEY_ILLEGAL = 900004;
    /**簽名不匹配**/
    public final static int SIGNATURE_NOT_MATCH = 900005;
    /**計算簽名錯誤**/
    public final static int COMPUTE_SIGNATURE_ERROR = 900006;
    /**計算加密文字錯誤**/
    public final static int COMPUTE_ENCRYPT_TEXT_ERROR  = 900007;
    /**計算解密文字錯誤**/
    public final static int COMPUTE_DECRYPT_TEXT_ERROR  = 900008;
    /**計算解密文字長度不匹配**/
    public final static int COMPUTE_DECRYPT_TEXT_LENGTH_ERROR  = 900009;
    /**計算解密文字suiteKey(ISV)或者corpid(普通企業)不匹配**/
    public final static int COMPUTE_DECRYPT_TEXT_CORPID_ERROR  = 900010;

    private static Map<Integer,String> msgMap = new HashMap<Integer,String>();
    static{
        msgMap.put(SUCCESS,"成功");
        msgMap.put(ENCRYPTION_PLAINTEXT_ILLEGAL,"加密明文文本非法");
        msgMap.put(ENCRYPTION_TIMESTAMP_ILLEGAL,"加密時間戳參數非法");
        msgMap.put(ENCRYPTION_NONCE_ILLEGAL,"加密隨機字符串參數非法");
        msgMap.put(SIGNATURE_NOT_MATCH,"簽名不匹配");
        msgMap.put(COMPUTE_SIGNATURE_ERROR,"簽名計算失敗");
        msgMap.put(AES_KEY_ILLEGAL,"不合法的aes key");
        msgMap.put(COMPUTE_ENCRYPT_TEXT_ERROR,"計算加密文字錯誤");
        msgMap.put(COMPUTE_DECRYPT_TEXT_ERROR,"計算解密文字錯誤");
        msgMap.put(COMPUTE_DECRYPT_TEXT_LENGTH_ERROR,"計算解密文字長度不匹配");
        msgMap.put(COMPUTE_DECRYPT_TEXT_CORPID_ERROR,"計算解密文字suiteKey(ISV)或者corpid(普通企業)不匹配");
    }

    public Integer  code;
    public DingTalkEncryptException(Integer exceptionCode){
        super(msgMap.get(exceptionCode));
        this.code = exceptionCode;
    }
}

PKCS7算法的加密填充:

import java.nio.charset.Charset;
import java.util.Arrays;

/*
 * PKCS7算法的加密填充
 */

public class PKCS7Padding {
    private final static Charset CHARSET    = Charset.forName("utf-8");
    private final static int     BLOCK_SIZE = 32;

    /**
     * 填充mode字節
     * @param count
     * @return
     */
    public static byte[] getPaddingBytes(int count) {
        int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE);
        if (amountToPad == 0) {
            amountToPad = BLOCK_SIZE;
        }
        char padChr = chr(amountToPad);
        String tmp = new String();
        for (int index = 0; index < amountToPad; index++) {
            tmp += padChr;
        }
        return tmp.getBytes(CHARSET);
    }

    /**
     * 移除mode填充字節
     * @param decrypted
     * @return
     */
    public static byte[] removePaddingBytes(byte[] decrypted) {
        int pad = (int) decrypted[decrypted.length - 1];
        if (pad < 1 || pad > BLOCK_SIZE) {
            pad = 0;
        }
        return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
    }

    private static char chr(int a) {
        byte target = (byte) (a & 0xFF);
        return (char) target;
    }

}

加解密工具類:

import java.util.Random;

/**
 * 加解密工具類
 */
public class Utils {
    /**
     *
     * @return
     */
    public static String getRandomStr(int count) {
        String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < count; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }


    /*
     * int轉byte數組,高位在前
     */
    public static byte[] int2Bytes(int count) {
        byte[] byteArr = new byte[4];
        byteArr[3] = (byte) (count & 0xFF);
        byteArr[2] = (byte) (count >> 8 & 0xFF);
        byteArr[1] = (byte) (count >> 16 & 0xFF);
        byteArr[0] = (byte) (count >> 24 & 0xFF);
        return byteArr;
    }

    /**
     * 高位在前bytes數組轉int
     * @param byteArr
     * @return
     */
    public static int bytes2int(byte[] byteArr) {
        int count = 0;
        for (int i = 0; i < 4; i++) {
            count <<= 8;
            count |= byteArr[i] & 0xff;
        }
        return count;
    }
}

接口方法:

/**
     * 通訊錄:注冊事件回調接口
     * @param accesstoken 企業的accesstoken
     * @param callBackTag  需要監聽的事件類型,共有20種(Array[String])
     * @param token      加解密需要用到的token,ISV(服務提供商)推薦使用注冊套件時填寫的token,普通企業可以隨機填寫
     * @param aesKey      數據加密密鑰。用於回調數據的加密,長度固定為43個字符,從a-z, A-Z, 0-9共62個字符中選取,您可以隨機生成,ISV(服務提供商)推薦使用注冊套件時填寫的EncodingAESKey
     * @param callBackUrl      接收事件回調的url
     * @return
     * @throws ApiException
     */
    public static JSONObject registerCallBack(String accesstoken,List<String> callBackTag,String token,String aesKey,String callBackUrl) throws ApiException{
        String url = CommomUrl.REGISTER_CALL_BACK.replace("ACCESS_TOKEN", accesstoken);
        JSONObject jsonReq = new JSONObject();
        jsonReq.put("call_back_tag", callBackTag);
        jsonReq.put("token", token);
        jsonReq.put("aes_key", aesKey);
        jsonReq.put("url", callBackUrl);
        
        System.out.println(jsonReq.toString());
        JSONObject jsonObject = doPostStr(url, jsonReq.toString());
        return jsonObject;
    }
    
    /**
     * 通訊錄:查詢事件回調接口
     * @param accesstoken  企業的accesstoken
     * @return
     */
    public static JSONObject getCallBack(String accesstoken){
        String url = CommomUrl.GET_CALL_BACK.replace("ACCESS_TOKEN", accesstoken);
        JSONObject jsonObject = doGetStr(url);
        return jsonObject;
    }
    
    /**
     * 通訊錄:更新事件回調接口
     * @param accesstoken 企業的accesstoken
     * @param callBackTag  需要監聽的事件類型,共有20種(Array[String])
     * @param token      加解密需要用到的token,ISV(服務提供商)推薦使用注冊套件時填寫的token,普通企業可以隨機填寫
     * @param aesKey      數據加密密鑰。用於回調數據的加密,長度固定為43個字符,從a-z, A-Z, 0-9共62個字符中選取,您可以隨機生成,ISV(服務提供商)推薦使用注冊套件時填寫的EncodingAESKey
     * @param callBackUrl      接收事件回調的url
     * @return
     * @throws ApiException
     */
    public static JSONObject updateCallBack(String accesstoken,List<String> callBackTag,String token,String aesKey,String callBackUrl) throws ApiException{
        String url = CommomUrl.UPDATE_CALL_BACK.replace("ACCESS_TOKEN", accesstoken);
        JSONObject jsonReq = new JSONObject();
        jsonReq.put("call_back_tag", callBackTag);
        jsonReq.put("token", token);
        jsonReq.put("aes_key", aesKey);
        jsonReq.put("url", callBackUrl);
        
        JSONObject jsonObject = doPostStr(url, jsonReq.toString());
        return jsonObject;
    }
    
    /**
     * 通訊錄:刪除事件回調接口
     * @param accesstoken  企業的accesstoken
     * @return
     */
    public static JSONObject deleteCallBack(String accesstoken){
        String url = CommomUrl.DELETE_CALL_BACK.replace("ACCESS_TOKEN", accesstoken);
        JSONObject jsonObject = doGetStr(url);
        return jsonObject;
    }

    /**
     * 通訊錄:獲取回調失敗的結果
     * @param accesstoken  企業的accesstoken
     * @return
     */
    public static JSONObject getCallBackFailedResult(String accesstoken){
        String url = CommomUrl.GET_CALL_BACK_FAILED_RESULT.replace("ACCESS_TOKEN", accesstoken);
        JSONObject jsonObject = doGetStr(url);
        return jsonObject;
    }

CommonUrl:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @version : 1.0
 * @Author : li_hao
 * @Description : 釘釘接口地址類
 * @Date : 2017-06-10 17:47
 **/
public class CommomUrl {
    private static Logger log = LoggerFactory.getLogger(CommomUrl.class);

    
    /** 注冊事件回調接口(請求方式:post) */
    public static final String REGISTER_CALL_BACK = "https://oapi.dingtalk.com/call_back/register_call_back?access_token=ACCESS_TOKEN";
    
    /** 查詢事件回調接口(請求方式:get) */
    public static final String GET_CALL_BACK = "https://oapi.dingtalk.com/call_back/get_call_back?access_token=ACCESS_TOKEN";
    
    /** 更新事件回調接口(請求方式:post) */
    public static final String UPDATE_CALL_BACK = "https://oapi.dingtalk.com/call_back/update_call_back?access_token=ACCESS_TOKEN";
    
    /** 刪除事件回調接口(請求方式:get) */
    public static final String DELETE_CALL_BACK = "https://oapi.dingtalk.com/call_back/delete_call_back?access_token=ACCESS_TOKEN";
    
    /** 獲取回調失敗的結果 (請求方式:get)*/
    public static final String GET_CALL_BACK_FAILED_RESULT = "https://oapi.dingtalk.com/call_back/get_call_back_failed_result?access_token=ACCESS_TOKEN";
    
}

get請求、post請求:

/**
     * get請求
     * @param url 為接口地址參數
     * @return
     */
    public static JSONObject doGetStr(String url){
         CloseableHttpClient httpClient = HttpClients.createDefault();
         HttpGet httpGet = new HttpGet(url);
         CloseableHttpResponse response = null;
         JSONObject jsonObject = null;//接收結果
         try {
            response = httpClient.execute(httpGet);
            HttpEntity entity = response.getEntity();//從消息體里獲取結果
            if(entity!=null){
                String result = EntityUtils.toString(entity,"UTF-8");
                jsonObject = JSONObject.parseObject(result);
            }
            EntityUtils.consume(entity);
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(response != null){
                    response.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
         return jsonObject;
    }
    
    /**
     * post請求
     * @param url 為接口地址參數
     * @param outStr
     * @return
     */
    public static JSONObject doPostStr(String url,String outStr){
        CloseableHttpClient httpClient = HttpClients.createDefault();
        //DefaultHttpClient httpClient = new DefaultHttpClient();
        HttpPost httpPost = new HttpPost(url);
        JSONObject jsonObject = null;
        try {
            httpPost.setEntity(new StringEntity(outStr, "UTF-8"));
            HttpResponse response = httpClient.execute(httpPost);
            String result = EntityUtils.toString(response.getEntity(), "UTF-8");
            jsonObject = JSONObject.parseObject(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return jsonObject;
    }

以上就是通訊錄同步的所有代碼了,注:釘釘ISV回調加密解密的方法和上述加密解密方法相同。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM