企业微信会话存档详细搭建以及问题处理

这篇具有很好参考价值的文章主要介绍了企业微信会话存档详细搭建以及问题处理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


写在前面

由于公司业务需求,需要开发企业微信的会话存档,从开始阅读文档到获取解密数据,中间趟过了不少坑,在此记录一下。


一、准备工作

  1. 登录企业微信后台,获取企业配置参数,企业微信后台登录地址:企业微信登录地址

  2. 生成一对公私钥,保存好密钥对生成地址企业微信会话存档开发,企业微信,服务器,windows,java

  3. 获取企业编码企业微信会话存档开发,企业微信,服务器,windows,java

  4. 设置企业后台,管理工具->会话内容存档,设置可信IP地址,获取会话存档Secret,设置消息加密公钥(将第二步生成的公钥填进去),需要带上BEGIN以及END企业微信会话存档开发,企业微信,服务器,windows,java

  5. 下载SDK,windows下为.dll文件,linux系统下为libWeWorkFinanceSdk_Java.so,使用SDK需要将
    Finance.java放入到项目的指定目录下,必须放下com.tencent.wework下企业微信会话存档开发,企业微信,服务器,windows,java

  6. Finance.java修改静态方法,由于我们部署在linux服务器中,所以加载.so文件,其它文章有说配置全局变量或者啥环境,觉得太麻烦,我这里配置的是文件的绝对路径,将libWeWorkFinanceSdk_Java.so上传到服务器上指定位置企业微信会话存档开发,企业微信,服务器,windows,java

  7. 项目中添加需要使用的依赖,注意!!!需要排除掉bcprov-jdk16,如果不排除,可能会在解密消息的时候出现错误

       <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcpg-jdk16</artifactId>
            <version>1.46</version>
            <exclusions> 
                <exclusion>
                    <groupId>org.bouncycastle</groupId>
                    <artifactId>bcprov-jdk16</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcpkix-jdk15on</artifactId>
            <version>1.64</version>
        </dependency>
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk15on</artifactId>
            <version>1.64</version>
        </dependency>

        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.9</version>
        </dependency>

二、使用步骤

  1. 企业微信后台设置会话回调事件,可以自己生成或者随机获取Token和AESKey需要保存好后续有用;配置测试回调地址,请求方式为GET企业微信会话存档开发,企业微信,服务器,windows,java
@GetMapping("/weChatPush")
    public String pathCheck(@RequestParam("msg_signature") String msgSignature
            , @RequestParam("timestamp") String timestamp
            , @RequestParam("nonce") String nonce
            , @RequestParam("echostr") String echostr) throws AesException {

        String sToken = "";
        String sCorpID = "";
        String sEncodingAESKey = "";
        WXBizJsonMsgCrypt wxcpt = new WXBizJsonMsgCrypt(sToken, sEncodingAESKey, sCorpID);
        //需要返回的明文
        String sEchoStr = null;
        log.info("获取 url 参数 msgSignature:{}  timestamp:{}   nonce:{}  echostr:{}",
                msgSignature, timestamp, nonce, echostr);
        try {
            sEchoStr = wxcpt.VerifyURL(msgSignature, timestamp, nonce, echostr);
            log.info("返回的明文: {}", sEchoStr);
            return sEchoStr;
        } catch (Exception e) {
            //验证URL失败,错误原因请查看异常
            log.error("验证URL失败,错误原因请查看异常", e);
        }
        return sEchoStr;
    }
/**
 * 对企业微信发送给企业后台的消息加解密示例代码.
 * 
 * @copyright Copyright (c) 1998-2014 Tencent Inc.
 */

// ------------------------------------------------------------------------

/**
 * 针对org.apache.commons.codec.binary.Base64,
 * 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本)
 * 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi
 */
package com.junbaooss.common.util.wechat;

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

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

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

/**
 * 提供接收和推送给企业微信消息的加解密接口(UTF8编码的字符串).
 * <ol>
 * 	<li>第三方回复加密消息给企业微信</li>
 * 	<li>第三方收到企业微信发送的消息,验证消息的安全性,并对消息进行解密。</li>
 * </ol>
 * 说明:异常java.security.InvalidKeyException:illegal Key Size的解决方案
 * <ol>
 * 	<li>在官方网站下载JCE无限制权限策略文件(JDK7的下载地址:
 *      http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html</li>
 * 	<li>下载后解压,可以看到local_policy.jar和US_export_policy.jar以及readme.txt</li>
 * 	<li>如果安装了JRE,将两个jar文件放到%JRE_HOME%\lib\security目录下覆盖原来的文件</li>
 * 	<li>如果安装了JDK,将两个jar文件放到%JDK_HOME%\jre\lib\security目录下覆盖原来文件</li>
 * </ol>
 */
public class WXBizJsonMsgCrypt {
	static Charset CHARSET = Charset.forName("utf-8");
	Base64 base64 = new Base64();
	byte[] aesKey;
	String token;
	String receiveid;

	/**
	 * 构造函数
	 * @param token 企业微信后台,开发者设置的token
	 * @param encodingAesKey 企业微信后台,开发者设置的EncodingAESKey
	 * @param receiveid, 不同场景含义不同,详见文档
	 * 
	 * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息
	 */
	public WXBizJsonMsgCrypt(String token, String encodingAesKey, String receiveid) throws AesException {
		if (encodingAesKey.length() != 43) {
			throw new AesException(AesException.IllegalAesKey);
		}

		this.token = token;
		this.receiveid = receiveid;
		aesKey = Base64.decodeBase64(encodingAesKey + "=");
	}

	// 生成4个字节的网络字节序
	byte[] getNetworkBytesOrder(int sourceNumber) {
		byte[] orderBytes = new byte[4];
		orderBytes[3] = (byte) (sourceNumber & 0xFF);
		orderBytes[2] = (byte) (sourceNumber >> 8 & 0xFF);
		orderBytes[1] = (byte) (sourceNumber >> 16 & 0xFF);
		orderBytes[0] = (byte) (sourceNumber >> 24 & 0xFF);
		return orderBytes;
	}

	// 还原4个字节的网络字节序
	int recoverNetworkBytesOrder(byte[] orderBytes) {
		int sourceNumber = 0;
		for (int i = 0; i < 4; i++) {
			sourceNumber <<= 8;
			sourceNumber |= orderBytes[i] & 0xff;
		}
		return sourceNumber;
	}

	// 随机生成16位字符串
	String getRandomStr() {
		String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
		Random random = new Random();
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < 16; i++) {
			int number = random.nextInt(base.length());
			sb.append(base.charAt(number));
		}
		return sb.toString();
	}

	/**
	 * 对明文进行加密.
	 * 
	 * @param text 需要加密的明文
	 * @return 加密后base64编码的字符串
	 * @throws AesException aes加密失败
	 */
	String encrypt(String randomStr, String text) throws AesException {
		ByteGroup byteCollector = new ByteGroup();
		byte[] randomStrBytes = randomStr.getBytes(CHARSET);
		byte[] textBytes = text.getBytes(CHARSET);
		byte[] networkBytesOrder = getNetworkBytesOrder(textBytes.length);
		byte[] receiveidBytes = receiveid.getBytes(CHARSET);

		// randomStr + networkBytesOrder + text + receiveid
		byteCollector.addBytes(randomStrBytes);
		byteCollector.addBytes(networkBytesOrder);
		byteCollector.addBytes(textBytes);
		byteCollector.addBytes(receiveidBytes);

		// ... + pad: 使用自定义的填充方式对明文进行补位填充
		byte[] padBytes = PKCS7Encoder.encode(byteCollector.size());
		byteCollector.addBytes(padBytes);

		// 获得最终的字节流, 未加密
		byte[] unencrypted = byteCollector.toBytes();

		try {
			// 设置加密模式为AES的CBC模式
			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);

			// 使用BASE64对加密后的字符串进行编码
			String base64Encrypted = base64.encodeToString(encrypted);

			return base64Encrypted;
		} catch (Exception e) {
			e.printStackTrace();
			throw new AesException(AesException.EncryptAESError);
		}
	}

	/**
	 * 对密文进行解密.
	 * 
	 * @param text 需要解密的密文
	 * @return 解密得到的明文
	 * @throws AesException aes解密失败
	 */
	String decrypt(String text) throws AesException {
		byte[] original;
		try {
			// 设置解密模式为AES的CBC模式
			Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
			SecretKeySpec key_spec = new SecretKeySpec(aesKey, "AES");
			IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));
			cipher.init(Cipher.DECRYPT_MODE, key_spec, iv);

			// 使用BASE64对密文进行解码
			byte[] encrypted = Base64.decodeBase64(text);

			// 解密
			original = cipher.doFinal(encrypted);
		} catch (Exception e) {
			e.printStackTrace();
			throw new AesException(AesException.DecryptAESError);
		}

		String jsonContent, from_receiveid;
		try {
			// 去除补位字符
			byte[] bytes = PKCS7Encoder.decode(original);

			// 分离16位随机字符串,网络字节序和receiveid
			byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20);

			int jsonLength = recoverNetworkBytesOrder(networkOrder);

			jsonContent = new String(Arrays.copyOfRange(bytes, 20, 20 + jsonLength), CHARSET);
			from_receiveid = new String(Arrays.copyOfRange(bytes, 20 + jsonLength, bytes.length),
					CHARSET);
		} catch (Exception e) {
			e.printStackTrace();
			throw new AesException(AesException.IllegalBuffer);
		}

		// receiveid不相同的情况
		if (!from_receiveid.equals(receiveid)) {
			throw new AesException(AesException.ValidateCorpidError);
		}
		return jsonContent;

	}

	/**
	 * 将企业微信回复用户的消息加密打包.
	 * <ol>
	 * 	<li>对要发送的消息进行AES-CBC加密</li>
	 * 	<li>生成安全签名</li>
	 * 	<li>将消息密文和安全签名打包成json格式</li>
	 * </ol>
	 * 
	 * @param replyMsg 企业微信待回复用户的消息,json格式的字符串
	 * @param timeStamp 时间戳,可以自己生成,也可以用URL参数的timestamp
	 * @param nonce 随机串,可以自己生成,也可以用URL参数的nonce
	 * 
	 * @return 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的json格式的字符串
	 * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息
	 */
	public String EncryptMsg(String replyMsg, String timeStamp, String nonce) throws AesException {
		// 加密
		String encrypt = encrypt(getRandomStr(), replyMsg);

		// 生成安全签名
		if (timeStamp == "") {
			timeStamp = Long.toString(System.currentTimeMillis());
		}

		String signature = SHA1.getSHA1(token, timeStamp, nonce, encrypt);

		// System.out.println("发送给平台的签名是: " + signature[1].toString());
		// 生成发送的json
		String result = JsonParse.generate(encrypt, signature, timeStamp, nonce);
		return result;
	}

	/**
	 * 检验消息的真实性,并且获取解密后的明文.
	 * <ol>
	 * 	<li>利用收到的密文生成安全签名,进行签名验证</li>
	 * 	<li>若验证通过,则提取json中的加密消息</li>
	 * 	<li>对消息进行解密</li>
	 * </ol>
	 * 
	 * @param msgSignature 签名串,对应URL参数的msg_signature
	 * @param timeStamp 时间戳,对应URL参数的timestamp
	 * @param nonce 随机串,对应URL参数的nonce
	 * @param postData 密文,对应POST请求的数据
	 * 
	 * @return 解密后的原文
	 * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息
	 */
	public String DecryptMsg(String msgSignature, String timeStamp, String nonce, String postData)
			throws AesException {

		// 密钥,公众账号的app secret
		// 提取密文
		Object[] encrypt = JsonParse.extract(postData);

		// 验证安全签名
		String signature = SHA1.getSHA1(token, timeStamp, nonce, encrypt[1].toString());

		// 和URL中的签名比较是否相等
		// System.out.println("第三方收到URL中的签名:" + msg_sign);
		// System.out.println("第三方校验签名:" + signature);
		if (!signature.equals(msgSignature)) {
			throw new AesException(AesException.ValidateSignatureError);
		}

		// 解密
		String result = decrypt(encrypt[1].toString());
		return result;
	}

	/**
	 * 验证URL
	 * @param msgSignature 签名串,对应URL参数的msg_signature
	 * @param timeStamp 时间戳,对应URL参数的timestamp
	 * @param nonce 随机串,对应URL参数的nonce
	 * @param echoStr 随机串,对应URL参数的echostr
	 * 
	 * @return 解密之后的echostr
	 * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息
	 */
	public String VerifyURL(String msgSignature, String timeStamp, String nonce, String echoStr)
			throws AesException {
		String signature = SHA1.getSHA1(token, timeStamp, nonce, echoStr);

		if (!signature.equals(msgSignature)) {
			throw new AesException(AesException.ValidateSignatureError);
		}

		String result = decrypt(echoStr);
		return result;
	}

}
  1. 点击保存的时候会对填写的接口发起请求,验证成功才能保存成功
  2. 当在会话存档的用户接收或者发送消息时,会对配置的接口发起一次请求(此处请求为POST),项目中可以根据回调去拉取会话数据,此处回调参数没有什么意义,不用在意参数,拉取消息解密代码及解密方法如下
@PostMapping("/weChatPush")
    public String callBack() {

        informationPullService.pullAndSaveMessage();

        return "200";
    }


public void pullAndSaveMessage() {
        long sdk = Finance.NewSdk();
        log.info("sdk:{}", sdk);
        log.info("初始化:{}", String.valueOf(Finance.Init(sdk, "", "")));
        String priKey = "-----BEGIN RSA PRIVATE KEY-----\n" +
               
                "-----END RSA PRIVATE KEY-----";

        /**
         * 获取seq,缓存如果不存在,则默认为0
         */
        String redisKey = "weChatMessage:gather:junbao";

        Long seq = 0L;

        if (redisUtil.exists(redisKey)) {

            seq = Long.parseLong(redisUtil.getString(redisKey));
        }

        long slice = Finance.NewSlice();
        long ret = Finance.GetChatData(sdk, seq, 100, null, null, 30, slice);
        if (ret != 0) {
            log.info("getchatdata ret " + ret);
            return;
        }

        WeChatMessageDTO weChatMessageDTO = JSONObject.parseObject(Finance.GetContentFromSlice(slice), WeChatMessageDTO.class);

        if (0 != weChatMessageDTO.getErrcode()) {
            log.error("获取会话数据失败:{}", weChatMessageDTO.getErrmsg());
            return;
        }

        //聊天内容
        List<WeChatMessageItemDTO> chatdata = weChatMessageDTO.getChatdata();
        for (WeChatMessageItemDTO chatdatum : chatdata) {

            //消息序号
            Long dataLong = chatdatum.getSeq();

            if (dataLong > seq) {
                seq = dataLong;
            }

            //消息ID
            String msgid = chatdatum.getMsgid();

            //加密密钥
            String encryptRandomKey = chatdatum.getEncrypt_random_key();
            //加密聊天消息
            String encryptChatMsg = chatdatum.getEncrypt_chat_msg();
            long msg = Finance.NewSlice();
            try {
                // 获取加密密钥
                String encryptKey = RsaUtil.decryptRSA(encryptRandomKey, priKey);
                // 解析密文.企业微信自有解密内容
                ret = Finance.DecryptData(sdk, encryptKey, encryptChatMsg, msg);
                if (ret != 0) {
                    log.info("解密聊天记录失败 ret :{}", ret);
                    continue;
                }
                // 获取切片中的内容
                String plaintext = Finance.GetContentFromSlice(msg);
                // 释放slice
                Finance.FreeSlice(msg);
                JSONObject plaintextJson = JSONObject.parseObject(plaintext);
                // 文件类型  "text"文本 ,"revoke"撤回消息
                String msgtype = plaintextJson.getString("msgtype");

                WeChatMessageGather weChatMessageGather = WeChatMessageGather.builder()
                        .msgId(msgid)
                        .messageType(msgtype)
                        .content(plaintext)
                        .seq(seq)
                        .createTime(new Date())
                        .build();

                boolean save = weChatMessageGatherService.save(weChatMessageGather);

                log.info("会话内容写入数据库:{}", save);
            } catch (Exception e) {
                log.error("循环拉会话异常", e);
            }
        }

        redisUtil.setDataAndExpiryDays(redisKey, String.valueOf(seq), 365);

        Finance.FreeSlice(slice);

        Finance.DestroySdk(sdk);
    }

@Data
@AllArgsConstructor
@NoArgsConstructor
public class WeChatMessageDTO implements Serializable {

/**
 * 0=拉取成功
 */
private Integer errcode;

private String errmsg;

private List<WeChatMessageItemDTO> chatdata;

}

@Data
@AllArgsConstructor
@NoArgsConstructor
public class WeChatMessageItemDTO implements Serializable {

/**
 * 消息序号
 */
private Long seq;

/**
 * 消息ID
 */
private String msgid;

private Integer publickey_ver;

private String encrypt_random_key;

private String encrypt_chat_msg;

}

解密方法

    public static String decryptRSA(String str, String privateKey) throws Exception {
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
        //此处的"RSA/ECB/PKCS1Padding", "BC"不可以改变,改变会导致解密乱码
        Cipher rsa = Cipher.getInstance("RSA/ECB/PKCS1Padding", "BC");
        rsa.init(Cipher.DECRYPT_MODE, getPrivateKey(privateKey));
        byte[] utf8 = rsa.doFinal(Base64.decodeBase64(str));
        String result = new String(utf8, "UTF-8");
        return result;
    }

    public static PrivateKey getPrivateKey(String privateKey) throws Exception {
        StringReader privateKeyReader = new StringReader(privateKey);
        PEMParser privatePemParser = new PEMParser(privateKeyReader);
        Object privateObject = privatePemParser.readObject();
        if (privateObject instanceof PEMKeyPair) {
            PEMKeyPair pemKeyPair = (PEMKeyPair) privateObject;
            JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
            PrivateKey privKey = converter.getPrivateKey(pemKeyPair.getPrivateKeyInfo());
            return privKey;
        }
        return null;
    }

说明

  1. 拉取的消息数据,msg_id唯一,可以通过这个字段,对消息进行去重
  2. 在接收到回调时,如何去拉取最新的数据呢?第一次拉取消息的seq是0,拉取消息的返回数据中有每一条消息对应的seq,可以将最大的seq更新、保存下来,在接收到数据回调之后,拉取数据时将最新的seq传入接口

过程中出现的问题以及解决办法

  1. 找不到SDK,需要确定填写的路径上是否有对应版本的jar包
  2. windows下报错:org.bouncycastle.openssl.PEMException"'s signer information does not match signer information of other classes in the same package ,linux下报错:org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.VerifyError: class org.bouncycastle.asn1.ASN1Primitive overrides final method equals.(Ljava/lang/Object;)Z 解决办法:确认一下pom有没有排除重复的jar包
  3. linux下报错:java.security.InvalidKeyException: unknown key type passed to RSA 解决办法:确认一下解密用的私钥,不要去除 BEGIN 、END以及换行符,保留原汁原味

结语

目前遇到的问题以及解决办法都在上面,当前是可以拉取并解密数据,如果你们还有其它问题,欢迎到评论区留言文章来源地址https://www.toymoban.com/news/detail-594879.html

到了这里,关于企业微信会话存档详细搭建以及问题处理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 企业微信小程序用户信息获取以及JSSDK使用

    海马汇与企业微信小程序 企业微信小程序访问的是在线子应用(即可直接通过浏览器访问),因此只需将海马汇打包部署到服务器上即可。 海马汇子应用目前使用过两种模式: 纯原生海马汇框架开发 该模式所有子应用/页面都需要自己开发,不存在标准子应用,因此开发工

    2024年02月11日
    浏览(44)
  • 企业数字化转型的五个常见问题以及回答

    数字化、数字化转型其实并不是新的概念,早在十年前就提出来了,但近两年才开始真正走向公众视野。由于概念的泛滥与传统信息化的双重叠加影响,多数企业存在认知上的诸多困扰。 数字化转型为何成为当前热点? 什么是数字化转型? 数字化转型的本质又是什么? 企业

    2024年02月08日
    浏览(50)
  • 幻兽帕鲁转移/迁移游戏存档之后,无法迁移角色存档,进入游戏需要重新建角色问题(已解决),服务器到服务器之间的存档转移

    很多朋友在迁移幻兽帕鲁游戏存档到服务器的时候,可能会遇到一个问题,就是迁移完成后,进入到游戏会发现又需要从头开始,重新新建角色。 其实这个问题也很好解决,因为Palworld服务端有两种,一种是有APPID,还有一种是没有APPID。   如果迁移的前后服务器版本不一致

    2024年02月20日
    浏览(44)
  • Hadoop简介以及集群搭建详细过程

    hadoop加群包括两个集群:hdfs集群,yarn集群 两个集群逻辑上分离,通常物理上在一起 两个集群都是标准的主从架构集群 逻辑上分离:两个集群相互之间没有依赖,互不影响 物理上在一起:某些角色今晚往往部署在同一台物理服务器上 MapReduce集群呢? MapReduce是计算框架、代

    2024年02月16日
    浏览(39)
  • 全网最详细的Python自动化测试+邮件推送+企业微信推送+Jenkins

    什么是python自动化?我理解的Python自动化测试就是把以前人为测试转化为机器测试的一种过程。自动化测试是一种比手工测试更快获得故障反馈的方法。 自动化测试是一种质量保障的方式,最重要的还是以做好一款高质量产品为前提。测试工作很多时候就是在不断平衡质量、

    2024年02月03日
    浏览(38)
  • docker集群的详解以及超详细搭建

    在我们日常学习或开发过程中,如果我们的服务均采用docker容器的方式运行,比如提供后端接口服务的容器 containerA 和提供数据存取服务的容器 containerB ,如下图所示,不同的docker容器拥有各自的ip地址和端口号。 本文假定一台主机只有一个docker进程。在这种情况下,容器

    2024年01月16日
    浏览(36)
  • 解决Ubuntu22.04.1上安装企业微信的问题

    本人工作的计算机电脑系统: Ubuntu22.04.1 Linux版本5.19.0-32-generic 由于公司经常在企业微信上发消息,手机有时不能及时查看,所以还是在这台linux上安装一个企业微信。 开始参考了很多资料,都是要安装deepin-wine,先克隆代码到本地,然后执行.sh脚本,再安装企业微信deb包,但

    2023年04月21日
    浏览(37)
  • web会话跟踪以及JWT响应拦截机制

    目录 JWT 会话跟踪 token 响应拦截器 http是无状态的,登录成功后,客户端就与服务器断开连接,之后再向后端发送请求时,后端需要知道前端是哪个用户在进行操作。 Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一 种基于 JSON 的开放标准((RFC 7519).定义了一种简

    2024年02月13日
    浏览(36)
  • uniapp开发企业微信应用中的定位问题记录

    项目背景 :开发工具为HBuilderX,框架为uniapp,开发移动端的Web应用,在企业微信中使用(自建应用),Web开发的应用,不是小程序。 需求点 :获取用户当前的位置信息,技术流程包括以下几个环节: 1、获取当前用户的经纬度位置信息,用的uniapp的 uni.getLocation() 接口。 2、

    2024年02月04日
    浏览(43)
  • 进程组,会话的基础概念,以及进程组,会话,控制终端,前台后台之间的联系(系列文章第二篇)

    这个系列的文章有四篇,其目的是为了搞清楚 : 进程,shell,shell进程,终端,控制终端,前台进程,后台进程,控制进程,前台进程组,后台进程组,会话,守护进程,init进程,用户进程,系统进程 它们之间的联系与区别 系列文章第一篇传送门:全面理解shell进程、终端

    2024年02月04日
    浏览(81)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包