一、公众号介绍
-
微信公众号分类
- 订阅号:主要偏于为用户传达资讯(类似报纸杂志),认证前后都是每天只可以群发一条消息;
- 服务号:主要偏于服务交互(类似银行,114,提供服务查询),认证前后都是每个月可群发4条消息;
- 企业号:主要用于公司内部通讯使用,需要先有成员的通讯信息验证才可以关注成功企业号;
-
公众号交互模式
公众号的交互可以看作是,微信客户端,微信服务器和公众号后台三方的交互,其交互模式如图所示:
在公众号后台调用用户信息相关接口和消息推送时,以用户的OpenID作为唯一标识;
什么是OpenID
为了识别用户,每个用户针对每个公众号会产生一个安全的OpenID,如果需要在多公众号、移动应用之间做用户共通,则需前往微信开放平台,将这些公众号和应用绑定到一个开放平台账号下,绑定后,一个用户虽然对多个公众号和应用有多个不同的OpenID,但他对所有这些同一开放平台账号下的公众号和应用,只有一个UnionID;
二、开发注意事项
- 微信公众平台开发是指为微信公众号进行业务开发,为移动应用、PC端网站、公众号第三方平台(为各行各业公众号运营者提供服务)的开发,请前往微信开放平台接入。
- 在申请到认证公众号之前,你可以先通过测试号申请系统,快速申请一个接口测试号,立即开始接口测试开发。
- 在开发过程中,可以使用接口调试工具来在线调试某些接口。
- 每个接口都有每日接口调用频次限制,可以在公众平台官网-开发者中心处查看具体频次。
- 在开发出现问题时,可以通过接口调用的返回码,以及报警排查指引(在公众平台官网-开发者中心处可以设置接口报警),来发现和解决问题。
- 公众平台以access_token为接口调用凭据,来调用接口,所有接口的调用需要先获取access_token,access_token在2小时内有效,过期需要重新获取,但1天内获取次数有限,开发者需自行存储,详见获取接口调用凭据(access_token)文档。
- 公众平台接口调用仅支持80端口。
- 微信平台推送给公众号后台的数据均为xml格式,而调用接口返回的数据均为json格式
三、开发准备
-
准备服务器,搭建后台服务
在注册成为微信公众号开发者之前,需要先搭建一个web应用服务来作为公众号的后台服务,这里以SpringMVC为例,在做好基础配置和部署后,编写这样一个Controller来作为处理微信服务的接口:
@Controller
@RequestMapping("/wx")
public class WechatController {
@RequestMapping("")
public void core(HttpServletRequest request, HttpServletResponse response) throws IOException {
//这里对request进行处理
}
}
-
进行开发者配置
在成为微信公众号开发者后,登录公众号平台官网会要求开发者配置服务器相关的信息
这里的URL(服务器地址)格式为:http://外网IP(域名):端口号/微信服务接口 。 http的端口号固定使用 80, https 为443不可填写其他。这个url为微信平台将该公众号中的用户信息/操作推送到公众号后台的请求地址,在每次修改配置时会发送一条用于校验服务器地址的请求,后台服务需要对请求中的校验字段进行处理,并做出相应,配置才会生效。
Token:用于验证后台服务器的标识,自行设置。
-
对微信平台的发送的验证消息进行处理
对微信平台推送来的请求处理逻辑流程图如下:
用java代码的处理方式如下:
// 微信加密签名
String signature = request.getParameter("signature");
// 时间戳
String timestamp = request.getParameter("timestamp");
// 随机数
String nonce = request.getParameter("nonce");
// 随机字符串
String echostr = request.getParameter("echostr");
PrintWriter out = response.getWriter();
// 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
if (SignUtil.checkSignature(signature, timestamp, nonce)) {
if(echostr != null) { //
out.print(echostr);
} else {
//进行对应的消息/操作处理
}
}
out.close();
/**
* 验证签名算法
*
* @param signature
* @param timestamp
* @param nonce
* @return
*/
public static boolean checkSignature(String signature, String timestamp, String nonce) {
String[] arr = new String[] { TOKEN, timestamp, nonce };
// 将token、timestamp、nonce三个参数进行字典序排序
Arrays.sort(arr);
StringBuilder content = new StringBuilder();
for (int i = 0; i < arr.length; i++) {
content.append(arr[i]);
}
MessageDigest md = null;
String tmpStr = null;
try {
md = MessageDigest.getInstance("SHA-1");
// 将三个参数字符串拼接成一个字符串进行sha1加密
byte[] digest = md.digest(content.toString().getBytes());
tmpStr = byteToStr(digest);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
content = null;
// 将sha1加密后的字符串可与signature对比,标识该请求来源于微信
return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;
}
当配置页面tonken验证成功时,将保存该配置
四、常见场景
-
自动回复用户一条消息
预实现功能
粉丝给公众号一条文本消息,公众号立马回复一条文本消息给粉丝,不需要通过公众平台网页操作。
具体流程
粉丝给公众号发送文本消息:“欢迎开启公众号开发者模式”,在开发者后台,收到公众平台发送的xml 如下:
<xml>
<ToUserName><![CDATA[公众号]]></ToUserName>
<FromUserName><![CDATA[粉丝号]]></FromUserName>
<CreateTime>1460537339</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[欢迎开启公众号开发者模式]]></Content>
<MsgId>6272960105994287618</MsgId>
</xml>
解释:
createTime 是微信公众平台记录粉丝发送该消息的具体时间
MsgType 用于标记消息类型,一般用于区别判断,text说明该xml 是文本消息
MsgId 是公众平台为记录识别该消息的一个标记数值, 微信后台系统自动产生
接下来对推送这个消息的请求作出响应,则可以对发送消息的用户进行回复
特别强调:
- 被动回复消息,即发送被动响应消息,不同于客服消息接口
- 它其实并不是一种接口,而是对微信服务器发过来消息的一次回复
- 收到粉丝消息后不想或者不能5秒内回复时,需回复“success”字符串(下文详细介绍)
- 客服接口在满足一定条件下随时调用
公众号想回复给粉丝一条文本消息,内容为“test”, 那么开发者发送给公众平台后台的xml 内容如下:
<xml>
<ToUserName><![CDATA[粉丝号]]></ToUserName>
<FromUserName><![CDATA[公众号]]></FromUserName>
<CreateTime>1460541339</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[test]]></Content>
</xml>
备注:
- ToUserName(接受者)、FromUserName(发送者) 字段按际填写
- createtime 只用于标记开发者回复消息的时间,微信后台发送此消息都是不受这个字段约束
- text : 用于标记 此次行为是发送文本消息 (当然可以是image/voice等类型,不同类型的具体参数参考官方文档)
- 文本换行使用‘\n’
将以上内容写入response的响应体中,则可对相应消息进行回复,其流程图如下图所示:
-
自定义公众号菜单
自定义菜单需要主动调用微信的相关接口,这就需要使用到Access_token
什么是 access_token
access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。
具体调用方法可查看公众平台wiki(官方防止盗链,请复制地址在浏览器查看), 在实际使用时,对access_token的控制需要使用中控服务器的方式来进行获取
由于几乎所有主动调用微信平台的接口都需要使用access_token,这里以自定义菜单为例,其余接口都会以相同模式进行调用。
预实现功能
服务端自行定义菜单样式,在用户点击菜单后,打开对应页面或者回复相应消息。
具体流程
首先查看公众平台wiki得知新建自定义菜单的接口为
POST https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN
发送的json数据格式为
{
"button":[
{
"type":"click",
"name":"今日歌曲",
"key":"V1001_TODAY_MUSIC"
},
{
"name":"菜单",
"sub_button":[
{
"type":"view",
"name":"搜索",
"url":"http://www.soso.com/"
},
{
"type":"click",
"name":"赞一下我们",
"key":"V1001_GOOD"
}]
}]
}
其中type字段标识了菜单按钮的类型,常用的类型为click和view,分别代表点击推送事件类型和跳转指定URL类型。其它类型可参考公众平台wiki。
在微信接口调用正确时,均会返回如下数据格式:
{"errcode":0,"errmsg":"ok"}
否则返回如下数据:
{"errcode":40018,"errmsg":"invalid button name size"}
其中errmsg将说明具体错误信息
实现方式
使用Apache的httpclient工具包进行微信接口的请求,其中的数据可以先定义对象,再使用第三方json包进行序列化。具体实现过程不再赘述。
自定义菜单成功后,将生成如下类似界面
若其中一个为click类型按钮,则客户端点击该按钮后,服务端将接收到内容如下格式的请求:
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[CLICK]]></Event>
<EventKey><![CDATA[EVENTKEY]]></EventKey>
</xml>
其中Event标识的事件类型为“CLICK”,EventKey与自定义接口时传入的key值相对应
对事件的回复方式与普通消息类似,不同的在于某些事件不可以进行回复,且服务器未作响应时,微信平台不会重复发送请求。
-
通过微信认证打开页面,并校验用户是否绑定已有账号
如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。因此,可以通过微信网页的授权机制,在用户访问公众号后台网页时获取访问用户的相关信息。
微信的认证授权是基于OAuth2.0机制实现的,有关OAuth2.0的知识可参考网上的资料。OAuth2.0基本模式可以理解为用户在授权方进行登录,再由授权方回调请求授权方的接口并带上接口调用凭证,请求授权方再使用该凭证和秘钥信息调用授权方用户信息的相关接口
微信授权的基本步骤如下:
- 用户同意授权,获取code
- 通过code换取网页授权access_token
- 刷新access_token(如果需要)
- 拉取用户信息(需scope为 snsapi_userinfo)
- 检验授权凭证(access_token)是否有效
若是针对已关注公众号的微信用户,则仅需前面两步即可取得用户的相关信息。每一步的具体操作可以参考公众平台wiki
实现方式
这里以SpringMVC的实现方式为例,定义一个链接为
"https://open.weixin.qq.com/connect/oauth2/authorize?appid="+Config.instance().getAppid()+"&redirect_uri=http://wx.keyil.cn/99/api/oauth2&response_type=code&scope=snsapi_base&state=userInfo#wechat_redirect"
的按钮
参数顺序要保证一致,微信授权对该链接的校验较为严格
用户在点击该链接后,将进行微信授权,已关注用户会通过静默授权的方式跳转至回调页面。此时,在回调接口对微信返回的code进行处理:
@Controller
@RequestMapping("/oauth2")
public class OAthuController {
@Autowired
private AccountService accountService;
@RequestMapping("")
public String oathu2(@RequestParam(required = true) String code, String state, HttpServletRequest request,
HttpServletResponse response) {
String openId = accountService.getOauth2OpenId(code);
Assert.notNull(openId, "openId为空");
CookieUtil.setCookie(response, "openid", openId, "hostname", "/api", 60 * 5);
String redirectUrl = "redirect:http://hostname";
if (accountService.getAccountInfo(openId) == null) {
redirectUrl += "userbind.html";
} else {
switch (state) {
case "userInfo":
redirectUrl += "userInfo.html";
break;
case "addressList":
redirectUrl += "addressList.html";
break;
}
}
return redirectUrl;
}
}
其中的AccountService已经对微信跟用户相关的接口进行了封装;对于已经关注公众号的用户,通过微信返回的code字段,便可以获取用户的OpenID,进而获取用户的相关信息;将该OpenID写入cookie,以保证该用户在后续的绑定请求中携带该字段。
@RequestMapping(value = "/bind", method = RequestMethod.POST)
public void bindUser(@RequestParam(required = true) String phoneNumber, @CookieValue(required = true) String openid,
HttpServletResponse response) {
accountService.bindUser(openid, phoneNumber);
this.writeSuccessResponse(response, "success");
}
在调用绑定请求时,通过cookie获取用户的OpenID,通过用户验证后,对该用户进行微信绑定。以上是demo的写法,仅供参考。
五、结语
以上内容便是微信公众号开发的一些主要模式的总结,微信后台的大部分功能均可遵循以上模式进行开发。除此之外,还有微信提供的js-jdk用于公众号后台网页的开发,该内容将会在后续的开发过程中进行补充。内容中如有疏漏,还往读者积极指正。