zoukankan      html  css  js  c++  java
  • 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回调加密解密的方法和上述加密解密方法相同。

  • 相关阅读:
    什么是 MyBatis?
    @RequestMapping 注解用在类上面有什么作用?
    如果你也用过 struts2.简单介绍下 springMVC 和 struts2 的区别有哪些?
    SpringMVC 流程?
    SpringMVC 工作原理?
    什么是 SpringMvc?
    依赖注入的三种实现方式?
    什么是IOC
    spring的作用
    什么是spring框架?
  • 原文地址:https://www.cnblogs.com/hooly/p/8087433.html
Copyright © 2011-2022 走看看