Java企业微信服务商代开发获取AccessToken示例

这里主要针对的是企业微信服务商代开发模式 文档地址

可以看到里面大致有三种token,一个是服务商的token,一个是企业授权token,还有一个是应用的token

这里面主要有下面几个参数

首先是服务商的 corpid 和 provider_secret ,这个可以在 应用管理-通用开发参数 里面查看

然后是企业的 corpid 和企业的永久授权码 permanent_code ,这两个是需要在企业授权的的时候通过回调获取的,具体请参考官方文档 获取永久授权码

最后就是应用的 suite_id 和 suite_secret 还需要一个 suite_ticket ,前面两个在应用信息里面就可以看到,suite_ticket 这个也是需要通过回调获取 ,具体参考官方文档 推送suite_ticket

拿到这些参数后就好说了,剩下的都是调接口

直接上代码

java 复制代码
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.qyzj.common.base.exception.BusinessException;
import com.qyzj.common.base.util.HttpsRequestUtil;
import com.qyzj.common.redis.constant.CacheKey;
import com.qyzj.common.redis.util.RedisUtil;
import com.qyzj.service.task.constant.WeiXinProviderConst;
import com.qyzj.service.task.constant.WeixinCorpKeyConst;
import com.qyzj.service.task.constant.WeixinCorpUrlConst;
import lombok.extern.java.Log;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @author Sakura
 * @date 2024/7/2 11:48
 */
@Component
@Log
public class WxTokenUtil {

    @Autowired
    private RedisUtil redisUtil;

    // 获取服务商token
    // 这里有一个问题,企业微信本身可能会使token提前失效,所以有时需要强制重新获取token,详情见官方文档
    // https://developer.work.weixin.qq.com/document/path/91200
    // 所以此处加了一个cache用来判断是否需要走缓存拿token,正常情况默认走缓存即可
    public String getProviderAccessToken(Boolean cache) {
        try {
            // 如果Redis里面有则直接取Redis里面的
            if (cache &&redisUtil.hasKey(CacheKey.KEY_CORP_PROVIDER_ACCESS_TOKEN)) {
                return redisUtil.get(CacheKey.KEY_CORP_PROVIDER_ACCESS_TOKEN).toString();
            }

            // 封装请求参数
            JSONObject json = new JSONObject();
            json.put("corpid", WeiXinProviderConst.corpId);
            json.put("provider_secret", WeiXinProviderConst.providerSecret);

            // 请求微信接口
            String accessTokenStr = HttpsRequestUtil.sendPostRequest(WeixinCorpUrlConst.PROVIDER_ACCESS_TOKEN, json.toJSONString());
            log.info("get provider access token return :" + accessTokenStr);
            JSONObject accessTokenJson = JSON.parseObject(accessTokenStr);
            // 获取 access_token
            String accessToken = accessTokenJson.getString(WeixinCorpKeyConst.providerAccessToken);
            //官方 expires_in 为 2个小时, 这里设置100分钟
            redisUtil.set(CacheKey.KEY_CORP_PROVIDER_ACCESS_TOKEN, accessToken, CacheKey.expireTime100);

            return accessToken;
        } catch (Exception e) {
            log.info("get provider access token error");
            e.printStackTrace();
            throw new BusinessException("get provider access token error");
        }
    }

    // 获取企业授权token
    public String getCorpAccessToken(String corpId, String permanentCode) {
        try {
            // 如果Redis里面有则直接取Redis里面的
            if (redisUtil.hasKey(CacheKey.KEY_CORP_ACCESS_TOKEN + "_" + corpId)) {
                return redisUtil.get(CacheKey.KEY_CORP_ACCESS_TOKEN + "_" + corpId).toString();
            }
            String accessTokenUrl = WeixinCorpUrlConst.CORP_ACCESS_TOKEN
                    .replace("#{CORP_ID}", corpId)
                    .replace("#{CORP_SECRET}", permanentCode);
            String accessTokenStr = HttpsRequestUtil.sendGetRequest(accessTokenUrl);
            log.info("get corp access token return :" + accessTokenStr);
            JSONObject accessTokenJson = JSON.parseObject(accessTokenStr);
            // 获取 access_token
            String accessToken = accessTokenJson.getString(WeixinCorpKeyConst.accessToken);
            //官方 expires_in 为 2个小时, 这里设置 100 分钟
            redisUtil.set(CacheKey.KEY_CORP_ACCESS_TOKEN + "_" + corpId, accessToken, CacheKey.expireTime100);

            return accessToken;
        } catch (Exception e) {
            log.info("get corp access token error");
            e.printStackTrace();
            throw new BusinessException("get corp access token error");
        }
    }

    // SuiteTicket是通过回调获取的,企微每10分钟会推送一次
    // 如果没有可手动在服务商企业微信后台刷新
    public String getSuiteTicket() {
        // 如果Redis里面有则直接取Redis里面的
        if (redisUtil.hasKey(CacheKey.KEY_CORP_SUITE_TICKET)) {
            return redisUtil.get(CacheKey.KEY_CORP_SUITE_TICKET).toString();
        } else {
            log.info("get suite ticket error");
            throw new BusinessException("get suite ticket error");
        }

    }

    // 获取应用token
    // 注意suiteTicket是从回调接口获取的
    // 注意该token需要配置ip白名单
    public String getSuiteAccessToken() {
        try {
            // 如果Redis里面有则直接取Redis里面的
            if (redisUtil.hasKey(CacheKey.KEY_SUITE_ACCESS_TOKEN)) {
                return redisUtil.get(CacheKey.KEY_SUITE_ACCESS_TOKEN).toString();
            }
            // 封装请求参数
            JSONObject json = new JSONObject();
            json.put(WeixinCorpKeyConst.suiteId, WeiXinProviderConst.suiteId);
            json.put(WeixinCorpKeyConst.suiteSecret, WeiXinProviderConst.suiteSecret);
            json.put(WeixinCorpKeyConst.suiteTicket, getSuiteTicket());

            String accessTokenStr = HttpsRequestUtil.sendPostRequest(WeixinCorpUrlConst.SUITE_ACCESS_TOKEN, json.toJSONString());
            log.info("get suite access token return :" + accessTokenStr);
            JSONObject accessTokenJson = JSON.parseObject(accessTokenStr);
            //获取 accessToken
            String accessToken = accessTokenJson.getString(WeixinCorpKeyConst.suiteAccessToken);
            //官方 expires_in 为 2个小时, 这里设置 100分钟
            redisUtil.set(CacheKey.KEY_SUITE_ACCESS_TOKEN, accessToken, CacheKey.expireTime100);

            return accessToken;
        } catch (Exception e) {
            log.info("get suite access token error");
            e.printStackTrace();
            throw new BusinessException("get suite access token error");
        }

    }
}

这里面用到的 HttpsRequestUtil 工具类

java 复制代码
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * @author Sakura
 * @date 2024/6/24 17:07
 */
public class HttpsRequestUtil {

    public static String sendPostRequest(String urlString, String jsonInputString) throws IOException {
        URL url = new URL(urlString);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();

        // 设置请求方法为POST
        connection.setRequestMethod("POST");

        // 设置请求头
        connection.setRequestProperty("Content-Type", "application/json");
        connection.setRequestProperty("Accept", "application/json");

        // 启用输出流,用于发送请求数据
        connection.setDoOutput(true);

        try (OutputStream os = connection.getOutputStream()) {
            byte[] input = jsonInputString.getBytes("utf-8");
            os.write(input, 0, input.length);
        }

        // 获取响应
        try (BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8"))) {
            StringBuilder response = new StringBuilder();
            String responseLine;
            while ((responseLine = br.readLine()) != null) {
                response.append(responseLine.trim());
            }
            return response.toString();
        } finally {
            // 关闭连接
            connection.disconnect();
        }
    }

    public static String sendGetRequest(String urlString) throws IOException {
        URL url = new URL(urlString);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();

        // 设置请求方法为GET
        connection.setRequestMethod("GET");

        // 设置请求头
        connection.setRequestProperty("Accept", "application/json");

        // 获取响应
        try (BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8"))) {
            StringBuilder response = new StringBuilder();
            String responseLine;
            while ((responseLine = br.readLine()) != null) {
                response.append(responseLine.trim());
            }
            return response.toString();
        } finally {
            // 关闭连接
            connection.disconnect();
        }
    }

}

还有企业微信的两个基本配置类

java 复制代码
public class WeixinCorpUrlConst {
    /**
     * 获取 provider_access_token 的 url
     */
    public static final String PROVIDER_ACCESS_TOKEN = "https://qyapi.weixin.qq.com/cgi-bin/service/get_provider_token";

    /**
     * 获取 suite_access_token 的 url
     */
    public static final String SUITE_ACCESS_TOKEN = "https://qyapi.weixin.qq.com/cgi-bin/service/get_suite_token";
    /**
     * 获取 pre_auth_code 的 url
     */
    public static final String PRE_AUTH_CODE = "https://qyapi.weixin.qq.com/cgi-bin/service/get_pre_auth_code?suite_access_token=";
    /**
     * 前端授权页面 的 url
     */
    public static final String URL_AUTH_PAGE = "https://open.work.weixin.qq.com/3rdapp/install";
    /**
     * 前端登录页面 的 url
     */
    public static final String URL_LOGIN_PAGE = "https://open.work.weixin.qq.com/wwopen/sso/3rd_qrConnect";
    /**
     * 获取 permanent_code 的 url
     */
    public static final String PERMANENT_CODE = "https://qyapi.weixin.qq.com/cgi-bin/service/get_permanent_code?suite_access_token=";
    /**
     * 获取 企业授权 access_token 的 url
     */
    public static final String CORP_ACCESS_TOKEN = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=#{CORP_ID}&corpsecret=#{CORP_SECRET}";
    /**
     * 获取 企业应用 access_token 的 url
     */
    public static final String ACCESS_TOKEN = "https://qyapi.weixin.qq.com/cgi-bin/gettoken";
    /**
     * 获取 应用信息 的 url
     */
    public static final String AGENT_INFO = "https://qyapi.weixin.qq.com/cgi-bin/agent/get?access_token=#{ACCESS_TOKEN}&agentid=#{AGENT_ID}";
    /**
     * 获取 部门列表 的 url
     */
    public static final String DEPARTMENT_LIST = "https://qyapi.weixin.qq.com/cgi-bin/department/list?access_token=#{ACCESS_TOKEN}";
    /**
     * 获取 部门成员列表 的 url
     */
    public static final String USER_LIST = "https://qyapi.weixin.qq.com/cgi-bin/user/list?access_token=#{ACCESS_TOKEN}&department_id=#{DEPARTMENT_ID}&fetch_child=1";
    /**
     * 获取 成员信息 的 url
     */
    public static final String USER_INFO = "https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token=#{ACCESS_TOKEN}&userid=#{USER_ID}";

    /**
     * 获取 企业管理员 的 url
     */
    public static final String ADMIN_USER_LIST = "https://qyapi.weixin.qq.com/cgi-bin/service/get_admin_list?suite_access_token=";
    /**
     * 第三方应用:获取 企业成员登录信息 的 url
     */
    public static final String PROVIDER_LOGIN_INFO = "https://qyapi.weixin.qq.com/cgi-bin/service/get_login_info?access_token=";
    /**
     * 自建应用:获取 企业成员登录信息 的 url
     */
    public static final String LOCAL_LOGIN_INFO = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token=#{ACCESS_TOKEN}&code=#{CODE}";

    public static final String LOGIN_DETAIL_INFO = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserdetail?access_token=#{ACCESS_TOKEN}";

    /**
     * 批量获取 客户列表 的 url
     */
    public static final String BATCH_CUSTOMER_LIST = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/batch/get_by_user?access_token=#{ACCESS_TOKEN}";
    /**
     * 获取 客户详情 的 url
     */
    public static final String CUSTOMER_INFO = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/get?access_token=#{ACCESS_TOKEN}&external_userid=#{EXTERNAL_USERID}";
    /**
     * 修改 客户备注信息 的url
     */
    public static final String UPDATE_CUSTOMER_REMARK = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/remark?access_token=#{ACCESS_TOKEN}";
    /**
     * 编辑 客户标签 的url
     */
    public static final String MARK_CUSTOMER_TAG = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/mark_tag?access_token=#{ACCESS_TOKEN}";

    /**
     * 获取 企业标签 的 url
     */
    public static final String TAG_GROUP_LIST = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/get_corp_tag_list?access_token=";

    /**
     * 添加 企业标签 的 url
     */
    public static final String ADD_TAG_GROUP = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/add_corp_tag?access_token=";

    /**
     * 修改 企业标签 的 url
     */
    public static final String UPDATE_TAG_GROUP = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/edit_corp_tag?access_token=";

    /**
     * 删除 企业标签 的 url
     */
    public static final String DELETE_TAG_GROUP = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/del_corp_tag?access_token=";

    /**
     * 获取 客户群列表 的 url
     */
    public static final String GROUP_CHAT_LIST = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/groupchat/list?access_token=#{ACCESS_TOKEN}";
    /**
     * 获取 客户群详情 的 url
     */
    public static final String GROUP_CHAT_INFO = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/groupchat/get?access_token=#{ACCESS_TOKEN}";
    /**
     * 查询 联系我 的 url
     */
    public static final String GET_CONTACT_WAY = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/get_contact_way?access_token=#{ACCESS_TOKEN}";
    /**
     * 添加 联系我 的 url
     */
    public static final String ADD_CONTACT_WAY = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/add_contact_way?access_token=#{ACCESS_TOKEN}";
    /**
     * 更新 联系我 的 url
     */
    public static final String UPDATE_CONTACT_WAY = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/update_contact_way?access_token=#{ACCESS_TOKEN}";
    /**
     * 删除 联系我 的 url
     */
    public static final String DEL_CONTACT_WAY = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/del_contact_way?access_token=#{ACCESS_TOKEN}";
    /**
     * 发送 欢迎语  的 url
     */
    public static final String SEND_WELCOME_MSG = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/send_welcome_msg?access_token=#{ACCESS_TOKEN}";
    /**
     * 上传 临时素材 的 url
     */
    public static final String UPLOAD_MEDIA = "https://qyapi.weixin.qq.com/cgi-bin/media/upload?access_token=";
    /**
     * 添加 入群欢迎语素材  的 url
     */
    public static final String ADD_GROUP_WELCOME_TEMPLATE = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/group_welcome_template/add?access_token=#{ACCESS_TOKEN}";
    /**
     * 更新 入群欢迎语素材  的 url
     */
    public static final String UPDATE_GROUP_WELCOME_TEMPLATE = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/group_welcome_template/edit?access_token=#{ACCESS_TOKEN}";
    /**
     * 删除 入群欢迎语素材  的 url
     */
    public static final String DEL_GROUP_WELCOME_TEMPLATE = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/group_welcome_template/del?access_token=#{ACCESS_TOKEN}";
    /**
     * 发送 应用消息  的 url
     */
    public static final String SEND_APPLICATION_MESSAGE = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=#{ACCESS_TOKEN}";
    /**
     * 获取待分配的离职成员列表 的url
     */
    public static final String DIMISSION_WAIT_ALLOT_CLIENT = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/get_unassigned_list?access_token=#{ACCESS_TOKEN}";
    /**
     * 分配离职成员的客户
     */
    public static final String DIMISSION_ALLOT_Client = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/resigned/transfer_customer?access_token=#{ACCESS_TOKEN}";
    /**
     *  分配离职成员的客户群
     */
    public static final String DIMISSION_ALLOT_GROUP = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/groupchat/transfer?access_token=#{ACCESS_TOKEN}";
    /**
     * 创建企业群发
     */
    public static final String ADD_CORP_MASS_URL = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/add_msg_template?access_token=#{ACCESS_TOKEN}";
    /**
     * 编辑客户企业标签
     */
    public static final String EDIT_CUSTOMER_CORP_TAG = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/mark_tag?access_token=#{ACCESS_TOKEN}";
    /**
     * 获取企业的jsapi_ticket
     */
    public static final String CORP_JS_API_TICKET = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=";
    /**
     * 获取应用的jsapi_ticket
     */
    public static final String AGENT_JS_API_TICKET = "https://qyapi.weixin.qq.com/cgi-bin/ticket/get?type=agent_config&access_token=";
    /**
     * 获取 开通了会话存档的员工
     */
    public static final String MSG_AUDIT_EMPLOYEE = "https://qyapi.weixin.qq.com/cgi-bin/msgaudit/get_permit_user_list?access_token=#{ACCESS_TOKEN}";
    /**
     * corpid转换 的 url
     */
    public static final String CORPID_TO_OPENCORPID = "https://qyapi.weixin.qq.com/cgi-bin/service/corpid_to_opencorpid?provider_access_token=";
    /**
     * userid转换 的 url
     */
    public static final String USERID_TO_OPENUSERID = "https://qyapi.weixin.qq.com/cgi-bin/batch/userid_to_openuserid?access_token=";

    public static final String GET_NEW_EXTERNAL_USERID = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/get_new_external_userid?access_token=";

    public static final String UNIONID_TO_EXTERNAL_USERID = "https://qyapi.weixin.qq.com/cgi-bin/idconvert/unionid_to_external_userid?access_token=";

    public static final String EXTERNAL_USERID_TO_PENDING_ID = "https://qyapi.weixin.qq.com/cgi-bin/idconvert/batch/external_userid_to_pending_id?access_token=";

    public static final String FINISH_OPENID_MIGRATION = "https://qyapi.weixin.qq.com/cgi-bin/service/finish_openid_migration?provider_access_token=";
}
java 复制代码
public class WeixinCorpKeyConst {
    public static String errcode = "errcode";
    public static String errmsg = "errmsg";

    // 服务商相关
    public static String corpId = "corpid";
    public static String authCorpId = "auth_corpid";
    public static String corpName = "corp_name";
    public static String suiteId = "suite_id";
    public static String suiteSecret = "suite_secret";
    public static String providerSecret = "provider_secret";
    public static String corpSecret = "corpsecret";
    public static String suiteTicket = "suite_ticket";
    public static String suiteAccessToken = "suite_access_token";
    public static String providerAccessToken = "provider_access_token";
    public static String preAuthCode = "pre_auth_code";
    public static String authCode = "auth_code";
    public static String permanentCode = "permanent_code";
    public static String authCorpInfo = "auth_corp_info";
    public static String authInfo = "auth_info";
    public static String agent = "agent";
    public static String agentId = "agentid";
    public static String accessToken = "access_token";

}

WeiXinProviderConst 就不贴了,里面就是记录上面那几个参数的

到这里可以发现,其实对接企业微信并不难,麻烦的地方就在于各种配置各种参数,然后就是回调,回调这个后面有空我也会整理出来

相关推荐
上海安当技术13 分钟前
单点登录 SSO 怎么选协议?SAML 2.0 / OAuth 2.0 / OIDC / CAS 对比与 ERP、OA、CRM 接入实战
java·开发语言
mifengxing14 分钟前
Java集合与泛型
java·算法·复习笔记
不才不才不不才30 分钟前
Spring 源码系列(14): JDK 动态代理 vs CGLIB,以及拦截器责任链
java·开发语言·spring
mifengxing31 分钟前
计算机组成原理——存储器系统
开发语言·考研·计算机组成原理·复习笔记·计算机408
流云鹤43 分钟前
05Java学习day(5)
java·学习
用户3126874877201 小时前
Spring 事件机制到底怎么传播的?ApplicationEvent 源码拆解
java·spring
默辨1 小时前
Spring AI Alibaba 核心知识点
java·ai·spring ai·spring alibaba
乐观的Terry1 小时前
10、发布系统-路由管理与灰度发布
java
唐青枫1 小时前
Java WebLogic 实战指南:从 Domain、数据源到 WAR 部署和集群管理
java
Yolanda_20221 小时前
Python学习-第九部分-错误处理与异常处理
开发语言·python·学习