首先说明笔者是一个Java小白只是想把自己工作中遇到的一些有用的东西分享给大家,如有错误请指出,如有雷同请联系!

近日笔者在做对接微信服务号发送需求时,发现通过微信平台注册的服务号和公众号在发送模板信息时都需要开启微信认证。微信认证需要营业执照这些的,让笔者很伤脑筋。搜寻多方资料后找到了可以申请微信公众平台测试号的方法。

一.如何申请?

 1.打开地址:微信公众平台测试号申请

其中核心在于url和Token怎么去编写。建议读者先阅读一下微信的消息接口使用指南

2.配置URL和Token的准备

URL这一块笔者推荐的是用natapp这个软件来做内网穿透,如果读者有自己的服务器地址这一步可以跳过。

打开链接natapp官网后,注册并登录,找到二级域名

点击注册二级域名

域名选择一个自己喜欢的后查询,笔者这里购买的是只要能对接微信API的域名。支付完后点击购买隧洞

笔者只尝试过VIP_1型的隧道,免费的隧道端口会随机变更,对于小白笔者来说不太友好。

名字随便取一个就好,隧道协议用web,二级域名我们刚才已经购买了则不需要,测试用流量就选小流量,时长根据自己需求选择。购买成功后点击我的隧道

绑定刚才购买的二级域名,这里的本地端口默认是80,笔者有需求所以改成了8000。配置好在页面上点击客户端下载,将natapp下载,并复制authtoken。这个token很重要!

在natapp目录下输入cmd

输入natapp -authtoken=你的authtoken

这样子就显示内网穿透开启成功啦!

3.正式配置URL和Token

首先准备一个Spring Boot项目

其次编写一个控制层

/**
 * 用于测试是否能够具备申请测试号的条件
 */
@RestController
@RequestMapping("/webchat")
public class WebChatController {
    @GetMapping
    public String checkWebChat(WebChatRequestDTO requestDTO) throws NoSuchAlgorithmException {
        //在生产环境中使用加密的方式是最好的。
        if (WebChatUtils.checkWebChat(requestDTO)){
            return requestDTO.getEchostr(); //测试通过
        }
        return "error"; //测试失败
    }

    // return requestDTO.getEchostr(); //测试的时候可以直接返回
}

注意:这里的@RestController不要用@Controller不要会接收不到请求的。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class WebChatRequestDTO {
    /**
     * 签名
     */
    private String signature;

    /**
     * 时间戳
     */
    private String timestamp;

    /**
     * 随机数
     */
    private String nonce;

    /**
     * 随机字符串
     */
    private String echostr;
}
public class WebChatUtils {
    /**
     * 微信公众平台-测试公众号-接口配置信息-Token
     */
    private static String webChatToken = "wxtoken";

    /**
     * 核实微信的请求
     *
     * @param requestModel
     * @return
     */
    public static boolean checkWebChat(WebChatRequestDTO requestModel) throws NoSuchAlgorithmException {
        //将token、timestamp、nonce三个参数进行字典序排序
        String[] array = {webChatToken,requestModel.getTimestamp(),requestModel.getNonce()};
        Arrays.sort(array);
        //将三个参数字符串拼接成一个字符串
        String str = String.join("",array);
        //进行SHA-1 加密
        MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
        byte[] digest = messageDigest.digest(str.getBytes());
        //将二进制字节数组digest逐个转为16进制数据,进而转换为字符串数据
        StringBuilder hexString = new StringBuilder();
        for (byte b : digest) {
            /*
                &作为"按位与"运算,1&1=1,1&0=0,0&=0
                0xff中的0x是16进制数前缀,ff是两个十六进制的数,每个f用二进制表示是1111
                此处传的b是byte类型(8位),toHexString传的参数是int类型(32位),所以前面需要补24个0
                最后 "& oxff"就是把前面24个0去掉只要后8位
             */
            String s = Integer.toHexString(b & 0xff);
            //遇到长度==1的数据时,前头补0
            if (s.length() == 1){
                hexString.append(0);
            }
            hexString.append(s);
        }
        //开发者获得加密后的字符串可与signature对比,表示该氢气来源于微信
        return requestModel.getSignature().equals(hexString.toString());
    }
}

注意:这里的webChatToken一定要和你在申请测试号网页上编写的一致!

微信这里呢是要发送一个请求给你到你所编写的url当中,他要判断是否和你设置的token一致不一致就会报错(记得启动项目)

配置成功后在做如下操作

划到最下面有个功能服务

这样就可以完成测试号的申请啦!

二.如何发送推送消息?

1.模板配置

笔者自己的微信号去关注了这个公众测试号,然后点击新增测试模板,这图中我们可以看到笔者写了很多东西。因为微信模板他是有字数限制的,且要以{{xx.DATA}}结尾不然会被视作纯文本

为了做出对应的效果只能用最笨的空白符拼接方法,如下图。可以看到由后面有一个小空格。

2.代码实现

/**
 * 用于微信公众平台测试号的申请环节。对应url和token部分
 */
@RestController
@RequestMapping("/vx")
public class WeiXinController {

    @RequestMapping("/weChatPush")
    @ResponseBody
    public String weChatPush(String template_id,String msg){
        try {

//            template_id = "ULhzHcZ4Llfvn_Hra0E3wWrtmvzBPHxxwkswY3oZe44";

            List<String> ls = VxUtils.getOpenId(getAccessToken());
            for (int i=0;i<ls.size();i++){
                System.out.println(ls.get(i));
                push(ls.get(i),template_id,msg);
            }
            return ("推送成功");
        }catch (Exception e){
            e.printStackTrace();
            return ("推送失败");
        }
    }

    @PostMapping("/push")
    @ResponseBody
    public Object push(String openid, String template_id,String msg) {

        RestTemplate restTemplate = new RestTemplate();
        //为了简单测试 这里每次都获取最新的access_token(在实际开发中,应该在 access_token 快过期时再重新获取)
        String url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + getAccessToken();
        //推送的模版 (目前为了简单测试 很多参数都写死,固定了,但也可以根据需要动态传入参数)
        WxTemplate wxTe = new WxTemplate();
        wxTe.setTouser(openid);//用户的openid(接收人)
        wxTe.setTemplate_id(template_id);//订阅消息模板id
        wxTe.setUrl("");//跳转链接(暂时没有)

        //日期格式定义 转换
        DateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        String dateString = dateformat.format(new Date());

        msg = "你的文本";

        Map<String, TemplateData> result = new HashMap<>();
        String[] keys = {"contentA", "contentB", "contentC", "contentD", "contentE"};
        String[] contents = {
                "xxxx",
                "xxxx",
                "xxxx",
                "xxxx",
                "xxxx"
        };//这里可以配置自己想要的长度

        int start = 0;

        for (int i = 0; i < contents.length && start < msg.length(); i++) {
            int end = Math.min(start + contents[i].length(), msg.length());

            // 直接使用预定义内容的长度来定位结束位置
            result.put(keys[i], new TemplateData(msg.substring(start, end), "#79CCE9"));
            start = end; // 更新起始位置为当前结束位置

            // 如果不是最后一个部分,跳过接下来的第一个空格
            if (i < contents.length - 1 && start < msg.length() && msg.charAt(start) == ' ') {
                start++;
            }
        }
        result.put("time",new TemplateData(dateString,"#79CCE9"));
        wxTe.setData(result);

        ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, wxTe, String.class);
        return JSON.parseObject(responseEntity.getBody());
    }
}
/**
 * 微信相关的工具类
 */
public class VxUtils {

    /**
     * 获取微信公众号或者测试号的token
     * @return
     */
    public static String getAccessToken(){
        RestTemplate restTemplate = new RestTemplate();
        Map<String, String> params = new HashMap<>();
        params.put("APPID", "你的appid");  //微信公众号或者测试号的appid
        params.put("APPSECRET", "你的appsecret");  //微信公众号或者测试号的appsecret
        ResponseEntity<String> responseEntity = restTemplate.getForEntity(
                "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={APPID}&secret={APPSECRET}", String.class, params);
        String body = responseEntity.getBody();
        JSONObject object = JSON.parseObject(body);
        String Access_Token = object.getString("access_token");
        String expires_in = object.getString("expires_in");// 过期时长(两小时)
        //System.out.println("有效时长expires_in:" + expires_in);
        return Access_Token;
    }

    /**
     * 获取openid(关注者列表)
     * @param access_token
     * @return
     */
    public static List<String> getOpenId(String access_token){
        RestTemplate restTemplate = new RestTemplate();
        Map<String, String> params = new HashMap<>();
        params.put("access_token", access_token);  //
        ResponseEntity<String> responseEntity = restTemplate.getForEntity(
                "https://api.weixin.qq.com/cgi-bin/user/get?access_token={access_token}", String.class, params
        );

        String body = responseEntity.getBody();
        // 解析成JSONObject 并获取(叫data的JSON对象)JSONObject
        JSONObject object = JSON.parseObject(body).getJSONObject("data");
        // 获取 JSONObject 中的 JSONArray
        JSONArray array = object.getJSONArray("openid");

        System.out.println("array"+array);
        // JSONArray 转成 List<Strig> 集合(注意 alibaba 的fastjson 1.2.23以下的版本 和 1.2.3 的版本不包含 toJavaList()方法 )
        List<String> lists = array.toJavaList(String.class);
        System.out.println(lists.get(0));

        return lists;
    }


}
/**
 * 微信模版消息内容
 */
public class TemplateData {

    private String value;//消息内容
    private String color;//消息颜色

    //构造方法(空参)
    public TemplateData() {
    }

    //构造方法(有参)
    public TemplateData(String value,String color) {
        this.value = value;
        this.color = color;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

}
/**
 * 微信消息参数
 */
public class WxTemplate {
	
	//进行推送所需参数
    private String touser;//用户openid(接收人)
    private String template_id;//消息模版id
    private String url = "https://baidu.com";//跳转到某链接或某页面
    private Map<String, TemplateData> data;//要推送的模板内容

    public String getTouser() {
        return touser;
    }

    public void setTouser(String touser) {
        this.touser = touser;
    }

    public String getTemplate_id() {
        return template_id;
    }

    public void setTemplate_id(String template_id) {
        this.template_id = template_id;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public Map<String, TemplateData> getData() {
        return data;
    }

    public void setData(Map<String, TemplateData> data) {
        this.data = data;
    }

}

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐