(java)微信公众号开发 之 扫码关注 获取OPPENID 对该OPPENID发送相关的信息

(java)微信公众号开发 之 扫码关注 获取OPPENID 对该OPPENID发送相关的信息

因为公司项目需要对接微信公众号平台,所以把自己的笔记整理在此,先写下大概的项目需求,具体操作往下看。

源码:gitee地址:https://gitee.com/yangJingYiBlog/wx-openid

项目需求:

  1. 顾客在我们项目平台提交一个申请,这个填写的申请单中有一个手机号必填。
  2. 提交完我们会生成一个二维码,该二维码是携带参数(手机号)请求微信公众平台所返回的二维码(要点:1.获取access_token,2.获取Ticket来换取二维码)
  3. 顾客扫描二维码之后,微信平台会推送消息到我们自己项目.(要点:对微信推送的消息进行解析,并响应微信服务器)此时也就获取到了oppenID
  4. 我们获取到了oppenid,根据oppenid来给该oppenID用户发送任意的消息。

1.微信公众平台接口的配置

首先我们要对接微信公众平台,需要在该平台微信公众平台地址
开发->开发者工具->公众平台测试账号中进行配置 见下图
开发->开发者工具-
公众平台测试账号
具体配置

在配置下图的URL的时候需要先在程序中写你要写的URL接口,我这里是http://******/sys/wx
废话不多说了 直接上代码了。

@Controller
@RequestMapping("/sys")
public class WxController {
 	private static final Logger log = LoggerFactory.getLogger(WxController.class);
	/**
     * 微信服务器 对 微信公众号管理平台的 服务器地址(URL) 进行校验
     * @param signature
     * @param timestamp
     * @param nonce
     * @param echostr
     * @return
     */
    @GetMapping("/wx")
    @ResponseBody
    public String firstWxVerify(@RequestParam(value="signature",required=false) String signature,
                                @RequestParam(value="timestamp",required=false) String timestamp,
                                @RequestParam(value="nonce",required=false) String nonce,
                                @RequestParam(value="echostr",required=false) String echostr){
        System.out.println(" 微信服务端返回的数据 用于校验 微信配置中心的网址是否正确: >>>" + signature + "\t" + timestamp + "\t" + nonce + "\t" + echostr);
        log.info("开始签名验证:"+" PARAM VAL: >>>" + signature + "\t" + timestamp + "\t" + nonce + "\t" + echostr);
        if (StringUtils.isNotEmpty(signature) && StringUtils.isNotEmpty(timestamp)
                &&StringUtils.isNotEmpty(nonce) && StringUtils.isNotEmpty(echostr)) {
            String sTempStr = "";
            if (StringUtils.isNotEmpty(signature) ) {
                log.info("验证成功:-----------:"+sTempStr);
                return echostr;
            } else {
                log.info("验证失败:-----------:00000");
                return "-1";
            }
        } else {
            log.info("验证失败:-----------:11111");
            return "-1";
        }
    }
}

在此没有对signature签名进行再次加密 对比 需要的大佬可以自行对比
这个步骤完成就可以进行下面的URL配置啦。

配置
消息模板配置
这个在给对应的oppenID发送消息的时候需要用到
消息模板
上面的配置完以后,就需要进行程序开发了

2. 获取access_token

直接上代码了

编写token实体类:

public class Token {
    //接口访问凭证
    private String accessToken;
    //凭证有效期 秒
    private int expiresIn;
    private Long timestamp=System.currentTimeMillis();
    public String getAccessToken() {
        return accessToken;
    }
    public void setAccessToken(String accessToken) {
        this.accessToken = accessToken;
    }
    public int getExpiresIn() {
        return expiresIn;
    }
    public void setExpiresIn(int expiresIn) {
        this.expiresIn = expiresIn;
    }
}

获取token(在此appid,secret,这两个参数 自行去修改成自己的)

	public final static String token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
	/**
     * 直接获取token  ---->>   微信服务器
     * token 失效时间 7100
     * @return
     */
    private static Token getTokenByAppidAndAppsecret() {
        Token token = null;
        String requestUrl = token_url.replace("APPID", appid).replace("APPSECRET", secret);
        // 发起GET请求获取凭证
        String result = HttpClientUtil.doGet(requestUrl);
        JSONObject jsonObject = JSONObject.parseObject(result);
        log.info("获取Token返回结果----->;WxHelper-getTokenByAppidAndAppsecret:"+jsonObject);
        if (null != jsonObject) {
            token = new Token();
            String access_token = jsonObject.getString("access_token");
            if(null != access_token && !"".equals(access_token)){
                token.setAccessToken(access_token);
                token.setExpiresIn(jsonObject.getIntValue("expires_in"));
            }else {
                token = null;
                // 获取token失败
                log.error("获取token失败 errcode:{} errmsg:{}", jsonObject.getIntValue("errcode"), jsonObject.getString("errmsg"));
            }
        }
        return token;
    }

3.获取Ticket来换取二维码

直接上代码
实体类

public class QRcodeDto {
    //成功返回值
    //获取的二维码ticket,凭借此ticket可以在有效时间内换取二维码。
    private String ticket;
    //二维码图片解析后的地址,开发者可根据该地址自行生成需要的二维码图片
    private String url;
    //该二维码有效时间,以秒为单位。 最大不超过2592000(即30天),此字段如果不填,则默认有效期为30秒。
    private long expire_seconds;
    //二维码类型,QR_SCENE为临时的整型参数值,QR_STR_SCENE为临时的字符串参数值,QR_LIMIT_SCENE为永久的整型参数值,QR_LIMIT_STR_SCENE为永久的字符串参数值
    private String action_name;
    //二维码详细信息
    private Map action_info;
/*
    //场景值ID,临时二维码时为32位非0整型,永久二维码时最大值为100000(目前参数只支持1--100000)
    private String scene_id;
    //场景值ID(字符串形式的ID),字符串类型,长度限制为1到64
    private String scene_str;
*/
		getter setter 省略
}

获取二维码

    public final static String ticket_url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=ACCESS_TOKEN";
      //请求二维码
    public final static String QRcode_url = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET";
	
	/**
     * 获取Ticket来换取二维码
     * @param tokenBySession
     * @param params
     * @return
     */
    private static QRcodeDto getTicket(String tokenBySession, String params) {
        QRcodeDto qRcode = null;
        String requestUrl = ticket_url.replace("ACCESS_TOKEN", tokenBySession);
        String doPostResult = HttpClientUtil.doPostSSL(requestUrl,params);
        //String doPostResult = HttpClientUtil.doPost(requestUrl, params);
        log.info("************获取Ticket来换取二维码***********,{}",doPostResult);
        JSONObject jsonObject = JSONObject.parseObject(doPostResult);
        if (null != jsonObject) {
            try {
                return JSON.parseObject(doPostResult,QRcodeDto.class);
            } catch (JSONException e) {
                qRcode = null;
                // 获取Ticket失败
                log.error("获取Ticket失败 errcode:{} errmsg:{}", jsonObject.getIntValue("errcode"), jsonObject.getString("errmsg"));
            }
        }
        return qRcode;
    }	
    
	/**
     * 生成二维码(关注公众号)
     * @return
     */
    public static String  makeQRcodeByPhone(String phone){
        String tokenBySession = getTokenByAppidAndAppsecret();
        if(null != tokenBySession && !"".equals(tokenBySession)){
            //phone  需要跟 微信关联
            //封装二维码信息
            QRcodeDto qRcode = new QRcodeDto();
            qRcode.setExpire_seconds(7200l);
            //二维码类型
            qRcode.setAction_name("QR_STR_SCENE");
            Map<String, Map<String, String>> mapMap = new HashMap<>();
            Map<String, String> map = new HashMap<>();
            map.put("scene_str", phone);
            mapMap.put("scene", map);
            qRcode.setAction_info(mapMap);
            //Map<String, Object> params = JSON.parseObject(JSON.toJSONString(qRcode), Map.class);

            //去获二维码地址
            QRcodeDto qRcodeDto = getTicket(tokenBySession, JSON.toJSONString(qRcode));
            //根据二维码地址去保存到本地 并返回本地二维码地址
            String qRcode2 = getQRcode(qRcodeDto.getTicket());
            return qRcode2;
        }else {
            return "token为空!请检查对接微信平台的方法!";
        }
    }
		
	
	/**
     * 获取下载二维码地址
     * @param ticket
     * @return
     */
    private static String getQRcode(String ticket) {
        String requestUrl = QRcode_url.replace("TICKET", ticket);
        String download = downloadByNet(requestUrl, ticket);
        return download;
    }
    
	/**
     * 下载二维码
     * @param requestUrl
     * @param ticket
     * @return
     */
    private static String downloadByNet(String requestUrl, String ticket) {
        // 构造URL
        URL url = null;
        try {
            url = new URL(requestUrl);
            // 打开连接
            URLConnection con = url.openConnection();
            // 输入流
            InputStream is = con.getInputStream();
            // 1K的数据缓冲
            byte[] bs = new byte[4056];
            // 读取到的数据长度
            int len;
            long l = System.currentTimeMillis();
            // 输出的文件流
            String filename = "/qr/"+l+".jpg";  //下载路径及下载图片名称

            File files = new File("/qr");
            if (!files.exists()) {
                files.mkdirs();// 创建文件根目录
            }
            File file = new File(filename);
            FileOutputStream os = new FileOutputStream(file, true);
            // 开始读取
            while ((len = is.read(bs)) != -1) {
                os.write(bs, 0, len);
            }
            // 完毕,关闭所有链接
            os.close();
            is.close();
            return "/profile/qr/"+l+".jpg";
        } catch (Exception e) {
            return null;
        }
    }

3.获取oppenID,顾客扫描二维码之后,微信平台会推送消息到我们自己项目.

实体类
wxPushMessage

/**
 * 微信推送过来的数据
 * 微信API :https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_event_pushes.html#%E5%85%B3%E6%B3%A8/%E5%8F%96%E6%B6%88%E5%85%B3%E6%B3%A8%E4%BA%8B%E4%BB%B6
 */
@Root(name = "xml", strict = false)
public class WxPushMessage {

    //开发者微信号
    @Element(name = "ToUserName")
    private String ToUserName;
    //发送方帐号(一个OpenID)
    @Element(name = "FromUserName")
    private String FromUserName;
    //消息创建时间 (整型)
    @Element(name = "CreateTime", required = false)
    private Long CreateTime;
    //消息类型,event
    @Element(name = "Event", required = false)
    private String Event;
    //事件类型,subscribe
    @Element(name = "MsgType", required = false)
    private String MsgType;

    @Element(name = "CardId", required = false)
    private String CardId;

    @Element(name = "UserCardCode", required = false)
    private String UserCardCode;

    @Element(name = "ModifyBonus", required = false)
    private Integer ModifyBonus;
    //	事件KEY值,qrscene_为前缀,后面为二维码的参数值
    @Element(name = "EventKey", required = false)
    private String EventKey="";

    public void setEventKey(String eventKey) {
        if (eventKey.startsWith("qrscene_")){
            EventKey=eventKey.replace("qrscene_","");
        }else {
            EventKey = eventKey;
        }
    }
}

获取oppenID

@Controller
@RequestMapping("/sys")
public class WxController {

    private static final Logger log = LoggerFactory.getLogger(WxController.class);

    /**
     * //微信方回调
     * @param body 微信回调传送的参数
     * @return String  返回一个空串告诉微信服务器 接受到了响应
     */
    @PostMapping("/wx")
    @ResponseBody
    public String wxCallBack(@RequestBody String body){
        //接受到微信的数据后解析为javabean
        WxPushMessage wxPushMessage = (WxPushMessage) XmlUtil.fromXML(body,WxPushMessage.class);
        System.out.println("=========wxPushMessage==========="+wxPushMessage);
        if(null != wxPushMessage){
            //开发者微信号
            String toUserName = wxPushMessage.getToUserName();
            //FromUserName
            String fromUserName = wxPushMessage.getFromUserName();
            //消息创建时间 (整型)
            Long createTime = wxPushMessage.getCreateTime();
            //消息类型,event
            String msgType = wxPushMessage.getMsgType();
            // 事件类型,subscribe->订阅  unsubscribe取消
            String event = wxPushMessage.getEvent();
            // 事件KEY值,qrscene_为前缀,后面为二维码的参数值
            String eventKey = wxPushMessage.getEventKey();
            //表示消息推送类型是订阅 SCAN消息推送类型是  之前关注过
            if(null != event && ("subscribe".equals(event) || "SCAN".equals(event))){
                // eventKey  之前传递的值  phone
                //TODO  关于重试的消息排重,推荐使用FromUserName + CreateTime 排重。
               // 更新数据库 将openid 和 phone绑定
                
                return "";
            }else if(null != event && "unsubscribe".equals(event)){//消息推送类型是 取消订阅
                // 更新数据库 将openid 和 phone解除绑定
                
                return "";
            }else {
                return "";
            }
        }else {
            return "";
        }
    }
}

4. 我们获取到了oppenid,根据oppenid来给该oppenID用户发送任意的消息。

实体类

/**
 * @author YangJY
 * @date 2020-01-07
 * 发送模板消息
 * 接口调用请求说明
 * http请求方式: POST https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN
 */
public class SendData {
    private String value;
    private String color="#173177";
}

发送消息

@Controller
@RequestMapping("/sys")
public class WxController {

	//发送消息
    public final static String news_url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN";

    private static final Logger log = LoggerFactory.getLogger(WxController.class);

    @GetMapping("/send")
    @ResponseBody
    public Map<String, Object> send(HttpSession session){
        Map<String, Object> hashMap = new HashMap<>();
        hashMap.put("touser","oBxBQ6Jd5pJL3LoqGGteYoDSDoTE");
        hashMap.put("template_id","05DNtwB1aVSvCKvftU00XnzVX8daAbGwBWSLqbN_4-w");
        //hashMap.put("url","");  //非必填
        //hashMap.put("miniprogram",null); //非必填
        //现在封装data-> SendData
        //创建data的对象map
        Map<String, Object> data = new HashMap<>();
        SendData firstData = new SendData();
        firstData.setValue("隔壁老王");
        data.put("first",firstData);
        SendData keyword1 = new SendData();
        keyword1.setValue("艹");
        data.put("keyword1",keyword1);
        hashMap.put("data",data);
        String o =  JSONObject.toJSONString(hashMap);
        log.info("hashMap:==================== ,{}",hashMap);
        log.info("String:====================,{}",o);
        send(hashMap,session);
        return hashMap;
    }
}

	/**
     * 发送消息
     * @param data
     * @return
     */
    public static boolean send( Map<String,Object> data,HttpSession session) {
        String token = getTokenByAppidAndAppsecret(session);
        if(null != token && !"".equals(token)){
            String requestUrl = news_url.replace("ACCESS_TOKEN", token);
            String toJSONString = JSON.toJSONString(data);
            log.info("***************发消息的参数******************{}",toJSONString);
            String doPostSSLResult = HttpClientUtil.doPostSSL(requestUrl, toJSONString);
            log.info("***************发送消息******************{}",doPostSSLResult);
            JSONObject jsonObject = JSONObject.parseObject(doPostSSLResult);
            if (null != jsonObject) {
                return true;
            }
            return false;
        }else {
            log.error("WxHelper.send() 中 token 为空");
            return false;
        }
    }


效果
效果
gitee地址:https://gitee.com/yangJingYiBlog/wx-openid

在此已经全部完结,整理的不是特别好,我会把所有代码上传到gitee上,大家可以去参考一下。
如果有什么错误,希望大佬指出。

  • 10
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值