微信有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就是消息加密的密钥。
编写服务器验证接口
微信会验证我们提供的服务器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的授权码模式,所以这里简要的说一下使用授权码模式的步骤。
- 获取授权码(code)
- 使用code换access_token
- 获取用户信息
获取授权码
获取授权码的地址如下:
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,获取当前登录的用户信息,返回给前端
网友评论