Acceso a la puerta de enlace de la aplicación empresarial DingTalk (tutorial de nivel de niñera)

fondo

Al acoplarse con la plataforma abierta DingTalk, habrá situaciones en las que la plataforma abierta DingTalk deba devolver la llamada a nuestros proyectos. Generalmente, los proyectos se implementan en la intranet de la empresa. Por lo tanto, debemos realizar la penetración de la intranet. La comparación de la penetración de la intranet de uso
común herramientas es la siguiente Tabla. Se puede ver que DingTalk es la mejor opción cuando se acopla con la plataforma abierta DingTalk...
Este artículo presentará en detalle el proceso general de acoplamiento y construcción de DingTalk Enterprise Application Gateway

Comparación de penetración de herramientas de penetración de intranet de uso común

inserte la descripción de la imagen aquí

Puerta de enlace de aplicaciones empresariales

De hecho, antes, DingTalk también proporcionaba una herramienta de penetración de intranet. Sin embargo, debido a razones como el cumplimiento de la seguridad, los recursos del servicio y los costos de mantenimiento , DingTalk ya no proporcionará herramientas de penetración de intranet a partir del servicio del 21 de julio de 2022 , si necesita penetración de intranet. en la depuración del entorno local o de desarrollo y prueba, consulte el servicio de penetración de intranet frp creado por usted mismo en el documento.Por lo tanto, si la empresa tiene esta demanda, en línea con la seguridad, la estabilidad, en términos de simplicidad, DingTalk Enterprise Application Gateway también es una buena opción. Y puede ponerse en contacto con la empresa para crear una plataforma de prueba. Haga clic para ingresar la dirección del sitio web oficial

qué es

Enterprise Application Gateway, también conocido como DingTalk Zero Trust Gateway , se utiliza para proporcionar a las empresas la capacidad de acceder de forma segura a las aplicaciones de la intranet en la red externa. Puede reemplazar las soluciones VPN tradicionales y mejorar la velocidad de acceso a las aplicaciones en función de las capacidades de aceleración de la red de Alibaba Cloud. Esto producto Basado en el concepto de confianza cero, proporciona verificación de acceso continua y dinámica para garantizar la seguridad de la información digital empresarial en la mayor medida

puntos débiles del usuario

Las empresas generalmente colocan el sistema de aplicación central en la intranet empresarial o el área DMZ, y establecen el aislamiento de los límites de la red a través del firewall.Si los
empleados de la empresa acceden a la aplicación de la intranet empresarial a través de dispositivos móviles o dispositivos de PC en el lado de Internet, hay dos formas: y mapeo de puertos.Sin
embargo, debido a las vulnerabilidades de los dispositivos VPN o el mapeo de puertos a IP o URL de red externa, los piratas informáticos pueden acceder directamente o atacar aplicaciones empresariales, lo que resulta en una fuga de datos centrales de la empresa.

inserte la descripción de la imagen aquí

El método de acceso a la aplicación de intranet original de la empresa, como se muestra en la siguiente figura

inserte la descripción de la imagen aquí

La arquitectura general de la puerta de enlace de aplicaciones empresariales DingTalk se muestra en la siguiente figura.
inserte la descripción de la imagen aquí
Se recomiendan las siguientes cinco soluciones para que los usuarios empresariales accedan a aplicaciones de intranet.
inserte la descripción de la imagen aquí

cómo utilizar

Si está acostumbrado a consultar el sitio web oficial, puede consultar el proceso de configuración de la puerta de enlace de aplicaciones empresariales para crearlo.

El proceso de construir y pisar el foso.

proceso de construcción

Preparación

  1. La puerta de enlace de aplicaciones empresariales se ha activado. Si no está activado, debe usar DingTalk para escanear el código QR a continuación o ingresar a esta página para escanear el código QR e
    instalar la aplicación de puerta de enlace de aplicaciones empresariales DingTalk
    inserte la descripción de la imagen aquí
  2. Después de escanear, aparecerá la siguiente página.Si
    la empresa activa esta función, el nivel de cuenta debe ser un administrador (no se permiten subadministradores)
    inserte la descripción de la imagen aquí

Ver aplicaciones de Enterprise Gateway

  1. Después de comunicarse con éxito con el lado del cliente y escanear el código QR anterior, el lado del cliente puede obtener el mensaje rápido de activación exitosa a través de la siguiente figura:
    inserte la descripción de la imagen aquí

  2. En el fondo de administración de DingTalk -> banco de trabajo -> aplicación de tres partes, puede ver la entrada de la puerta de enlace de seguridad DingTalk, haga clic para ingresar al fondo de la puerta de enlace de seguridad Ingrese a la página de inicio de la puerta de enlace empresarial Página de inicio de la puerta de
    inserte la descripción de la imagen aquí
    enlace
    inserte la descripción de la imagen aquí
    empresarial
    inserte la descripción de la imagen aquí

Configurar conector

  1. Requisitos previos
    Tenga en cuenta que si necesita implementar en un servidor Linux, debe cumplir las siguientes condiciones (Windows también debe cumplir las siguientes condiciones)
    inserte la descripción de la imagen aquí

  2. Crear conector
    Seleccione Nuevo conector, seleccione el tipo de implementación, haga clic en Continuar, genere el siguiente comando y luego copie el siguiente comando de Linux
    inserte la descripción de la imagen aquí

  3. Después de ejecutar el comando de copia en el servidor Linux, puede ver que el conector se ha iniciado correctamente
    inserte la descripción de la imagen aquí

  4. Crear un nuevo grupo de conectores
    Agregue el conector recién creado al grupo de conectores La función del grupo de conectores: administre los conectores en lotes y utilícelo cuando aplique la administración más adelante
    inserte la descripción de la imagen aquí

Suplemento: configuración de autoencendido del conector

2022.12.12 Suplemento, cuando la empresa estaba haciendo ajustes de red, se desconectó el conector por la desconexión de la red, y se reinició el servidor por conveniencia, sin embargo, el conector no reinició después de reiniciar, por lo que trajo una serie de problemas Necesita configurar el inicio automático después de reiniciar El servidor actual es ubuntu, por lo que debe /etc/rc.localconfigurar el elemento de inicio automático a continuación.

# 进入rc.local
vim  /etc/rc.local
# 配置启动名称 
## cd 后面跟的是连通器脚本所在目录, 就是执行第2步创建连通器是所在创建的目录
## ;(分号)后面执行的命令就是图2圈出来的参数!!!
cd /home/dingding-getway/connector ;./start.sh -a endpoint.ztna-dingtalk.com:8021 -k 6cc96011442d42149bdfbf95a1c61343 -s 42e4d95397dd4a75bf8b51e1ac655026df1786927f77f4a788e951b9d79fc55b &

Figura 1
inserte la descripción de la imagen aquí
Figura 2
inserte la descripción de la imagen aquí

Configurar la gestión de aplicaciones

requisitos previos

  1. Crear una nueva aplicación
    Método 1: Crear una aplicación de terceros en el fondo de gestión - Workbench
    inserte la descripción de la imagen aquí

Método 2: Cree aplicaciones en la plataforma abierta DingTalk de acuerdo con los escenarios de uso

inserte la descripción de la imagen aquí

  1. Complete la información de la solicitud según sea necesario
    inserte la descripción de la imagen aquí

  2. Una vez que la configuración es exitosa, AgentId, AppKey y AppSecret se generan automáticamente para su uso en la conexión de la interfaz
    inserte la descripción de la imagen aquí

  3. Publicar la aplicación (se usa después de que se completa el programa de desarrollo)
    Después de que se libera la aplicación, otros usuarios pueden verla ( aquí se refiere a verla en su propio banco de trabajo, no en el banco de trabajo del fondo de administración ). la versión de la experiencia pertenece a la versión en escala de grises. Haga clic para ver la introducción de la versión de la aplicación
    inserte la descripción de la imagen aquí

aplicación de configuración

  1. Después de crear la aplicación, configure la aplicación de prueba en la administración de aplicaciones, haga clic en la aplicación no configurada y configúrela.
    inserte la descripción de la imagen aquí

  2. Administración de aplicaciones: configuración básica
    Haga clic en el "+" de la opción del conector, seleccione el conector implementado (o grupo de conectores) y establezca una conexión con la aplicación de prueba. Seleccione el
    nombre de dominio para configurar, el nombre de dominio aquí es la dirección de intranet + puerto (se pueden configurar asignaciones múltiples).Después de guardar la configuración, ¡se puede lograr la penetración de la intranet!

inserte la descripción de la imagen aquí

Configuración avanzada

Aviso:

  • A continuación se presenta la configuración avanzada. Si no hay un requisito especial, puede omitir la configuración avanzada (ir directamente al siguiente paso).
  • El requisito previo para la configuración aquí es que necesitamos configurar la política de acceso en el siguiente paso y obtener el nombre de dominio de acceso a la red externa antes de configurar

Debido a la introducción en segundo plano, la plataforma abierta DingTalk necesita implementar devoluciones de llamada de eventos en las aplicaciones creadas (esta interfaz se llama cuando cambia la estructura organizativa de la empresa). Sin embargo, cuando configuramos la política de acceso, la interfaz de devolución de llamada DingTalk todavía aparece: por
lo url 地址访问异常, 不允许3xxx跳转
que necesita realizar una configuración avanzada en nuestra plataforma de gestión-administración de aplicaciones, antes de que no haya configuración, se informará un error después de completar la url, y el contenido se muestra en la figura a continuación

inserte la descripción de la imagen aquí

  1. Configuración avanzada
    Añadir para permitir acceso anónimo url: el formato eshttps://生成的外网访问域名/事件回调sdk接口地址.*
    inserte la descripción de la imagen aquí
  2. Después de que la configuración sea exitosa, la prueba
    debe implementar el SDK de devolución de llamada de eventos en el servidor de la intranet antes de realizar la prueba.
    inserte la descripción de la imagen aquí

Configurar política de acceso

La gestión de políticas está a cargo de los administradores de la empresa, incluido el registro de políticas, la modificación de políticas, la eliminación de políticas, la desactivación de políticas, la habilitación de políticas, la priorización, etc.

  1. Luego haga clic en el botón "Crear política" para ingresar a la interfaz de creación de políticas
    inserte la descripción de la imagen aquí
  2. En la página de la política, complete la información de la política, los elementos de configuración son los siguientes
    inserte la descripción de la imagen aquí
    inserte la descripción de la imagen aquí
  3. Haga clic en Finalizar para crear la política.
    inserte la descripción de la imagen aquí

Obtener el nombre de dominio para el acceso a la red externa (pisando el foso)

  1. Después de configurar la política de acceso, puede obtener el nuevo nombre de dominio a través de la administración de la aplicación en lugar de acceder a través de la ip de la red pública que muestra el conector.
    inserte la descripción de la imagen aquí

  2. Se puede ver que la red interna ip+port que configuramos cada vez se asigna a un nombre de dominio único al que puede acceder la red externa , y el nuevo nombre de dominio se puede copiar según sea necesario
    inserte la descripción de la imagen aquí

  3. Probar el efecto de penetración de la intranet de la puerta de enlace empresarial
    Acceso a la intranet
    inserte la descripción de la imagen aquí
    Acceso a la red pública
    inserte la descripción de la imagen aquí

Adjunto: SDK de devolución de llamada de eventos de DingTalk

Dirección de introducción de devolución de llamada de evento: https://open.dingtalk.com/document/org/configure-event-subcription

  1. Método de cifrado y descifrado de plataforma abierta DingTalk

    
    import java.io.ByteArrayOutputStream;
    import java.nio.charset.Charset;
    import java.security.MessageDigest;
    import java.security.Permission;
    import java.security.PermissionCollection;
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Random;
    import java.security.Security;
    import java.lang.reflect.Field;
    
    import javax.crypto.Cipher;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    
    import com.alibaba.fastjson.JSON;
    
    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
     * JDK8的下载地址 https://www.oracle.com/java/technologies/javase-jce8-downloads.html
     * @Author caoHaiYang
     * @Date 2022/8/18 10:15
     */
    public class DingCallbackCrypto {
          
          
    
        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         企业自建应用-事件订阅, 使用appKey
         *                       企业自建应用-注册回调地址, 使用corpId
         *                       第三方企业应用, 使用suiteKey
         *
         * @throws DingTalkEncryptException 执行失败,请查看该异常的错误码和具体的错误信息
         */
        public DingCallbackCrypto(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 + "=");
        }
    
        public Map<String, String> getEncryptedMap(String plaintext) throws DingTalkEncryptException {
          
          
            return getEncryptedMap(plaintext, System.currentTimeMillis(), Utils.getRandomStr(16));
        }
    
        /**
         * 将和钉钉开放平台同步的消息体加密,返回加密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);
                //System.out.println(JSON.toJSONString(array));
                StringBuffer sb = new StringBuffer();
                for (int i = 0; i < 4; i++) {
          
          
                    sb.append(array[i]);
                }
                String str = sb.toString();
                System.out.println(str);
                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);
            }
        }
    
        public static class Utils {
          
          
            public Utils() {
          
          
            }
    
            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();
            }
    
            public static byte[] int2Bytes(int count) {
          
          
                byte[] byteArr = new byte[] {
          
          (byte)(count >> 24 & 255), (byte)(count >> 16 & 255), (byte)(count >> 8 & 255),
                        (byte)(count & 255)};
                return byteArr;
            }
    
            public static int bytes2int(byte[] byteArr) {
          
          
                int count = 0;
    
                for (int i = 0; i < 4; ++i) {
          
          
                    count <<= 8;
                    count |= byteArr[i] & 255;
                }
    
                return count;
            }
        }
    
        public static class PKCS7Padding {
          
          
            private static final Charset CHARSET = Charset.forName("utf-8");
            private static final int BLOCK_SIZE = 32;
    
            public PKCS7Padding() {
          
          
            }
    
            public static byte[] getPaddingBytes(int count) {
          
          
                int amountToPad = 32 - count % 32;
                if (amountToPad == 0) {
          
          
                    amountToPad = 32;
                }
    
                char padChr = chr(amountToPad);
                String tmp = new String();
    
                for (int index = 0; index < amountToPad; ++index) {
          
          
                    tmp = tmp + padChr;
                }
    
                return tmp.getBytes(CHARSET);
            }
    
            public static byte[] removePaddingBytes(byte[] decrypted) {
          
          
                int pad = decrypted[decrypted.length - 1];
                if (pad < 1 || pad > 32) {
          
          
                    pad = 0;
                }
    
                return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
            }
    
            private static char chr(int a) {
          
          
                byte target = (byte)(a & 255);
                return (char)target;
            }
        }
    
        public static class DingTalkEncryptException extends Exception {
          
          
            public static final int SUCCESS = 0;
            public static final int ENCRYPTION_PLAINTEXT_ILLEGAL = 900001;
            public static final int ENCRYPTION_TIMESTAMP_ILLEGAL = 900002;
            public static final int ENCRYPTION_NONCE_ILLEGAL = 900003;
            public static final int AES_KEY_ILLEGAL = 900004;
            public static final int SIGNATURE_NOT_MATCH = 900005;
            public static final int COMPUTE_SIGNATURE_ERROR = 900006;
            public static final int COMPUTE_ENCRYPT_TEXT_ERROR = 900007;
            public static final int COMPUTE_DECRYPT_TEXT_ERROR = 900008;
            public static final int COMPUTE_DECRYPT_TEXT_LENGTH_ERROR = 900009;
            public static final int COMPUTE_DECRYPT_TEXT_CORPID_ERROR = 900010;
            private static Map<Integer, String> msgMap = new HashMap();
            private Integer code;
    
            static {
          
          
                msgMap.put(0, "成功");
                msgMap.put(900001, "加密明文文本非法");
                msgMap.put(900002, "加密时间戳参数非法");
                msgMap.put(900003, "加密随机字符串参数非法");
                msgMap.put(900005, "签名不匹配");
                msgMap.put(900006, "签名计算失败");
                msgMap.put(900004, "不合法的aes key");
                msgMap.put(900007, "计算加密文字错误");
                msgMap.put(900008, "计算解密文字错误");
                msgMap.put(900009, "计算解密文字长度不匹配");
                msgMap.put(900010, "计算解密文字corpid不匹配");
            }
    
            public Integer getCode() {
          
          
                return this.code;
            }
    
            public DingTalkEncryptException(Integer exceptionCode) {
          
          
                super((String)msgMap.get(exceptionCode));
                this.code = exceptionCode;
            }
        }
        static {
          
          
            try {
          
          
                Security.setProperty("crypto.policy", "limited");
                RemoveCryptographyRestrictions();
            } catch (Exception var1) {
          
          
            }
    
        }
        private static void RemoveCryptographyRestrictions() throws Exception {
          
          
            Class<?> jceSecurity = getClazz("javax.crypto.JceSecurity");
            Class<?> cryptoPermissions = getClazz("javax.crypto.CryptoPermissions");
            Class<?> cryptoAllPermission = getClazz("javax.crypto.CryptoAllPermission");
            if (jceSecurity != null) {
          
          
                setFinalStaticValue(jceSecurity, "isRestricted", false);
                PermissionCollection defaultPolicy = (PermissionCollection)getFieldValue(jceSecurity, "defaultPolicy", (Object)null, PermissionCollection.class);
                if (cryptoPermissions != null) {
          
          
                    Map<?, ?> map = (Map)getFieldValue(cryptoPermissions, "perms", defaultPolicy, Map.class);
                    map.clear();
                }
    
                if (cryptoAllPermission != null) {
          
          
                    Permission permission = (Permission)getFieldValue(cryptoAllPermission, "INSTANCE", (Object)null, Permission.class);
                    defaultPolicy.add(permission);
                }
            }
    
        }
        private static Class<?> getClazz(String className) {
          
          
            Class clazz = null;
    
            try {
          
          
                clazz = Class.forName(className);
            } catch (Exception var3) {
          
          
            }
    
            return clazz;
        }
        private static void setFinalStaticValue(Class<?> srcClazz, String fieldName, Object newValue) throws Exception {
          
          
            Field field = srcClazz.getDeclaredField(fieldName);
            field.setAccessible(true);
            Field modifiersField = Field.class.getDeclaredField("modifiers");
            modifiersField.setAccessible(true);
            modifiersField.setInt(field, field.getModifiers() & -17);
            field.set((Object)null, newValue);
        }
        private static <T> T getFieldValue(Class<?> srcClazz, String fieldName, Object owner, Class<T> dstClazz) throws Exception {
          
          
            Field field = srcClazz.getDeclaredField(fieldName);
            field.setAccessible(true);
            return dstClazz.cast(field.get(owner));
        }
    
    }
    
    
  2. Interfaz de la capa del controlador

      /**
         * 事件回调方法
         *
         * @param msg_signature
         * @param timeStamp
         * @param nonce
         * @param json
         * @return
         */
        @RequestMapping("/callback")
        public Map<String, String> callBack(
                @RequestParam(value = "msg_signature", required = false) String msg_signature,
                @RequestParam(value = "timestamp", required = false) String timeStamp,
                @RequestParam(value = "nonce", required = false) String nonce,
                @RequestBody(required = false) JSONObject json) {
          
          
            try {
          
          
                // 1. 从http请求中获取加解密参数
                // 2. 使用加解密类型
                // Constant.OWNER_KEY 说明:
                // 1、开发者后台配置的订阅事件为应用级事件推送,此时OWNER_KEY为应用的APP_KEY。
                // 2、调用订阅事件接口订阅的事件为企业级事件推送,
                //      此时OWNER_KEY为:企业的appkey(企业内部应用)或SUITE_KEY(三方应用)
                DingCallbackCrypto callbackCrypto = new DingCallbackCrypto(dingTalkConfig.getToken(), dingTalkConfig.getAesKey(), dingTalkConfig.getAppkey());
                String encryptMsg = json.getString("encrypt");
                String decryptMsg = callbackCrypto.getDecryptMsg(msg_signature, timeStamp, nonce, encryptMsg);
                // 3. 反序列化回调事件json数据
                JSONObject eventJson = JSON.parseObject(decryptMsg);
                log.info("反序列化回调事件json数据:" + eventJson);
                String eventType = eventJson.getString("EventType");
                // 4. 根据EventType分类处理
                if ("check_url".equals(eventType)) {
          
          
                    // 测试回调url的正确性
                    log.info("测试回调url的正确性");
                } else if ("user_add_org".equals(eventType)) {
          
          
                    // 处理通讯录用户增加事件
                    log.info("发生了:" + eventType + "事件");
                } else {
          
          
                    // 添加其他已注册的
                    log.info("发生了:" + eventType + "事件");
                }
    
                // 5. 返回success的加密数据
                Map<String, String> successMap = callbackCrypto.getEncryptedMap("success");
                return successMap;
    
            } catch (DingCallbackCrypto.DingTalkEncryptException e) {
          
          
                e.printStackTrace();
            }
            return null;
        }
    
  3. Lista de devoluciones de llamadas de eventos proporcionadas por DingTalk
    Para otros tipos, haga clic en este enlace azul para obtener
    inserte la descripción de la imagen aquí
    inserte la descripción de la imagen aquí

Complemento: cuando se realiza la depuración conjunta de front-end, debe configurar los pasos para el front-end

Después de reutilizar la función de inicio de sesión de DingTalk, cuando el front-end realiza la depuración local, penetrará en la intranet de la puerta de enlace para que la red externa pueda probar el acceso. Por supuesto, también es posible construir directamente la URL de inicio de sesión del código QR de la intranet
, pero aquí nos enfocamos en la primera situación A. Explora su solución

  1. En la plataforma de puerta de enlace de aplicaciones empresariales DingTalk , configure el mapeo desde la red interna ip+port de la aplicación correspondiente a la red externa
    inserte la descripción de la imagen aquí

  2. Encuentre y copie la asignación de red externa asignada
    inserte la descripción de la imagen aquí

  3. En el desarrollo de aplicaciones empresariales de la plataforma abierta DingTalk, elija su propia aplicación. Configure la devolución de llamada de inicio de sesión
    Aquí puede configurar directamente la dirección de red interna o la dirección de red externa asignada en el paso anterior.
    inserte la descripción de la imagen aquí
    Si este paso no está configurado, el frente end accederá a esta página. Ocurrirán los siguientes problemas
    inserte la descripción de la imagen aquí


2023-01-03

Suplemento: Cómo configurar el conector de la puerta de enlace para que se inicie automáticamente

ubuntu

La idea general es seguir el blog de este tipo grande para configurar el software de inicio automático en Ubuntu.El servidor que uso es ubuntu 18.04

  1. Paso 1: verifique el directorio del sistema /lib/systemd/system/rc-local.service, si no lo creó usted mismo, el contenido del archivo es (si el archivo existe, no hay ningún elemento [Instalar], debe agregar tú mismo)

    #  SPDX-License-Identifier: LGPL-2.1+
    #
    #  This file is part of systemd.
    #
    #  systemd is free software; you can redistribute it and/or modify it
    #  under the terms of the GNU Lesser General Public License as published by
    #  the Free Software Foundation; either version 2.1 of the License, or
    #  (at your option) any later version.
     
    # This unit gets pulled automatically into multi-user.target by
    # systemd-rc-local-generator if /etc/rc.local is executable.
    [Unit]
    Description=/etc/rc.local Compatibility
    Documentation=man:systemd-rc-local-generator(8)
    ConditionFileIsExecutable=/etc/rc.local
    After=network.target
     
    [Service]
    Type=forking
    ExecStart=/etc/rc.local start
    TimeoutSec=0
    RemainAfterExit=yes
    GuessMainPID=no
     
    [Install]
    WantedBy=multi-user.target
    Alias=rc-local.service
    

    inserte la descripción de la imagen aquí

  2. Los archivos en el directorio etc también deben modificarse como se indicó anteriormente, verifique /etc/systemd/system/rc-local.service, si no existe tal archivo, agregue este archivo

    	#  SPDX-License-Identifier: LGPL-2.1+
    	#
    	#  This file is part of systemd.
    	#
    	#  systemd is free software; you can redistribute it and/or modify it
    	#  under the terms of the GNU Lesser General Public License as published by
    	#  the Free Software Foundation; either version 2.1 of the License, or
    	#  (at your option) any later version.
    	 
    	# This unit gets pulled automatically into multi-user.target by
    	# systemd-rc-local-generator if /etc/rc.local is executable.
    	[Unit]
    	Description=/etc/rc.local Compatibility
    	Documentation=man:systemd-rc-local-generator(8)
    	ConditionFileIsExecutable=/etc/rc.local
    	After=network.target
    	 
    	[Service]
    	Type=forking
    	ExecStart=/etc/rc.local start
    	TimeoutSec=0
    	RemainAfterExit=yes
    	GuessMainPID=no
    	 
    	[Install]
    	WantedBy=multi-user.target
    	Alias=rc-local.service
    	```
    
    
  3. Cree el archivo de script /etc/rc.local y escriba el comando de script que desea ejecutar

    vim /etc/rc.local
    

    inserte la descripción de la imagen aquí

  4. Dar permiso rc.local para ejecutar

    sudo chmod +x /etc/rc.local
    
  5. habilitar el servicio

    sudo systemctl enable rc-local
    sudo systemctl start rc-local.service
    sudo systemctl status rc-local.service
    

    inserte la descripción de la imagen aquí

  6. Reinicie la computadora para ver el efecto
    Después de reiniciar, verifique si la aplicación iniciada por el script puede ejecutarse normalmente
    inserte la descripción de la imagen aquí

sistema operativo centos

Consulte los tres métodos de Linux para configurar el inicio automático

  1. De acuerdo con la configuración del archivo del sistema, busque el script de inicio correspondiente y la ubicación del archivo del sistema/lib/systemd/system/rc-local.service
    inserte la descripción de la imagen aquí

  2. editar secuencia de comandos de iniciovim /etc/rc.d/rc.local
    inserte la descripción de la imagen aquí

  3. Otorgue permisos ejecutables al script chmod +x /etc/rc.d/rc.local

  4. Pruebe si el software se inicia después de reiniciar
    inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/qq_43371556/article/details/126600171
Recomendado
Clasificación