Springboot微信公众号开发

我是个菜鸡,所以去B站看了视频,然后写了这个博客

一:开发前准备:

1.环境搭建:

去往:微信文档地址
去往:微信测试号申请
我们默认你已经拥有测试账号了,下面就开始

2.接入概述:

声明:我也是把文档的东西搬过来加上代码一起看
其实文档写的很清楚了,我们第一步就是去验证URL有效性成功后即接入生效,成为开发者。
在我们本地测试的时候,需要将我们的本地地址映射到公网,我们使用一个免费且非常方便的工具:natapp(注意的是这个免费的地址经常会变化,测试开发的时候一定注意,说多了都是泪啊)

第一步:填写服务器配置
我们测试号填写url和token,就可以开始验证是否可以连接了
测试号不需要(EncodingAESKey)可以先忽略

第二步:验证消息的确来自微信服务器
开发者提交信息后,微信服务器将发送GET请求到填写的服务器地址URL上
开发者通过检验signature对请求进行校验(下面有校验方式)。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;

/**
 * @author yhh
 * @Description:
 * @date 2020/5/13 14:24
 */
public class WxServlet extends HttpServlet {

    /**
     * 测试号-接口配置信息(连接)
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /**
         * signature	微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
         * timestamp	时间戳
         * nonce	随机数
         * echostr	随机字符串
         */
        String signature = req.getParameter("signature");
        String timestamp = req.getParameter("timestamp");
        String nonce = req.getParameter("nonce");
        String echostr = req.getParameter("echostr");

        //校验请求
        if(WxService.check(timestamp,nonce,signature)){
            System.out.println("校验成功");
            //原样返回
            PrintWriter writer = resp.getWriter();
            writer.print(echostr);
            writer.flush();
            writer.close();
        }else {
            System.out.println("校验失败");
        }
    }
}
public static boolean check(String timestamp, String nonce, String signature) {
        //1) 将token、timestamp、nonce三个参数进行字典序排序
        String[] strs = new String[]{timestamp, nonce, TOKEN};
        Arrays.sort(strs);
        //2)将三个参数字符串拼接成一个字符串进行sha1加密
        String str = strs[0] + strs[1] + strs[2];
        String mySign = sha1(str);
        //3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
        return mySign.equals(signature);
    }
 private static String sha1(String str) {
        try {
            //获取一个加密对象
            MessageDigest md = MessageDigest.getInstance("sha1");
            //加密
            byte[] digest = md.digest(str.getBytes());
            char[] chars = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
            StringBuilder sb = new StringBuilder();
            //处理加密结果,一个byte分别 处理 高四位和低四位,范围都是0-15(相当于16进制的数),所以分开处理
            for (byte b : digest) {
                //高四位向右移动4位,让他与15,得到一个0-15的数字,然后转换成16进制的
                sb.append(chars[(b >> 4) & 15]);
                //处理低四位
                sb.append(chars[b & 15]);
            }
            return sb.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }

        return null;
    }

我们的项目是Springboot的项目,所以定义的servlet需要被Spring管理起来

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.yhhcompany.servlet.WxServlet;

@SpringBootApplication
public class WechatApplication {

    public static void main(String[] args) {
        SpringApplication.run(WechatApplication.class, args);
    }
    @Bean  //一定要加,不然这个方法不会运行
    public ServletRegistrationBean getServletRegistrationBean() {
        //一定要返回ServletRegistrationBean
        ServletRegistrationBean bean = new ServletRegistrationBean(new WxServlet());
        //放入自己的Servlet对象实例
        bean.addUrlMappings("/wx");  //访问路径值
        return bean;
    }
}

到此就已经接入成功了

3.接收用户消息及统一回复

当普通微信用户向公众账号发消息时,微信服务器将POST消息的XML数据包到开发者填写的URL上
在这里插入图片描述
所以我们继续完善我们的servlet

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;

/**
 * @author yhh
 * @Description:
 * @date 2020/5/13 14:24
 */
public class WxServlet extends HttpServlet {
    /**
     * 接受消息和事件推送
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    	//这里是避免中文乱码问题
        req.setCharacterEncoding("utf8");
        resp.setCharacterEncoding("utf8");
        //接受的消息体
        Map<String,String> requestMap =  WxService.parseRequest(req.getInputStream());
        System.out.println(requestMap);
        //准备回复的数据包
        String xml = WxService.getResonse(requestMap);
        PrintWriter writer = resp.getWriter();
        writer.print(xml);
        writer.flush();
        writer.close();
    }

解析微信请求的XML,并转换成map

public static Map<String, String> parseRequest(ServletInputStream inputStream) {
        SAXReader saxReader = new SAXReader();
        HashMap<String, String> map = new HashMap<>();
        try {
            Document read = saxReader.read(inputStream);
            //获取根节点
            Element root = read.getRootElement();
            List<Element> elements = root.elements();
            for (Element element : elements) {
                map.put(element.getName(), element.getStringValue());
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return map;
    }

收到用户发送的消息,然后可以做自己的逻辑处理,注意返回给微信也是XML的数据包

 /**
     * 用于处理所有的事件和消息回复
     * 返回的是xml数据包
     *
     * @param requestMap
     * @return
     */
    public static String getResonse(Map<String, String> requestMap) {
        String msgType = requestMap.get("MsgType");
        BaseMessage msg = null;
        //不同的消息類型,需要轉換成xml數據包返回
        switch (msgType) {
            case "text":
                /**
                 * <xml>
                 *   <ToUserName><![CDATA[toUser]]></ToUserName>
                 *   <FromUserName><![CDATA[fromUser]]></FromUserName>
                 *   <CreateTime>12345678</CreateTime>
                 *   <MsgType><![CDATA[text]]></MsgType>
                 *   <Content><![CDATA[你好]]></Content>
                 * </xml>
                 */
                msg = processingText(requestMap);
                break;
            case "image":
                break;
            case "voice":
                break;
            case "music":
                break;
            case "video":
                break;
            case "news":
                break;
            case "event":
                msg = dealEvent(requestMap);
                break;
            default:
                break;
        }
        String s = objToXml(msg);
        return s;
    }

对象转XML,我们引入的是Xstream的jar包

private static String objToXml(BaseMessage msg) {
        XStream xStream = new XStream();
        xStream.processAnnotations(TextMessage.class);
//        xStream.processAnnotations(TextMessage.class);
//        xStream.processAnnotations(TextMessage.class);
//        xStream.processAnnotations(TextMessage.class);
//        xStream.processAnnotations(TextMessage.class);
        String s = xStream.toXML(msg);
        String trim = s.trim();
        return trim;
    }

这里就采用面向对象的思想封装了返回对象

private static BaseMessage processingText(Map<String, String> requestMap) {
		//这里的返回内容你想写什么就写什么
        TextMessage tm = new TextMessage(requestMap,"老铁666");
        return tm;
    }

在贴着两个对象

@Data
@ToString
@XStreamAlias("xml")
public class TextMessage extends BaseMessage {
    @XStreamAlias("Content")
    private String content ;
    public TextMessage(Map<String, String> requestMap,String content) {
        super(requestMap);
        this.setMsgType("text");
        this.content =content ;
    }
}
/**
 * @author yhh
 * @Description:
 * @date 2020/5/13 17:42
 */
@Data
@XStreamAlias("xml")
public class BaseMessage {
    /**
     * 参数	是否必须	描述
     * ToUserName	是	接收方帐号(收到的OpenID)
     * FromUserName	是	开发者微信号
     * CreateTime	是	消息创建时间 (整型)
     * MsgType	是	消息类型,文本为text
     */
    @XStreamAlias("ToUserName")
    private String toUserName ;
    @XStreamAlias("FromUserName")
    private String fromUserName ;
    @XStreamAlias("CreateTime")
    private String createTime ;
    @XStreamAlias("MsgType")
    private String msgType ;

    public BaseMessage(Map<String,String> requestMap) {
        this.toUserName = requestMap.get("FromUserName");
        this.fromUserName = requestMap.get("ToUserName");
        this.createTime = System.currentTimeMillis()/1000+"";
    }
}
4.补充一点:如何获取access_token

access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。

先定义一个对象用来临时存储access_token

import lombok.Data;

/**
 * @author yhh
 * @Description:
 * @date 2020/5/15 14:25
 */
@Data
public class AccessToken {
    private String accessToken;
    //好久过期
    private long expireDate;

    //这里传token和过期时间
    public AccessToken(String accessToken, String expireIn) {
        this.accessToken = accessToken;
        this.expireDate = System.currentTimeMillis()+Integer.parseInt(expireIn)*1000;
    }

    /**
     * 判断token是否过期
     * @return
     */
    public Boolean isExpire(){
        return System.currentTimeMillis()>expireDate;
    }
}

获取access_token,过期的话重新获取,并将access_token 存储起来

public class WxService {
    //token 是我们在测试号自己随便写的
    private static final String TOKEN = "youhonghan";
    //获取access_token的地址
    private static final String ACCESSTOKEN_URL="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
    //测试号给的
    private static final String APPID="xx";
    private static final String APPSECRET="xx";
    //z暂存access_token
    private static AccessToken at ;
    //发送post请求封装自己的菜单
    private static final String MUNE_URL="https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";

    private static void getToken() {
        String url=ACCESSTOKEN_URL.replace("APPID",APPID).replace("APPSECRET",APPSECRET);
        String access = get(url);
        JSONObject object = JSONObject.parseObject(access);
        String accessToken = object.getString("access_token");
        String expiresIn = object.getString("expires_in");
        //获取accesstoken 并存起来
        at = new AccessToken(accessToken, expiresIn);
    }

    /**
     * 向外暴露,判断access_token是否过期
     * @return
     */
    public static String getAccessToken(){
        if(at==null||at.isExpire()){
            getToken();
        }
        String accessToken = at.getAccessToken();
        return accessToken;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值