WxJava | weixin-java-mp组件核心源码剖析+access_token管理的最佳实践

2023-11-07 17:02:17 浏览数 (2)

  • 言:随着移动互联网的快速发展,微信公众号成为了企业与用户之间进行互动和沟通的重要渠道。为了更好地管理和维护微信公众号,本文将介绍如何基于WxJava SDK实现对access_token有效管理。 微信的"access_token"是有坑的,你知道有哪几个?

微信公众号管理平台要解决的问题

在微信公众号管理平台建设中,主要涉及两个场景: 1、调微信API执行相关操作 2、接收微信的回调并执行相关操作。

下载地址:https://www.processon.com/view/653e2a0a824d4a62d2d9c3e2

涉及到的三大系统: 微信客户端、微信后台服务、微信公众号管理系统【第三方服务】 唐成,公众号:的数字化之路微信生态圈 | 想让微信公众号做到“千人千面”?试试接入第三方服务!

weixin-java-mp组件简介

weixin-java-mp组件是一个基于Java语言开发的微信公众号开发工具包,是WxJava SDK在微信公众号场景的一个实现。

WxJava - 微信开发 Java SDK。 支持微信支付、微信开放平台、公众号、企业号/企业微信、小程序等的后端开发。 https://gitee.com/binary/weixin-java-tools

weixin-java-mp组件它提供了一系列的功能和方法,方便开发者快速集成和使用微信公众号的相关功能。

weixin-java-mp组件的架构主要包括以下几个部分:

1、配置管理:weixin-java-mp提供了配置管理的功能,方便开发者配置微信公众号的信息,如AppID、AppSecret等。开发者只需在代码中配置相应的信息即可使用weixin-java-mp进行开发。

2、API接口封装:weixin-java-mp将微信提供的API接口进行了封装,简化了开发者调用微信API的过程。开发者只需调用相应的方法即可完成对微信公众号的操作。

3、微信回调消息处理:weixin-java-mp提供了消息处理的功能,包括接收用户消息、处理事件推送等。开发者可以通过实现相应的消息处理器来对接收到的消息进行处理。

weixin-java-mp组件的核心类类图及作用域

高可用环境下管理access_token时遇到的问题

高可用(High Availability,HA)是指在系统发生故障或异常情况时,仍能够保持服务的稳定性和可用性,确保业务系统的持续运行。 什么是高可用五个9?系统的可用时间不能少于99.999%,即在一年中最多只有53分钟的故障时间。这是软件质量中用来衡量可用性的高标准。而相对的,"四个9"(99.99%)则被视为行业平均水平的可用性。 SparkDesk

要实现99.99%系统可用性,同一个服务的个数肯定>1。即使大于1个,也不能避免故障,譬如:语雀,这波故障,放眼整个互联网也是炸裂般的存在。

在高可用环境下,access_token的管理会遇到以下问题:

1、获取access_token的接口,每天调用次数限制。 2、access_token过期:access_token的有效期为2个小时,需要定期刷新; 3、access_token泄露:access_token是敏感信息,需要妥善保管,防止泄露。

解决办法如下:

1、定时刷新:设置定时任务,定期刷新access_token,确保其有效性;

2、使用缓存机制:将access_token缓存起来,减少频繁获取的次数; access_token需要进行缓存中心化管理。使用本地缓存会存在不一致的问题且强制刷新时比较困难。

高可用环境下access_token管理的最佳实践

技术方案:

要确保access_token是正常的,需要完成以下工作 :

1、确保缓存的aceess_token是正确的。 自定义一个WxMpService的实现,使用稳定版的获取access_token接口。

因为weixin-java-mp 4.5.0中仍然使用/cgi-bin/token来获取token。目前这个服务器是uat和pro环境共用。这两个环境互相隔离。如果uat环境的服务通过/cgi-bin/token获取到token,那么线上服务的token在5分钟后就会过期,但线上redis缓存的时间会大于5分钟。那么5分钟后,线上的使用这个token的操作就会报错“invalid access_token”,这就出现线上事故了。

另外,最近几个月中发现使用/cgi-bin/token时,微信没有保证在5分钟内新老access_token都可用。 所以强烈建议使用新接口: 稳定版接口https://api.weixin.qq.com/cgi-bin/stable_token 唐成,公众号:的数字化之路【微信生态圈】微信体系中的access_token有哪些?

2、使用中心化的中间件保存access_token。譬如 redis

本示例基于Redis中间件实现对access_token的中心化管理。

具体实现相关的类图:

step1:

step2:

具体实现:

代码语言:javascript复制
        <dependency>
            <groupId>com.github.binarywang</groupId>
            <artifactId>weixin-java-mp</artifactId>
            <version>4.5.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.7.17</version>
        </dependency>
代码语言:javascript复制
// 在weixin-java-mp组件中WxMpService接口抽象了对微信API的调用。
// StableAccessTokenServiceImpl类使用了微信公众号的新token接口
@Slf4j
public class StableAccessTokenServiceImpl extends BaseWxMpServiceImpl<CloseableHttpClient, HttpHost> {

    private CloseableHttpClient httpClient;
    private HttpHost httpProxy;

     ...

    @Override
    public String getAccessToken(boolean forceRefresh) throws WxErrorException {
        final WxMpConfigStorage config = this.getWxMpConfigStorage();
        if (!config.isAccessTokenExpired() && !forceRefresh) {
            return config.getAccessToken();
        }

        Lock lock = config.getAccessTokenLock();
        boolean locked = false;
        try {
            do {
                locked = lock.tryLock(100, TimeUnit.MILLISECONDS);
                if (!forceRefresh && !config.isAccessTokenExpired()) {
                    return config.getAccessToken();
                }
            } while (!locked);


            try {
                String json = String.format("{"grant_type": "client_credential", "appid": "%s", "secret": "%s"}", config.getAppId(), config.getSecret());
                // 
                HttpPost httpPost = new HttpPost("https://api.weixin.qq.com/cgi-bin/stable_token");
                httpPost.setHeader("Content-Type", "application/json");
                httpPost.setEntity(new StringEntity(json, "UTF-8"));

                if (this.getRequestHttpProxy() != null) {
                    RequestConfig requestConfig = RequestConfig.custom().setProxy(this.getRequestHttpProxy()).build();
                    httpPost.setConfig(requestConfig);
                }
                try (CloseableHttpResponse response = getRequestHttpClient().execute(httpPost)) {
                    String accessToken = this.extractAccessToken(new BasicResponseHandler().handleResponse(response));
                    log.info("获取accessToken完成 appid {} ", config.getAppId());
                    return accessToken;
                } finally {
                    httpPost.releaseConnection();
                }
            } catch (IOException e) {
                throw new WxRuntimeException(e);
            }
        } catch (InterruptedException e) {
            throw new WxRuntimeException(e);
        } finally {
            if (locked) {
                lock.unlock();
            }
        }
    }
    
}
代码语言:javascript复制
    @Bean
    public WxMpService wxMpService() {
        // 自定义调用微信API的 http请求组件
        WxMpService wxMpService = new StableAccessTokenServiceImpl();
        wxMpService.setMaxRetryTimes(3);
        return wxMpService;
    }
代码语言:javascript复制
    @Bean
    public RedisTemplateWxRedisOps redisTemplateWxRedisOps(StringRedisTemplate stringRedisTemplate) {
        //使用Redis来存取token
        return new RedisTemplateWxRedisOps(stringRedisTemplate);
    }

至此,access_token的稳定性工作已经搞定了。 微信公众号的多账户管理后面再展开

0 人点赞