微信开发

微信有4种类型的服务:

  • 订阅号,针对媒体和个人,偏向于向用户传达咨询即发消息(一般是推文),每天可以群发一条消息,消息被放在“订阅号消息”栏中。
  • 服务号,针对企业和组织,偏向于交互服务,因为偏向于服务,所以每月只能发4次推文。
  • 企业号,所有功能都支持
  • 小程序

订阅号和服务号在开发者接口权限上有所不同,服务号更全面。不过对于一些基本的接口权限,订阅号都能满足,具体可以参考权限接口说明

对于有支付需求的应用,不管是服务号还是订阅号都需要做支付申请,但是尽量选择服务号。

出于用户体验和安全性方面的考虑,微信公众号的注册有一定门槛,某些高级接口的权限需要微信认证后才可以获取。所以,为了帮助开发者快速了解和上手微信公众号开发,熟悉各个接口的调用,微信推出了微信公众帐号测试号,通过手机微信扫描二维码即可获得测试号。

接入微信公众平台

环境准备:

  • 公众平台测试账号(参考上一节)
  • 内网映射,在接入公众平台时,需要提供服务器的URL地址,这里使用Natapp做内网映射

natapp隧道搭建

微信公众平台开发者模式需要提供服务器的URL地址,这里使用NATAPP做内网穿透。

打开NATAPP官网,注册登录后,点击购买隧道,可以使用免费隧道,也可购买付费隧道。免费隧道与付费隧道不同的是,付费隧道支持固定的二级域名(当然,这个二级域名也是需要你购买的),这样我们每次启动natapp做内网穿透时,域名不会改变的,方便调试。

购买隧道后,我们做客户端的配置,这里我是Mac环境,其他环境参考官网

下载natapp客户端,添加执行权限

# 解压
➜  Desktop unzip natapp_darwin_amd64_2_3_9.zip 
Archive:  natapp_darwin_amd64_2_3_9.zip
  inflating: natapp  
# 移动natapp到natapp目录下
➜  Desktop mkdir ~/natapp && mv natapp ~/natapp/
➜  Desktop cd ~/natapp 
➜  natapp chmod +x natapp 

运行natapp

./natapp -authtoken=xxxxxxxxxx

authtoken的值在我的隧道列表中可以查看。至此,natapp隧道搭建完成,总的流程如下:

  • 购买隧道(可以使用免费的隧道),设置域名和本地应用端口
  • 在本地启动natapp客户端做穿透

配置后台服务器信息

微信服务器使用应用服务器配置信息来与其做交互,配置信息主要包括URL地址、token和EncodingAESKey,其中:

  • URL 是应用服务器的地址,它是微信服务器与应用服务器的交互入口,公众号产生的任何消息(如用户关注该公众号,或用户向公众号发送消息等),都会调用该地址通知应用服务器。
  • token 表示令牌,在填写了URL地址后,微信服务器需要验证该地址是否有效,这是就需要用到这个token
  • EncodingAESKey 消息加密密钥,用户在公众号上产生的所有事件,都会以消息的形式发送了上面的URL地址,这个消息可以选择加密,这个EncodingAESKey就是消息加密的密钥。
image-20200326151044920.png

编写服务器验证接口

微信会验证我们提供的服务器URL是否有效,具体流程如下:

  • 微信服务器向URL发送GET请求
  • 服务器验证该请求,并返回指定的值

GET请求附带了一些参数,包括:

  • signature 签名,作为我们验证结果的比对
  • timestamp 时间戳,验证参数之一
  • nonce 随机数,验证参数之一
  • echostr 如果成功返回该参数

具体的验证算法伪代码如下:

if(sha1(token+timestamp+nonce) == signature){
  return echostr;
}
return "";

将token、timestamp和nonce按顺序组成一个字符串,使用sha1加密。如果加密后的字符串与signature相同,则返回echostr。微信服务器在收到echostr后,表示该开发服务器已经接入了微信服务器。

@RestController
@RequestMapping("/auth/wechat")
public class WeChatMakeController {

    @GetMapping("/mkserver")
    public String makeServer(String signature,
                             String timestamp,
                             String nonce,
                             String echostr){
        return echostr;
    }
}

这里为了方面,我直接返回了echostr。

获取access_token

调用各微信接口时都需使用access_token,access_token的有效期目前为2个小时,需定时刷新,每天只能刷新2000次,接口为GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET。一般的处理方式是,将access_token存储在内存的某一个地方(实例或redis中)统一管理。向外提供获取access_token的方法,方法内部需要考虑token过期与多线程的情况。参考me.chanjar.weixin.mp.api.impl.WxMpServiceHttpClientImpl#getAccessToken

public String getAccessToken(boolean forceRefresh) throws WxErrorException {
    // 微信配置存储器
    final WxMpConfigStorage config = this.getWxMpConfigStorage();
    // 是否获取 或 强制刷新
    if (!config.isAccessTokenExpired() && !forceRefresh) {
      // 未过期直接返回数据
      return config.getAccessToken();
    }

    // 多线程情况
    Lock lock = config.getAccessTokenLock();
    lock.lock();
    try {
      if (!config.isAccessTokenExpired() && !forceRefresh) {
        return config.getAccessToken();
      }
      // 调用微信接口获取access_token
      // 更新存储器中的token
      // 返回token
     
    } finally {
      lock.unlock();
    }
}

配置菜单

公众号下的菜单栏最多包括3个一级菜单,每个一级菜单最多包含5个二级菜单。菜单有多种类型

  • click 点击事件
  • view 跳转url
  • miniprogram 跳转到小程序
  • scancode_push 扫码推事件
  • scancode_waitmsg 扫码推事件且弹出“消息接收中”提示框.
  • pic_sysphoto 弹出系统拍照发图
  • pic_photo_or_album 弹出拍照或者相册发图
  • pic_weixin 弹出微信相册发图器
  • location_select 弹出地理位置选择器
  • location_select 弹出地理位置选择器
  • media_id 下发消息(除文本消息)
  • view_limited 跳转图文消息URL

其中最常用的是click、view和miniprogram。不同的菜单类型需要加的不同的参数,比如,view类型的菜单是跳转url的,所以,我们需要附加url项。而click类型的菜单,我们需要附加一个key,以标识该菜单。

 {
     // button代表的是菜单栏,是一个数组,里面的元素表示菜单
     "button":[
       // click类型的菜单,使用key标识该菜单,方便后台处理对应的业务
       {    
            "type":"click",
            "name":"今日歌曲",
            "key":"V1001_TODAY_MUSIC"
        },
       // view类型的菜单,url表示要跳转的地址
        {   
           "type":"view",
           "name":"搜索",
           "url":"http://www.soso.com/"
        },
       // 使用子菜单,sub_button是一个数组,里面的元素是其子菜单
        {
          "name":"其他",
          "sub_button": []
        }
     ]
 }

生成菜单时,需要向以下地址发送post请求

https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN

获取用户信息

公众号获取用户信息有两种方式,通过openid获取以及通过OAuth2获取。

通过OpenID获取用户信息

当用户关注公众号后,微信服务器会为该用户对于公众号产生一个openid,这个openid是唯一的,同一个用户在不同的公众号中openid是不同的。后台可根据该openid来获取用户信息。

GET https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN

access_token是我们上面章节介绍获取的。openid是微信服务器向后台配置的URL发送的请求参数,比如,当用户关注了公众号,微信服务器会调用后台的url地址,其中FromUserName就是该用户的OpenID。

通过OAuth2获取用户信息

1 订阅号不能使用该接口,服务号需要认证后才能使用该接口

2 必须使用微信内置浏览器

关于OAuth2.0 授权可以参考阮一峰的理解OAuth 2.0,因为这里采用的是OAuth2.0的授权码模式,所以这里简要的说一下使用授权码模式的步骤。

  1. 获取授权码(code)
  2. 使用code换access_token
  3. 获取用户信息

获取授权码

获取授权码的地址如下:

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

  • redirect_uri 为微信授权后的回调页,该回调页的域名部分需要在公众号的后台设置(接口权限/网页授权)

    注意:在公众号后台设置的回调地址时,填写的是域名,而不是URL,因此请勿加 http:// 等协议头。

  • response_type 授权类型,这里填固定值code,因为微信采用的是OAuth2.0的授权码模式

  • scope 授权的作用域,有两种取值

    snsapi_base snsapi_userinfo
    用户信息 用户的OpenID 用户基本信息(昵称、头像等)
    感知度 用户无感知,直接进入回调页 1 用户未关注公众号,弹出授权页面,用户手动授权<br />2 用户已关注公众号,且通过点击公众号的菜单进入,无需授权页,即无感知

    由上可以看出,用户即使未关注公众号(已认证的服务号),也可以获取用户信息。

  • state 表示客户端的状态,微信服务器会原封不动的返回该值

获取access_token

注意这里的网页授权access_token与前面所讲的access_token是不同的,这里的access_token只适合在OAuth2.0授权流程中使用。

通过第一步后,微信服务器会调用回调也的地址,并附带code和state两个参数

redirect_uri/?code=CODE&state=STATE。

回调页使用这个code从微信服务器上获取access_token。这一步是后台服务器和微信服务器做交互,可以认为是安全的通信。

https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID
&secret=SECRET
&code=CODE
&grant_type=authorization_code
  • appid 公众号的appid
  • secret 公众号的appsecret
  • code 第一步获取的code
  • grant_type 授权类型,授权码模式,所以这里是固定值authorization_code

返回如下信息

{
  "access_token":"访问token",
  "expires_in":7200,
  "refresh_token":"刷新token",
  "openid":"OPENID",
  "scope":"SCOPE" 
}

当用户未关注公众号时,访问公众号的网页,也会产生OpenID,且这个OpenID与关注后的OpenID相同。

获取用户信息

使用以下地址获取用户信息

https://api.weixin.qq.com/sns/userinfo?
access_token=ACCESS_TOKEN
&openid=OPENID
&lang=zh_CN
  • access_token 访问令牌,上一步得到
  • openid 用户的唯一标识,上一步得到

返回的用户信息

{   
  "openid":" OPENID",
  "nickname": "NICKNAME",
  "sex":"1男,2女,0未知",
  "province":"PROVINCE",
  "city":"CITY",
  "country":"COUNTRY",
  "headimgurl": "headimgurl",
  "privilege":[ "PRIVILEGE1" "PRIVILEGE2"     ],
  "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
}

前后端分离获取用户信息

先后端分离时,微信登录的解决办法:

  • 前端请求微信的code
  • 收到code后,将code发送给后端
  • 后端通过code换取access-token
  • 通过access-token,获取当前登录的用户信息,返回给前端
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,511评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,495评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,595评论 0 225
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,558评论 0 190
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,715评论 3 270
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,672评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,112评论 2 291
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,837评论 0 181
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,417评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,928评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,316评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,773评论 2 234
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,253评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,827评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,440评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,523评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,583评论 2 249

推荐阅读更多精彩内容