公众号开发_公众号开发教程 - CSDN
精华内容
参与话题
  • 微信公众号开发基本流程

    万次阅读 多人点赞 2019-04-26 09:40:21
    过年前后做了个微信公众号项目,已经过去一段时间了,抽空回忆总结下基本流程吧,不然很快估计自己就忘了。。 微信公众平台官网:https://mp.weixin.qq.com 文章目录一、注册公众号二、了解公众号管理页面三、必备...

    背景:
    过年前后做了个微信公众号项目,已经过去一段时间了,抽空回忆总结下基本流程吧,不然很快估计自己就忘了。。

    微信公众平台官网:https://mp.weixin.qq.com



    一、注册公众号

    在这里插入图片描述
    首先注册时可以看到公众号有三种类型,个人用户大多数选择订阅号,而企业用户一般选择服务号和企业号

    我们平常大多数关注的都是订阅号,他们统一都放置在微信应用的订阅号消息列表中,没有微信支付等高级功能,只是用于发布文章等基础功能。
    在这里插入图片描述

    服务号企业号都在会话列表,和我们的微信好友是同级别的位置,具备微信支付等高级功能,一般是某个企业品牌的对外操作窗口,如海底捞火锅、顺丰速运等。
    在这里插入图片描述

    我们前期开发测试只需要注册个人订阅号即可,真正开发使用的是开发者工具里的测试号,具体下面会说。

    真正生产的话,使用的都是经过微信认证的订阅号、服务号、企业号。


    二、了解公众号管理页面

    我们在微信公众平台扫码登录后可以发现管理页面左侧菜单栏有丰富的功能:
    在这里插入图片描述
    大概可以分为这几大模块:
    首页功能小程序管理推广统计设置开发

    作为开发人员,首先应该关注的是设置、开发模块;而作为产品运营人员,关注的是功能、管理、推广模块;作为数据分析人员,关注的是统计模块。

    首先我们不妨各个功能模块都点击看一看,大概了解下我们能做些什么。可以确认的是,这个微信公众平台当然不只是给开发人员使用的,它提供了很多非技术人员可在UI界面上交互操作的功能模块。

    如配置消息回复、自定义菜单、发布文章等:
    在这里插入图片描述
    这个时候我们可能会想:这些功能好像非技术人员都能随意操作,那么还需要我们技术人员去开发吗?

    答案是: 如果只是日常简单的推送文章,就像我们关注的大多数公众号一样,那确实不需要技术人员去开发;但是,如果你想将你们的网站嵌入进去公众号菜单里(这里指的是把前端项目的首页链接配置在自定义菜单),并且实现微信端的独立登录认证、获取微信用户信息、微信支付等高级功能,或者觉得UI交互的配置方式无法满足你的需求,你需要更加自由、随心所欲的操作,那么我们就必须启用开发者模式了,通过技术人员的手段去灵活控制公众号。

    这里有一点需要注意,如果我们决定技术人员开发公众号,必须启用服务器配置,而这将导致UI界面设置的自动回复和自定义菜单失效!

    我们在 开发 - 基本配置 - 服务器配置 中点击启用
    在这里插入图片描述
    在这里插入图片描述
    我们团队就遇到过这种情况:两个项目组共用一个公众号,结果一个启用了服务器配置,使另一个项目组手动配置的菜单失效了。所以要注意这点!

    至于服务器配置中的选项代表什么意思、如何填写,我们下面再讲。


    三、必备开发者工具的使用

    在这里插入图片描述
    我们进入 开发 - 开发者工具, 可以发现微信提供了六种开发者工具,其中前四种属于开发必备:开发者文档在线接口调试工具web开发者工具公众平台测试账号

    1.开发者文档

    在这里插入图片描述
    这个不用说!在我们开发中属于最最最基础和重要的东西了,我们要想熟练开发公众号,首先必须熟读开发者文档!有些功能的开发甚至非要反复研读、咬文嚼字一番不可。PS:该文档吐槽的地方也不少,有些地方的确讲的不够明确!

    2.在线接口调试工具

    在这里插入图片描述
    这个工具也算比较实用,包含大多数接口的在线调试,我们可以直接在上面输入参数,获取微信服务端的返回结果。

    3.web开发者工具

    在这里插入图片描述
    这个工具是一款桌面应用,需要下载,它通过模拟微信客户端的UI使得开发者可以使用这个工具方便地在PC或者Mac上进行开发和调试工作,一般是前端使用该工具进行页面、接口调试。

    4.公众平台测试账号

    在这里插入图片描述
    这个测试号工具对我们的重要性可以说是仅次于开发者文档。我们可以创建测试号无需申请、认证真实的公众帐号、可在测试帐号中体验并测试微信公众平台所有高级接口。并且所有的配置都可在一个页面上编辑,使开发测试变得极其便利。


    四、细读开发者文档

    文档地址:https://mp.weixin.qq.com/wiki

    需要注意的是,细读开发者文档不是让你所有模块都去阅读,而是重点的重复细读,非重点的选择性阅读。
    在这里插入图片描述
    其中前两个模块:开始前必读开始开发,属于重点关注对象,也是整个微信开发的基石所在,需要多读几遍。其次是微信网页开发模块微信网页授权,比较难理解,需要特别注意。其他的模块则根据你们的项目功能需求,有选择性的阅读即可。

    这里我就不多罗嗦了,大家看文档去吧!下面我会描述一些重点内容的实际操作情况以及代码,请确保你已经浏览过文档


    五、开发流程重点解析


    1.开发环境准备

    这里所谓的开发环境准备主要指的是我们项目服务端和微信服务端的网络通讯环境准备。

    我们平常开发可能只需要IP端口就能通讯,顶多配置下白名单放行,但微信公众号开发我们需要通过域名通讯(微信会访问我们配置的域名地址:服务器基本配置中的URL,下面会介绍),也就是我们各自开发环境需要拥有独立的域名,微信就能通过这个域名请求到我们的本地开发服务,各自进行开发测试。

    而我们一般都是内网开发,整个内网只有一个对外域名,所以这时就需要 内网穿透 ,为我们每个开发人员配置各自开发机器的域名。

    那如何进行内网穿透呢?你首先可以找下你们的网管,看他能不能帮你解决,如果不能,那就安装内网穿透工具,我们自己动手!

    我选择的内网穿透工具是natapp,这个有免费版、收费版,免费版的域名会随机变化,而收费版可以拥有固定域名,建议选择收费版,9元每月并不贵;大家可以对照natapp的文档安装使用,并不难。
    在这里插入图片描述
    这样我们本地开发环境就拥有自己的域名啦!然后就可以在测试号管理页面配置本地访问地址URL了。

    2.服务器基本配置

    无论是在真实公众号开发 - 基本配置 - 服务器配置,还是在 测试号管理 中,我们都可以看到这几个基本参数:
    开发者ID(AppID)、开发者密码(AppSecret)、服务器地址(URL)、令牌(Token)

    AppID 是公众号唯一开发识别码,配合开发者密码可调用公众号的接口能力,大多数微信接口都需要附带该参数。

    AppSecret 是校验公众号开发者身份的密码,具有极高的安全性。切记勿把密码直接交给第三方开发者或直接存储在代码中。如需第三方代开发公众号,请使用授权方式接入。其中获取accessToken就需要同时传入AppID和AppSecret获取。

    URL 是开发者用来接收微信消息和事件的接口URL,也就是我们服务后端的入口地址,需要注意的是该地址必须以域名形式填写,且必须以http 或 https 开头,分别支持80端口和443端口。如:http://yuanj.natapp1.cc/wechat。

    Token 可由开发者可以任意填写,用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性),也就是我们项目和微信服务端进行通信时,必须保证公众平台配置的Token和我们后台代码配置的Token保持一致,这样微信就能验证我们身份。

    注:EncodingAESKey 参数由开发者手动填写或随机生成,将用作消息体加解密密钥,我们前期可以采用明文模式进行开发测试,暂时先不用关注。
    在这里插入图片描述
    我们点击提交时,微信会以GET请求的方式访问我们配置的URL地址,并附加几个参数进行验证,所以你需要在该地址对应的项目后端接口里对这几个参数进行加工处理返回微信需要的结果,这样就可以验证成功,使微信服务端认可你配置的URL和Token参数,后续就能互相通信了!
    在这里插入图片描述
    具体情况可以阅读微信文档 - 开始前必读 - 接入指南

    这里附上该接口的Java代码:

    /**
     * 微信对接验证接口
     * */
    @RestController
    @RequestMapping(value = "/wechat")
    public class ValidateController {
        @Autowired
        WechatConfig wechatConfig;
    
       @RequestMapping(value = "", method = RequestMethod.GET)
       public void validate(HttpServletRequest req, HttpServletResponse resp) {
            System.out.println("-----开始校验签名-----");
    
            // 接收微信服务器发送请求时传递过来的参数
            String signature = req.getParameter("signature");
            String timestamp = req.getParameter("timestamp");
            String nonce = req.getParameter("nonce"); //随机数
            String echostr = req.getParameter("echostr");//随机字符串
    
            // 将token、timestamp、nonce三个参数进行字典序排序并拼接为一个字符串
            String TOKEN = wechatConfig.getToken();
            String sortStr = sort(TOKEN,timestamp,nonce);
            
            // 字符串进行shal加密
            String mySignature = WechatUtils.shal(sortStr);
            
            // 校验微信服务器传递过来的签名 和  加密后的字符串是否一致, 若一致则签名通过
            if(!"".equals(signature) && !"".equals(mySignature) && signature.equals(mySignature)){
                System.out.println("-----签名校验通过-----");
                try {
                    resp.getWriter().write(echostr);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }else {
                System.out.println("-----校验签名失败-----");
            }
        }
        
       /**
         * 参数排序
         * @param token
         * @param timestamp
         * @param nonce
         * @return
         */
        public static String sort(String token, String timestamp, String nonce) {
            String[] strArray = {token, timestamp, nonce};
            Arrays.sort(strArray);
            StringBuilder sb = new StringBuilder();
            for (String str : strArray) {
                sb.append(str);
            }
            return sb.toString();
        }
    }
    
    

    3.存取access_token参数

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

    access_token这个参数非常重要,几乎贯穿整个微信公关号项目开发,我们如何在有效期内定时刷新获取呢?
    如果我们的微信公众号项目是单服务架构,可以直接作为静态变量存储在内存里;如果是多服务,可以用中间件存储Redis、数据库都可以。SpringBoot项目内部可以通过@Scheduled注解,执行定时任务,既然access_token有效期是2小时,那我们可以一小时刷新获取一次,将其存入Redis,覆盖之前的access_token。


    4.公众号消息管理

    在这里插入图片描述
    很多公众号都可以通过消息发送来与其进行交互,那这样的功能如何代码实现呢?

    具体我们可以在微信文档 - 消息管理 模块查阅:
    在这里插入图片描述
    在此我要提到的一点就 微信公众号的消息交互都是通过XML格式进行的!这点就很坑了。。现在我们前后端、服务端的消息传输基本都是Json格式了,也习惯了Json格式的解析处理,所以遇到XMl格式的处理又要多费些事了。

    为什么微信采用XML格式呢?我个人猜测是几年前还是XML格式的天下,当时Json还没有这么流行,腾讯毕竟是产品业务驱动的,当然选择当时开发人员最熟悉的XML格式了开发,后面随着微信平台的普及,用户越来越多,想重构改成Json格式估计也十分困难,所以历史就遗留下来了呗。。

    我在此推荐一个GitHub上一个微信开发 Java SDK,里面有整个微信开发平台很多功能模块造好的轮子,我们可以参考下直接使用:
    https://github.com/Wechat-Group/WxJava
    在这里插入图片描述
    比如现在对于XMl消息解析这个需求,上面就提供了完整详尽的代码。


    5.获取openid以及网页授权(重难点)

    注意,这是公众号开发的重难点之一,请把技术文档中的微信网页授权模块多读两遍,然后带着疑问来看我的解析。

    (1)先明确为什么需要网页授权?我们的目的是什么?

    答:用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。也就是通过这种授权机制,我们能获取微信用户信息,比如:头像、昵称、地区、个性签名等。

    (2)既然目的是获取用户基本信息,微信不是提供了专门的接口吗?非要网页授权?

    答:在文档的 用户管理 - 获取用户基本信息(UnionID机制) 模块可以看到的确有获取用户基本信息接口:
    在这里插入图片描述
    可以看到,这个接口只需要提供openid或者unionid,即可直接获取用户基本信息。那么问题来了,openid(unionid)又是如何获取呢?

    微信平台提供了两种方式获取用户的openid

    第一种方式:

    用户与公众号产生消息交互时,会以POST请求的方式向我们配置的服务器URL地址发送XML格式的消息,并附带该用户对应公众号的openid!关于什么是消息交互我们可以查看文档中的消息管理模块,比如我们在公众号输入栏中发送文字图片语音等属于普通消息交互,我们关注、取关、点击自定义菜单等属于事件消息交互,每当前端用户进行这个操作时,微信服务端都会向我们项目后台发送POST请求给我们传达信息:
    在这里插入图片描述
    可以看到,这个推送数据包中就包含了用户的消息交互类型、时间以及我们需要的openid!也就是说,无论用户在公众号里干了啥操作,我们都能知道他这个操作干了啥,以及他是谁(openid),这时就能调用 用户管理 - 获取用户基本信息(UnionID机制) 接口获取用户基本信息了。

    别高兴太早,这种通过消息交互获取用户信息的方式,用户占主动地位,我们项目后端服务被动接受,那么如果我有个基本需求:我想在自定义菜单 - 对应我们网站的前端页面上展示微信用户基本信息,能做到吗?你如何把后台接收到的消息和前端用户关联绑定?
    可见,这种被动的方式并不能实现该功能,我们需要主动出击,在前端就能获取到当前操作用户的openid!

    第二种方式:

    这种方式就是通过网页授权机制主动出击!详情见下文。

    (3)网页授权有哪几种机制?分别是怎样实现?应用于什么场景?

    答:主要有两种机制,对应两种scope:

    snsapi_base为scope发起的网页授权,是用来获取进入页面的用户的openid的,并且是静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务页面)。

    snsapi_userinfo为scope发起的网页授权,是用来获取用户基本信息的。但这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息。

    光看这两句解释你可能有一堆疑问,我们逐一分析:

    两种机制的前面授权步骤相同,大概如下:

    我们先要按照文档要求构造一个链接https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
    其中重点参数是redirect_uri,这个参数填的既可以是前端项目url,也可以是后端接口url,然后点击这个链接后,微信服务端经过重定向到我们填写的redirect_uri,会在此redirect_uri后拼接上一个code参数!然后前端或者后端通过code参数就可以调微信接口https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code获取openid等信息了:
    在这里插入图片描述
    这里讲下 snsapi_basesnsapi_userinfo不同点

    首先snsapi_base静默授权,什么意思呢?就是用户没有感知;与之对应的就是非静默授权snsapi_userinfo了,这个scope公众号会弹出一个小窗口需要用户手动点击授权,类似这种:
    在这里插入图片描述
    那么这两种scope授权的优劣势在哪呢?

    snsapi_base 的优势在于用户无感知,体验好,方便快捷;劣势在于获取openid后只能通过用户管理 - 获取用户基本信息(UnionID机制) 接口获取用户基本信息,而这种方式需要确保用户已经关注,不然是没有相关信息的!
    snsapi_userinfo 的优势在于无需用户关注公众号,只要用户点击了授权确认,即可通过access_token和openid调用专门的拉去用户信息接口获取信息,比较暴力。。;劣势在于需要用户手动授权,可能影响用户体验
    在这里插入图片描述

    在此说下,我们项目是通过snsapi_base静默授权的,其中redirect_uri配置的是前端项目首页地址(前后端分离),并将构造的这个链接封装起来,直接配置在自定义菜单里,那么用户点击菜单,就直接重定向到前端项目,然后前端获取code参数调用后端获取openid接口,将获取的openid缓存到客户端,以便后面使用。

    (4)想要进行网页授权,我们需要在公众平台配置什么吗?

    答:需要!
    如果是测试号,需要在 测试号管理 - 体验接口权限表 - 网页服务 - 网页帐号 点击 修改
    在这里插入图片描述
    在这里插入图片描述
    在这里配置的是回调页面redirect_uri的域名

    如果是正式号(需要微信认证),需要在 开发 - 接口权限 - 网页服务 - 网页帐号 - 网页授权获取用户基本信息 的配置选项中,修改授权回调域名。请注意,这里填写的是域名(是一个字符串),而不是URL,因此请勿加 http:// 等协议头;

    而且正式号其他配置的地方也和测试号不一样,比如多了IP白名单、域名根路径下的txt验证文件,这个稍微摸索下应该没啥问题的。


    over 暂时就回忆这么多了。。。可能有遗漏大家可以提出哈 ~ 下一篇博客写几个开发时的小问题补充下吧

    展开全文
  • 微信公众号开发(一)

    2019-04-28 00:22:41
    开发前首先我们要知道一些概念 各公众号区别: 1、订阅号:为媒体和个人提供一种信息传播方式,主要偏于为用户传达资讯(类似报纸杂志),主要的定位是阅读,每天可以群发1条消息; 2、服务号:为企业,政府或组织...

    开发前首先我们要知道一些概念

    各公众号区别:
    1、订阅号:为媒体和个人提供一种信息传播方式,主要偏于为用户传达资讯(类似报纸杂志),主要的定位是阅读,每天可以群发1条消息;
    2、服务号:为企业,政府或组织提供对用户进行服务,主要偏于服务交互(类似银行提供服务查询),每个月只可群发4条消息;
    3、企业号:为企业,政府,事业单位,实现生产管理和协作运营的移动化,主要用于公司内部通讯使用,旨在为用户提供移动办公,需要先有成员的通讯信息验证才可以关注成功企业号;

    img_191e68910b6bd3180e4d2a122c54af5c.png

    温馨提示:
    1、如果想简单的发送消息,达到宣传效果,建议选择订阅号;
    2、如果想进行商品销售,进行商品售卖,为用户提供服务,建议申请服务号;
    3、如果想用来管理内部企业员工、团队,对内使用,可选择申请企业号。

    编辑模式:主要针对非编程人员及信息发布类公众帐号使用。
    开启该模式后,可以方便地通过界面配置“自定义菜单”(认证的订阅号、服务号)和“自动回复的消息”。
    好处是可视化界面配置,操作简单,快捷,但是功能有限。
    由于编辑模式功能有限我们就不做多讲我们把重点方法开发模式上
    首先我们自己要先申请一个公众号对立面的各个功能要熟悉然后再看开发模式

    img_15e06f3c0b8767ad1a72e0b60fa03c18.png

    开发模式:主要针对具备开发能力的人使用。开启该模式后,能够使用微信公众平台开放的接口,
    通过编程方式实现自定义菜单的创建、删除、用户消息的交互,
    这种模式更加灵活,能实现更多复杂的功能,提供个性化服务。

    微信公众平台是运营者通过公众号为微信用户提供资讯和服务的平台,而公众平台开发接口则是提供服务的基础,
    开发者在公众平台网站中创建公众号、获取接口权限后,可以通过阅读本接口文档来帮助开发。
    文档地址:http://mp.weixin.qq.com/wiki/home/
    要站在自己网站的角度看文档。

    注册公众号

    img_11b3dd14e369941ca882e8b8f74b8169.png

    我们这里只对订阅号做探讨其实和服务号开发是一样的

    开发模式

    微信公众平台开发模式交互原理

    img_2b3b6d132b7552dc58490bf045f3df66.jpe

    这里我们需要服务器,服务器有阿里云,bae,花生壳,nat123,natapp由于价格原因阿里云,bae价格都比较比较昂贵我们现在做测试就选择natapp内网穿透https://natapp.cn/
    先下载客户端

    img_99381298ac248c86d142aa648d328b4e.png

    在官网上购买隧道5块钱的要以.top为后缀的

    img_8c23fd667e395ad789c1211881e5b6a6.png

    再购买一个二级域名5块钱

    img_365a685f20b16140c7e0391f7a1aa25d.png

    安装natapp

    img_72eae03b52436c55eb15a1fd255bb4b8.png
    img_2ce806baeca0f2f673089f2a39bea8cb.png

    根据教程安装好natapp

    安装成功的标识 把地址拷贝下来http://zhangshuai.nat100.top

    img_1cb23c275636e0e43ef9a2a66112ab80.png

    这个时候就可以通过域名在任何地方访问我们的应用了

    微信公众号URL接入

    进入公众平台测试帐号

    img_b06fa82bd231d22b126f9440241717fe.png

    img_c3717d44449942aecdd69b515a40843f.png

    可以查看文档接入指南(https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319)也可以跟我一起来(我们这里Tomcat服务器的端口是8080有些人的端口是80根据自己的情况)

    img_bb62b344b38391f5ed16ded2a23d6fee.png
    img_f381f63bbe9a18f76ec31794c296b051.png

    首先我们先创建一个Maven项目

    img_643a8e73c3ebe531fef60e7ba8292ef9.png

    先配置好项目
    web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         id="WebApp_ID" version="3.0">
      <display-name>Archetype Created Web Application</display-name>
    <servlet>
    <servlet-name>springMVC</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:application-mvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
    <servlet-name>springMVC</servlet-name>
    <url-pattern>*.do</url-pattern>
    </servlet-mapping>
    <!--配置字符编码过滤器-->
     <filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
    </filter>
    <filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>*.do</url-pattern>
    </filter-mapping>
    </web-app>
    

    application-mvc.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:jee="http://www.springframework.org/schema/jee"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
            http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!-- 0.开启注解扫描  -->
    <context:component-scan base-package="com.jd.wx"/>
    <!-- 1.注解驱动支持 -->
    <mvc:annotation-driven/>
    <!-- 2.配置静态资源处理 -->
    <mvc:default-servlet-handler/>
    <!-- 3.视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
    </beans>
    

    现在我们先创建一个Controller测试一下

    img_6e234f8b322373f01b3b3d8163e74483.png
    @Controller
    public class WeixinController {
    //验证签名
    /**
     * @param signature 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
     * @param timestamp 时间戳
     * @param nonce 随机数
     * @param echostr 随机字符串
     * @return
     */
    @RequestMapping("/weixin")
    public String checkSignature(String signature,String timestamp,String nonce,String echostr){
        System.out.println(signature);
        System.out.println(timestamp);
        System.out.println(nonce);
        System.out.println(echostr);
        return null;
    }
    }
    

    打印结果

    img_0f227a88fff4800d25bddb33a2278949.png

    开发者通过检验signature对请求进行校验(下面有校验方式)。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。加密/校验流程如下:
    1)将token、timestamp、nonce三个参数进行字典序排序
    2)将三个参数字符串拼接成一个字符串进行sha1加密
    3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

      //加密/校验流程如下:
        String[] arr = {WeixinUtil.TOKEN,timestamp,nonce};
        //1.将token、timestamp、nonce三个参数进行字典序排序
        Arrays.sort(arr);
        String str = "";
        //2.将三个参数字符串拼接成一个字符串进行sha1加密
        for (String temp : arr) {
            str += temp;
        }
        //3.开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
        if (signature.equals(SecurityUtil.SHA1(str))) {
            System.out.println("接入成功!");
            return echostr;
        }
        System.out.println("接入失败!");
        return null;
    

    此时我们需要工具类

    img_0d9cf289fc1c2f4af3ef56081d13ece7.png

    和SecurityUtil

    package com.jd.wx.util;
    import java.security.MessageDigest;
    public class SecurityUtil {
    private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5',
            '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
    /**
     * encode string
     * @param algorithm
     * @param str
     * @return String
     */
    public static String encode(String algorithm, String str) {
        if (str == null) {
            return null;
        }
        try {
            MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
            messageDigest.update(str.getBytes());
            return getFormattedText(messageDigest.digest());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * Takes the raw bytes from the digest and formats them correct.
     * @param bytes
     *  the raw bytes from the digest.
     * @return the formatted bytes.
     */
    private static String getFormattedText(byte[] bytes) {
        int len = bytes.length;
        StringBuilder buf = new StringBuilder(len * 2);
        // 把密文转换成十六进制的字符串形式
        for (int j = 0; j < len; j++) {
            buf.append(HEX_DIGITS[(bytes[j] >>> 4) & 0x0f]);
            buf.append(HEX_DIGITS[bytes[j] & 0x0f]);
        }
        return buf.toString();
    }
    
    public static String MD5(String content){
        return SecurityUtil.encode("MD5", content);
    }
    
    public static String SHA1(String content){
        return SecurityUtil.encode("SHA1", content);
    }
    
    public static void main(String[] args) {
        System.out.println("111111 MD5  :"
                + SecurityUtil.encode("MD5", "111111"));
        System.out.println("111111 SHA1 :"
                + SecurityUtil.encode("SHA1", "111111"));
    }
    }
    

    配置成功

    img_1c3161c8e0b141847e63647874439e92.png

    现在有一个问题是当用户给公众号发送一个消息,消息会推送到我们给的地址上面 我们怎么知道什么时候是验证什么时候是消息推送怎么区分呢?
    其实很简单 开发者通过检验signature对请求进行校验若确认此次GET请求来自微信服务器,如果是消息接收他会发送一个POST请求
    当普通微信用户向公众账号发消息时,微信服务器将POST消息的XML数据包到开发者填写的URL上。

     @RequestMapping(value="/weixin",method= RequestMethod.GET)
     @RequestMapping(value="/weixin",method= RequestMethod.POST)
    

    此时他会发送一个xml数据过来我们需要jaxb(自己百度一下)工具来解析xml


    img_dc9421dbd3e8fd6f79fa2e18f157bc56.png
    img_6313fc9c09c2af84c0a77a39a7219e66.png
    @Getter@Setter@ToString
    @XmlRootElement(name = "xml")
    @XmlAccessorType(XmlAccessType.FIELD)
    public class XmlMessageEntity {
    private String ToUserName;
    private String FromUserName;
    private Long CreateTime;
    private String MsgType;
    private String Content;
    private String Event;
    private Long MsgId;
    }
    

    这个时候我们就可以在Controller里处理用户发送到公众号上的消息进行回复了

    img_7378e43178279abb84d3bf9ed7fd9da4.png
    @RequestMapping(value="/weixin",method=RequestMethod.POST)
    @ResponseBody
    public XmlMessageEntity handlerMessage(@RequestBody XmlMessageEntity entity){
        System.out.println(entity);
        XmlMessageEntity newEntity = new XmlMessageEntity();
        newEntity.setFromUserName(entity.getToUserName()); //设置发送方
        newEntity.setToUserName(entity.getFromUserName());  //设置接收方
        newEntity.setCreateTime(new Date().getTime()); //设置发送时间
        //如果是第一次关注,回复“欢迎关注!”
        if("event".equals(entity.getMsgType())){
            //如果是关注事件
            if("subscribe".equals(entity.getEvent())){
                //调用接口获取用户详细信息
                String reuslt = HttpUtil.get(WeixinUtil.GET_USERINFO_URL.replace("ACCESS_TOKEN", WeixinUtil.getAccessToken())
                        .replace("OPENID", entity.getFromUserName()));
                System.out.println(WeixinUtil.GET_USERINFO_URL.replace("ACCESS_TOKEN", WeixinUtil.getAccessToken())
                        .replace("OPENID", entity.getFromUserName()));
                System.out.println(reuslt);
                //创建客户信息,保存到数据库
                //回复内容
                newEntity.setContent("欢迎关注!");
            }else if ("unsubscribe".equals(entity.getEvent())) {
                //更新客户状态,设置为取消关注
            }
        }
        //如果发送的是你好,回复“很高兴认识你”
        if(entity.getContent().contains("你好")){
            newEntity.setContent("很高兴认识你");
        }else{
            //否则就统一回复“帅哥好”
            newEntity.setContent("帅哥好!");
        }
        //发送类型
        newEntity.setMsgType("text");
        return newEntity;
    
    }
    

    现在我通过手机扫二维码进行测试(也可以通过接口调试工具https://mp.weixin.qq.com/debug/cgi-bin/apiinfo?t=index)

    img_dce5c62e2383eaf1e6191638caaba233.png

    测试成功


    img_0e60702587bb02407f3e1a8f6b7c0a8f.png

    获取用户基本信息(包括UnionID机制)

    开发者可通过OpenID来获取用户基本信息。请使用https协议。
    接口调用请求说明
    http请求方式: GET https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN

    img_2e700650e6f66957d49a291469b2e041.png

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

    img_a301208ec8977b2f8314511470809836.png

    首先我们先创建一个网络请求的工具类HttpUtil

    /**
    * Http工具类
    */
    public class HttpUtil {
    
    /**
     * 发送get请求
     * @throws Exception
     */
    public static String get(String url) {
    
        String result = "";
        InputStream in = null;
        try {
            // 打开和URL之间的连接
            HttpURLConnection conn = (HttpURLConnection) new URL(url)
                    .openConnection();
            // 设置通用的请求属性
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("Content-Type", "application/json");
            conn.setRequestProperty("Accept", "application/json");
            conn.setRequestMethod("GET");
            // 建立实际的连接
            conn.connect();
            // 定义输入流来读取URL的响应
            in = conn.getInputStream();
            result = StreamUtils.copyToString(in, Charset.forName("utf-8"));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }
    
    /**
     * 发送post请求
     *
     * @throws Exception
     */
    public static String post(String url, String paramStr) {
        InputStream in = null;
        OutputStream os = null;
        String result = "";
        try {
            // 打开和URL之间的连接
            HttpURLConnection conn = (HttpURLConnection) new URL(url)
                    .openConnection();
            // 设置通用的请求属性
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            // 发送POST请求须设置
            conn.setRequestMethod("POST");
            conn.setDoOutput(true);
            conn.setDoInput(true);
            os = conn.getOutputStream();
            // 注意编码格式,防止中文乱码
            if (StringUtils.hasText(paramStr)) {
                os.write(paramStr.getBytes("utf-8"));
                os.close();
            }
    
            in = conn.getInputStream();
            result = StreamUtils.copyToString(in, Charset.forName("utf-8"));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (os != null) {
                    os.close();
                }
                if (in != null) {
                    in.close();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return result;
    }
    }
    

    接着我们想获取AccessToken 通过HttpUtil发送GET请求 "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET"我们把响应回来的数据转成json对象然后回去对象的access_token

    public static String accessToken;
    //accessToken的失效时间
    public static Long expiresTime = 0L;
    
    /**
     * 获取AccessToken
     * @return
     */
    public static String getAccessToken() {
    //如果accessToken为null或者accessToken已经失效就去重新获取(提前10秒)
    if(new Date().getTime()>= expiresTime){
        //发送http请求
        String result = HttpUtil.get(GET_ACCESSTOKEN_URL.replace("APPID", APPID).replace("APPSECRET", SECRET));
        //转成json对象
        JSONObject json = JSON.parseObject(result);
        accessToken = (String) json.get("access_token");
        Integer expires_in = (Integer) json.get("expires_in");
        //失效时间=当前时间+7200
        expiresTime = new Date().getTime()+((expires_in-10)*1000);
    }
    return accessToken;
    }
    
    img_905d8827bada974853edd5d4ee92001f.png
    public class WeixinUtil {
    public static final String TOKEN = "zhangshuai";
    public static final String APPID = "wxa379f9e383fb5ad7";
    public static final String SECRET = "ce2d71a98230f1d5d6cb35c7e5e90573";
    //获取基础ACCESSTOKEN的URL
    public static final String GET_ACCESSTOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
    
    public static String accessToken;
    //accessToken的失效时间
    public static Long expiresTime = 0L;
    
    /**
     * 获取AccessToken
     * @return
     */
    public static String getAccessToken() {
        //如果accessToken为null或者accessToken已经失效就去重新获取(提前10秒)
        if(new Date().getTime()>= expiresTime){
            //发送http请求
            String result = HttpUtil.get(GET_ACCESSTOKEN_URL.replace("APPID", APPID).replace("APPSECRET", SECRET));
            //转成json对象
            JSONObject json = JSON.parseObject(result);
            accessToken = (String) json.get("access_token");
            Integer expires_in = (Integer) json.get("expires_in");
            //失效时间=当前时间+7200
            expiresTime = new Date().getTime()+((expires_in-10)*1000);
        }
        return accessToken;
    }
    

    接着我们来获取用户信息

    //获取用户信息的URL(需要关注公众号)
    public static final String GET_USERINFO_URL = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN ";
    
    img_d05cb8a369085b7e5f76ca1c644d2d91.png
    //调用接口获取用户详细信息
    String reuslt = HttpUtil.get(WeixinUtil.GET_USERINFO_URL.replace("ACCESS_TOKEN", WeixinUtil.getAccessToken())
     .replace("OPENID", entity.getFromUserName()));
    System.out.println(WeixinUtil.GET_USERINFO_URL.replace("ACCESS_TOKEN", WeixinUtil.getAccessToken())
     .replace("OPENID", entity.getFromUserName()));
    System.out.println(reuslt);
    

    这时我们重新关注查看日志是否打印用户信息

    img_ddad3f86a3c1075d72275851c09cb455.png

    自定义菜单创建接口

    1、自定义菜单最多包括3个一级菜单,每个一级菜单最多包含5个二级菜单。
    2、一级菜单最多4个汉字,二级菜单最多7个汉字,多出来的部分将会以“...”代替。
    3、创建自定义菜单后,菜单的刷新策略是,在用户进入公众号会话页或公众号profile页时,如果发现上一次拉取菜单的请求在5分钟以前,就会拉取一下菜单,如果菜单有更新,就会刷新客户端的菜单。测试时可以尝试取消关注公众账号后再次关注,则可以看到创建后的效果。

    http请求方式:POST(请使用https协议) https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN

    img_79b7c9a745918db5b30995e4be8623b1.png

    先根据文档发送GET请求

    img_7d95494fd6ad73aaa6d3fac34cd783f9.png
    //创建自定义菜单的URL
    public static final String CREATEMENU_URL = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";
    

    /**
     *创建菜单
     * @return
     */
    public static void createMenu() {
        String result = HttpUtil.post(CREATEMENU_URL.replace("ACCESS_TOKEN", getAccessToken()),
                "{\"button\":[{ \"type\":\"click\",\"name\":\"今日歌曲\",\"key\":\"V1001_TODAY_MUSIC\"},{\"name\":\"菜单\",\"sub_button\":[{  \"type\":\"view\",\"name\":\"百度官网\",\"url\":\"https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx2cd3085e1b3695d9&redirect_uri=http://chen.nat300.top/index.do&response_type=code&scope=snsapi_userinfo#wechat_redirect\"},{\"type\":\"click\",\"name\":\"赞一下我们\",\"key\":\"V1001_GOOD\"}]}]}");
        System.out.println(result);
    }
    
    img_bb5c44e18f78896a99db4701bcc4f16e.png

    模板消息

    发送模板消息调用接口
    http请求方式: POST
    https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN

    img_2ed26de1e4f557edc723e1978328d7cf.png
    //发送模板消息的URL
    public static final String SEND_TEMPLATE_URL = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN";
    

    /**
     * 发送模板消息
     */
    public static void sendTemplate() {
        String result = HttpUtil.post(SEND_TEMPLATE_URL.replace("ACCESS_TOKEN",getAccessToken()),"{\"touser\":\"ogGJv0zE12eFlOGQuCALxtD3vT7E\",\"template_id\":\"flx-OcF_I8Uj6lj3kRLA4_hN79xVCY4OZ006JQLreVU\",\"url\":\"http://www.baidu.com\",\"data\":{\"first\":{\"value\":\"恭喜你购买成功!\",\"color\":\"#173177\"},\"keyword1\":{\"value\":\"巧克力\",\"color\":\"#173177\"},\"keyword3\":{\"value\":\"39.8元\",\"color\":\"#173177\"},\"keyword2\":{\"value\":\"2017年9月04日\",\"color\":\"#173177\"},\"remark\":{\"value\":\"欢迎再次购买!\",\"color\":\"#173177\"}}}");
        System.out.println(result);
    }
    
    img_5915b80c40fdd8ea3505431ec5293428.png

    每个模板的id不固定

    img_5754dcdc11a2966e2c7f03f600630e21.png

    img_a9590f5bc94662ecba8fec5a48ac1901.png

    微信网页授权

    关于网页授权回调域名的说明

    1、在微信公众号请求用户网页授权之前,开发者需要先到公众平台官网中的“开发 - 接口权限 - 网页服务 - 网页帐号 - 网页授权获取用户基本信息”的配置选项中,修改授权回调域名。请注意,这里填写的是域名(是一个字符串),而不是URL,因此请勿加 http:// 等协议头;
    2、授权回调域名配置规范为全域名,比如需要网页授权的域名为:www.qq.com,配置以后此域名下面的页面http://www.qq.com/music.htmlhttp://www.qq.com/login.html 都可以进行OAuth2.0鉴权。但http://pay.qq.comhttp://music.qq.comhttp://qq.com无法进行OAuth2.0鉴权
    3、如果公众号登录授权给了第三方开发者来进行管理,则不必做任何设置,由第三方代替公众号实现网页授权即可

    关于网页授权的两种scope的区别说明

    1、以snsapi_base为scope发起的网页授权,是用来获取进入页面的用户的openid的,并且是静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务页面)
    2、以snsapi_userinfo为scope发起的网页授权,是用来获取用户的基本信息的。但这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息。
    3、用户管理类接口中的“获取用户基本信息接口”,是在用户和公众号产生消息交互或关注后事件推送后,才能根据用户OpenID来获取用户基本信息。这个接口,包括其他微信接口,都是需要该用户(即openid)关注了公众号后,才能调用成功的。

    关于网页授权access_token和普通access_token的区别

    1、微信网页授权是通过OAuth2.0机制实现的,在用户授权给公众号后,公众号可以获取到一个网页授权特有的接口调用凭证(网页授权access_token),通过网页授权access_token可以进行授权后接口调用,如获取用户基本信息;
    2、其他微信接口,需要通过基础支持中的“获取access_token”接口来获取到的普通access_token调用。

    关于UnionID机制

    1、请注意,网页授权获取用户基本信息也遵循UnionID机制。即如果开发者有在多个公众号,或在公众号、移动应用之间统一用户帐号的需求,需要前往微信开放平台(open.weixin.qq.com)绑定公众号后,才可利用UnionID机制来满足上述需求。
    2、UnionID机制的作用说明:如果开发者拥有多个移动应用、网站应用和公众帐号,可通过获取用户基本信息中的unionid来区分用户的唯一性,因为同一用户,对同一个微信开放平台下的不同应用(移动应用、网站应用和公众帐号),unionid是相同的。

    下面我们来进行网络授权

    1 第一步:用户同意授权,获取code
    2 第二步:通过code换取网页授权access_token
    3 第三步:刷新access_token(如果需要)
    4 第四步:拉取用户信息(需scope为 snsapi_userinfo)

    修改网页账号

    如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。

    img_d92de38c8680070be159db484dba3a27.png
    img_6c9139c1cb3009f5043b0eaa6c15e844.png
    img_f2c5ca4b965c5c77d66f6a2843640b5d.png

    https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

    img_2a5a10d75ba3277aa825dac87e6a8df2.png
    img_4a717a4c79991e88bb0ad453a354c702.png

    请求方法
    获取code后,请求以下链接获取access_token: https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

    img_4c6c54e8fff38cda133abb2610c53a69.png
    //获取网页版的ACCESSTOKEN的URL
    public static final String GET_WEB_ACCESSTOKEN_URL="https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
    //通过网页获取用户信息的URL(不需要关注公众号)
    public static final String GET_WEB_USERINFO_URL = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";
    
      @Controller
    public class IndexController {
    @RequestMapping("/index")
    public String index(String code){
        System.out.println(code);
        //获取ACCESSTOKEN
        String result = HttpUtil.get(WeixinUtil.GET_WEB_ACCESSTOKEN_URL.replace("APPID", WeixinUtil.APPID)
                .replace("SECRET", WeixinUtil.SECRET).replace("CODE", code));
        System.out.println(result);
        JSONObject json = JSON.parseObject(result);
        String access_token = json.getString("access_token");
        String openid = json.getString("openid");
        //获取用户信息
        String userinfo = HttpUtil.get(WeixinUtil.GET_WEB_USERINFO_URL.replace("ACCESS_TOKEN", access_token).replace("OPENID", openid));
        System.out.println(userinfo);
        //把openid放到session
        //.....
        return "index";
    }
    }
    

    微信JS-SDK

    img_9d863fdd464a55080af2196eb20424e7.png

    第一步:绑定域名
    img_326025928da13c2692bc8c35197f49d3.png

    第二步引入JSP文件

    img_85ecb7ac3385ee30cf56d449de3b7366.png

    index.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
    <script type="text/javascript">
        //通过config接口注入权限验证配置
        wx.config({
            debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
            appId: 'wxa379f9e383fb5ad7', // 必填,公众号的唯一标识
            timestamp: '666666', // 必填,生成签名的时间戳
            nonceStr: 'zhangshuai', // 必填,生成签名的随机串
            signature: 'c7f8ec79e45118a512820345cba07abac0caa0ca',// 必填,签名,见附录1
            jsApiList: ['onMenuShareTimeline', 'hideAllNonBaseMenuItem'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
        });
        wx.ready(function () {
            // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
            wx.onMenuShareTimeline({
                title: '新的标题', // 分享标题
                desc: '新的描述', // 分享描述
                link: 'http://zhangshuai.nat100.top/index.jsp', // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
                imgUrl: 'http://zhangshuai.nat100.top/1.jpg', // 分享图标
                success: function () {
                    window.location.href = "http://www.baidu.com";
                    // 用户确认分享后执行的回调函数\
                },
                cancel: function () {
                    // 用户取消分享后执行的回调函数
                    alert("取消分享!")
                }
            });
            //隐藏非基础按钮
            wx.hideAllNonBaseMenuItem();
        });
    </script>
    <title>免费烧饼</title>
    </head>
    <body>
    烧饼店
    </body>
    </html>
    

    第三步:通过config接口注入权限验证配置

      //通过config接口注入权限验证配置
        wx.config({
            debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
            appId: 'wxa379f9e383fb5ad7', // 必填,公众号的唯一标识
            timestamp: '666666', // 必填,生成签名的时间戳
            nonceStr: 'zhangshuai', // 必填,生成签名的随机串
            signature: 'c7f8ec79e45118a512820345cba07abac0caa0ca',// 必填,签名,见附录1
            jsApiList: ['onMenuShareTimeline', 'hideAllNonBaseMenuItem'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
        });
    

    在这里值得注意的是signature生成签名要按步骤

    签名说明
    1.将 api_ticket、timestamp、card_id、code、openid、nonce_str的value值进行字符串的字典序排序。
    2.将所有参数字符串拼接成一个字符串进行sha1加密,得到signature。
    3.signature中的timestamp,nonce字段和card_ext中的timestamp,nonce_str字段必须保持一致。
    4.code=1434008071,timestamp=1404896688,card_id=pjZ8Yt1XGILfi-FUsewpnnolGgZk, api_ticket=ojZ8YtyVyr30HheH3CM73y7h4jJE ,nonce_str=123 则signature=sha1(12314048966881434008071ojZ8YtyVyr30HheH3CM73y7h4jJEpjZ8Yt1XGILfi-FUsewpnnolGgZk)=f137ab68b7f8112d20ee528ab6074564e2796250。
    强烈建议开发者使用卡券资料包中的签名工具SDK进行签名或使用debug工具进行校验:http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=cardsign

    img_ef5f5a0cccd280b360a505793a0ccfc3.png
    img_959768c776209e92c048a518962938ab.png

    WeixinUtil
    Dubug模式获取ticket票据

    //获取jssdk使用的ticket
    public static final String GET_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";
    

    public static void main(String[] args) {
        //通过access_token获取ticket
        String temp = HttpUtil.get(WeixinUtil.GET_TICKET_URL.replace("ACCESS_TOKEN", getAccessToken()));
        System.out.println(temp);
    }
    

    将控制台输出的票据拷贝

    img_c10acd546e943c7a80d7e4df355a5156.png

    确认签名算法正确,可用 http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign 页面工具进行校验。
    img_7b31745284c3d6aaf5bfcfcf809c9a62.png

    这时将生成的signature签名拷贝到
    img_85ecb7ac3385ee30cf56d449de3b7366.png

    展开全文
  • 微信公众号开发详细教程

    千次阅读 2019-05-24 17:53:17
    微信公众号开发之配置开发服务器 微信公众号开发之获取access_token 微信公众号开发之关键词回复 微信公众号开发之模板消息 微信公众号开发之授权回调 ...
    展开全文
  • 微信公众号开发——项目搭建

    万次阅读 2018-09-11 15:23:25
    前往微信公众平台(https://mp.weixin.qq.com/)获取开发权限和开发账号,公众号分为好几种,小程序,订阅号,服务号,企业号,个人只能用订阅号,权限比较少(api接口权限); 二 想要自定义模块功能或者是回复...

    一 

    前往微信公众平台(https://mp.weixin.qq.com/)获取开发权限和开发账号,公众号分为好几种,小程序,订阅号,服务号,企业号,个人只能用订阅号,权限比较少(api接口权限);

    想要自定义模块功能或者是回复信息啥的,就得有一个自己的后台来写接口,所以要配置自己的服务器

    基本配置:服务器配置(首页-开发-基本配置-服务器配置)

    进入修改配置,这里的服务器地址必须是外网(没有服务器的小伙伴如果这里分不清。。外网特征就是别人可以通过你的本机ip直接访问到,在简单点就是,你如果使用WiFi连接的网络那就肯定是内网或者说是局域网,查看方式,window键加R键,输入cmd回车,然后输入ipconfig 查看你当前的ip,然后百度本机ip,不一样的话就不是外网,解决办法是使用natapp进行内网穿透或者是用路由器进行端口映射,将服务器的某个端口映射出去,在最后会有详细步骤)而且是80端口,格式为ip/域名+wx(接口名 ,固定,但是前面可以加路由比如ip/home/wx)Token随便写个;

    然后是后台不多说先上代码:

    /// <summary>
    /// 配合微信服务器验证域名或者是ip的可用行或者是获取消息的推送--pxj
    /// </summary>
    [AtSkipAuthorize]
    public void wx()
    {
    	//获取消息推送,或者是事件推送,类型是xml文件流
    	if (Request.RequestType == "POST")
    	{
    
    	}
    	else
    	{
    		#region 验证请求来源是否是微信
    		string signature = Request["signature"].ToString();
    		string timestamp = Request["timestamp"].ToString();
    
    		string nonce = Request["nonce"].ToString();
    		string echostr = Request["echostr"].ToString();
    		string token = "hellowb";
    		List<string> list = new List<string>() { token, timestamp, nonce };
    		list.Sort();
    		string data = string.Join("", list);
    		byte[] temp1 = Encoding.UTF8.GetBytes(data);
    		SHA1CryptoServiceProvider sha = new SHA1CryptoServiceProvider();
    		byte[] temp2 = sha.ComputeHash(temp1);
    
    		var hashCode = BitConverter.ToString(temp2);
    		hashCode = hashCode.Replace("-", "").ToLower();
    
    		if (hashCode == signature)
    		{
    			Response.Write(echostr);
    			Response.End();
    		}
    		LogHelper.WriteLog("signature:" + signature + " timestamp:" + timestamp + " nonce:" + nonce + " echostr:" + echostr);
    		#endregion
    	}
    }

    写好后台配置好服务器就可以在微信公众号网站也就是刚才的那个页面点击提交了,点击提交后微信的服务器会向你所配置的服务器80端口的这个接口发送GET请求,验证接口是否正确,如果提交成功,恭喜你可以进入下一个坑了;

    当你在写微信公众号的页面的时候,你有时候会用到上传图片或者打开摄像机等要求,这时候要接入JS_SDK,配置签名等等,这个签名应该算是配置里最难受的一块了。。。

    签名的获取有几个步骤,先是要通过appid和appSecret来获取access_token,然后用access_token来获取jsapi_ticket,代码如下

     //获取access_token
    string url = string.Format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}", appId, appSecret);
    var wc = new WebClient();
    string access_tokenRet = wc.DownloadString(url);
    string access_token = JsonHelper.JsonToObj<AccessTokenModel>(access_tokenRet).Access_Token;
    accesstokenstatic = access_token;
    
    //获取jsapi_ticket
    string Jsapi_TicketUrl = string.Format("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={0}&type=jsapi", access_token);
    var jsapiRet = wc.DownloadString(Jsapi_TicketUrl);
    string ticket = JsonHelper.JsonToObj<TickModel>(jsapiRet).ticket;
    ticketstatic = ticket;

    然后获取十位的时间戳和自己写的随机字符串和页面传来的url,将jsapi_ticket,url,timestamp(时间戳),nocestr(随机字符串随便写一个就行)sort方法排序,然后根据这几个的顺序进行赋值并用sha1加密如下

    string signaturestring = "jsapi_ticket=" + ticket + "&noncestr=" + nocestr + "&timestamp=" + timestamp + "&url=" + url2;
    byte[] temp1 = Encoding.UTF8.GetBytes(signaturestring);
    
    SHA1CryptoServiceProvider sha = new SHA1CryptoServiceProvider();
    byte[] temp2 = sha.ComputeHash(temp1);
    var hashCode = BitConverter.ToString(temp2);
    hashCode = hashCode.Replace("-", "").ToLower();

     如此一来全部的都有了,返回这些就行了,如果报错了说url不合法就要取配置以下域名,二级域名也能用;

    四 ,带参二维码的扫描和事件的推送

    当你的公众号涉及到分享二维码给其他人,每一张二维码上还得要有你的信息的时候,需要在公众号的二维码上添加上一个场景值,比如你的用户id,当客户或者是其他用户通过你分享的二维码进入公众号的时候,微信服务器会推送给你一个xml包,其中一个子节点上就存着那个场景值;

    直接上代码--获取部分,

     public void GetQrCode()
     {
    	 string accesstoken = accesstokenstatic;
    	 if (string.IsNullOrEmpty(accesstoken))
    	 {
    		 //先主动取获取以下所需要的变量
    		 getaccesstoken(null, null);
    		 accesstoken = accesstokenstatic;
    		 Timer();
    	 }
    	//使用匿名对象作为post的参数然后再序列化
    	 var datap = new
    	 {
    		 //二维码的有效期(10天)
    		 expire_seconds = 864000,
    		 //类型动态的二维码
    		 action_name = "QR_SCENE",
    		 //信息
    		 action_info = new
    		 {
    			 scene = new {
    				 //自定义参数数值型为scene_id ,字符为scene_str
    				 scene_id = 15552300793
    			 }
    		 }
    	 };
    	 //使用流发送post带参请求
    	 string url = string.Format("https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={0}",accesstoken);
    	 HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
    	 request.Method = "POST";
    	 request.ContentType = "application/json";
    	 byte[] data = Encoding.UTF8.GetBytes(JsonHelper.ModelToJson(datap));
    	 request.ContentLength = data.Length;
    	 using (Stream strea = request.GetRequestStream())
    	 {
    		 strea.Write(data,0,data.Length);
    		 strea.Close();
    	 }
    	 HttpWebResponse respose = (HttpWebResponse)request.GetResponse();
    	 Stream stream = respose.GetResponseStream();
    	 string stringRespose = "";
    	 using (StreamReader sr = new StreamReader(stream, Encoding.UTF8))
    	 {
    		 stringRespose = sr.ReadToEnd();
    	 }
    	 if (string.IsNullOrEmpty(stringRespose))
    	 {
    		 Response.Write("sorry");
    	 }
    	 else { 
    		 //此时已经获取到二维码所使用的ticket
    		 string qrticket = JsonHelper.JsonToObj<QrCoderTicektModel>(stringRespose).ticket; 
    		 //二维码解析后的地址(可以自己转化为二维码)
    		 string qrurl =  JsonHelper.JsonToObj<QrCoderTicektModel>(stringRespose).url; 
    		 Response.Write("hhhhhh");
    		 LogHelper.WriteLog("getsometing:" + stringRespose);
    	 }
     }

    现在是获得了二维码的解析后的url,和获取二维码的ticket;有两种方式可以得到二维码,一种是直接将这个地址url返给前端,前端自己生成,二是后台获取到这个二维码后保存到服务器,前端自己去取;

    获取微信的推送,如刚才所说,扫码的时候会发送一个xml包;

    代码如下

    public void wx()
    {
    	//获取消息推送,或者是事件推送,类型是xml文件流
    	if (Request.RequestType == "POST")
    	{
    		#region 推送
    		//加载xml包,获取推送的内容
    		StreamReader sr = new StreamReader(Request.InputStream);
    		string xmlData = sr.ReadToEnd();
    		LogHelper.WriteLog(xmlData);
    		var doc = new XmlDocument();
    		doc.LoadXml(xmlData);
    		//获取xml包里的消息类型和事件类型,分类处理
    		var msgTypeNode = doc.SelectSingleNode("xml/MsgType");
    		var evenTypeNode = doc.SelectSingleNode("xml/Event");
    		string msgTypeString = msgTypeNode.InnerText;
    		if (evenTypeNode == null)
    		{
    			//此时为普通的消息推送
    		}
    		else
    		{
    			//此时为事件推送
    			string evenTypeString = evenTypeNode.InnerText;
    			//扫描带参的二维码的时候和关注和取关的推送
    			if (evenTypeString == "SCAN" || evenTypeString == "subscribe")
    			{
    				string SaoMaRenOpenId = doc.SelectSingleNode("xml/FromUserName").InnerText;
    				var BeiSaoRenCanShuNode = doc.SelectSingleNode("xml/EventKey");
    				if (BeiSaoRenCanShuNode != null)
    				{
    					string BeiSaoRenCanShuSting = BeiSaoRenCanShuNode.InnerText.Split('_')[1];
    					//已经获取扫码人和被扫人的个人id,下面就是存到数据库做关联就ok了,用到的时候取
    				}
    			}
    
    			//地理位置推送
    			if (evenTypeString == "LOCATION")
    			{
    
    			}
    
    			//点击自定义菜单拉去信息的时候的推送
    			if (evenTypeString == "CLICK")
    			{
    
    			}
    		}
    		#endregion
    	}

    五,

    natapp内网穿透:

    https://natapp.cn/注册,登录,获取自己的隧道,下载客户端,解压,cmd进入客户端的文件夹,natapp.exe -authtoken = "你的autotoken",

    如下则成功了

     

     

    展开全文
  • 微信公众号主要有以下几个步骤 微信公众号的通讯机制 微信公众号简介 1.注册微信公众号 2.注册测试公众号 3.搭建微信本地调试环境 1)下载客户端natapp: 2)安装natapp: 4.微信公众号接入(校验签名) 第1步中...
  • 1.登录开发公众平台 https://mp.weixin.qq.com/ 2.注册填好相关信息登录后 3.设置域名 首先介绍一款在线调试工具https://natapp.cn 注册登录后先购买vip隧道,然后购买二级域名 购买域名 然后下载...
  • 微信公众号开发小记(一)开篇

    万次阅读 2018-08-10 12:33:33
    接下来的时间里,我会以连载的形式分享微信公众号开发的相关文章,其中也会说一下我遇到的问题以及解决的思路,以及在开发过程中的一些感悟!因为是一个连载系列,所以会以一个初学者,也就是接触过编程但是没有接触...
  • 序言移动互联网时代,微信的影响力已经不用多说,从用户体量到无人不知的朋友圈都已充分体现了它的强大。无论是企业、媒体还是自媒体人都先后开通了微信公众号,利用其进行文化宣传和信息传播,起到了...
  • 微信公众号开发教程(一) 验证接入

    万次阅读 多人点赞 2018-06-22 14:22:35
    微信公众号开发教程(一)验证接入本篇文章主要介绍了微信公众号开发接入详细流程,希望对刚接触公众号开发的同学有所帮助,有兴趣的同学可多多关注叩丁狼公众号,后续会更新不同的公众号小案例。公众号的分类我们平常...
  • 微信公众号开发-超级简单

    万次阅读 2020-05-06 17:05:12
    这个功能就是别人给公众号发什么消息,就返回指定内容 关键词回复:输入关键词,返回指定内容 收到消息回复:当你不是输入关键词时,自动发送当前消息,如果输入的是关键词,就返回关键词所指定的内容 被关注...
  • 如何在本地进行微信公众号开发和调试
  • 微信公众号开发(三)前端界面

    万次阅读 热门讨论 2020-06-26 18:20:09
    由于该公众号主要的用途是用于公司售后服务,经沟通确定相关需求. 第一: 了解相关的UI框架,最终选定了Jquery-WeUI框架. 第二: 制作简单案例,在移动端进行测试,要达到移动端的自适应. 目前完成三个界面如下: ...
  • 在微信开发中,一个页面如何跳转到公众号首页? 相当于在页面实现一个退出登陆的效果,跳转到刚进入公众号首页的样子
  • 微信公众平台开发入门

    万人学习 2019-12-30 15:18:17
    例如,公众号是什么,它有什么特点,它能做什么,怎么开发公众号。 其次,通过本课程的学习,学员能够掌握微信公众平台开发的方法、技术和应用实现。例如,开发者文档怎么看,开发环境怎么搭建,基本的消息交互...
  • 微信服务号已开通微信支付,需要实现一个关注即给用户发送红包,不知道在哪里编辑代码,有具体的实现方法吗,开发文档看不大懂,不知道在哪实践,求大神告知。
  • 公众号后台开发上传文件功能

    千次阅读 2017-11-05 09:58:16
    微信公众号开发中, 上传文件是一个常用功能, 包括图片上传,视频,文件,附件,语音都要使用到这个。 今天的课程中, 介绍一个上传文件的类, 可以设置保存文件路径, 设置上传...
  •  我想进行微信公众号开发遇到的第一个困难就是微信公众号必须输入一个外网可以访问的域名,在网上我看到的都是使用ngrok或者花生壳的。但其实没必要这么麻烦。那么如何做呢?  1.下载QQ浏览器  我想程序员用的...
  • 随着自媒体时代的到来 ,很多企业、单位、个人都申请了微信公众号,但是,一个微信公众号如何才能发挥出应有的效果呢?从今天开始,我将总结我的运营经验。上图是第一部分,微信公众号的建立流程图。
  • 微信公众号开发基本配置

    万次阅读 2018-05-23 15:25:19
    下面,就对微信公众号开发的一些必要配置做一些简单说明.1.AppID和AppSecret以及IP白名单首先,你需要一个公众号,当然公司一般都会提供给你.登录公众号之后,你需要获取的最基本的参数就是AppID和AppSecret.左侧边栏: ...
  • 1、先引入入口.html文件 参见官网https://uniapp.dcloud.io/collocation/manifest?id=h5-template 2、配置manifest.json 3、运行 4、在微信开发者工具中进行公众号网页调试 ...
1 2 3 4 5 ... 20
收藏数 218,193
精华内容 87,277
关键字:

公众号开发