在进行微信开发中,你时常会因为获取某些值或者配置什么设置而陷入莫名其妙的坑中,我也一样。明明觉得对的,却始终不成功,这篇文章主要总结一下我陷入的坑。
在进行微信开发中,你可能会想获取用户的openID,作为用户在公众号中的唯一凭证。
官方文档中对openID是这样介绍的:为了识别用户,每个用户针对每个公众号会产生一个安全的OpenID,如果需要在多公众号、移动应用之间做用户共通,则需前往微信开放平台,将这些公众号和应用绑定到一个开放平台账号下,绑定后,一个用户虽然对多个公众号和应用有多个不同的OpenID,但他对所有这些同一开放平台账号下的公众号和应用,只有一个UnionID,可以在用户管理-获取用户基本信息(UnionID机制)文档了解详情。
也许你会在用户打开我们的网页时获取到用户的openID。
那问题来了,我们到底该怎么获取到这个openID呢?
你可以参考这地址下的文档:http://mp.weixin.qq.com/wiki/4/9ac2e7b1f1d22e9e57260f6553822520.html
获取openID的基本流程图:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxf0e81c3bee622d60&redirect_uri=你要回调的的地址&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect
在上面的链接中通过设置scope的值可以改变获取方式,snsapi_base(不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息)。
当打开此链接时页面将跳转至 redirect_uri/?code=CODE&state=STATE。。这时上面的code便会被赋予值,这值便是我们想要的code。
- 通过Code获取openID
获取code后,请求以下链接获取access_token:
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
如果成功返回json数据,上面便带有我们想要的openID。本人在后台开发中用到是SpringMVC技术,将就点看吧。
参考如下实例代码:
@RequestMapping("startRegister")
public void thCarStartRegister(Map map, HttpServletRequest request,Writer out) throws IOException{
String wxcode = request.getParameter("code");
if (wxcode != null && !"".equals(wxcode)) {// 判断微信的code是否有值
// 微信获取用户openID
String openID = (String) JSON.parseObject(ThcHttpUtils.thcGet(WeiXinUtils.getWXAccessUri(wxcode), ""))
out.wirte(openID);
} else {//没有code值重新重定向下
return "redirect:" + WeiXinUtils.getWXCodeUri(THCarData.THCAR_URL + "startRegister");
}
}
public static String getWXAccessUri(String wxcode) {
String uri = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + APPID + "&secret=" + SECRET + "&code="
+ wxcode + "&grant_type=authorization_code";
return uri;
}
这样便能取得openID,用于我们对用户的唯一身份凭证。上面写得比较粗糙,希望对你有帮助。
可以说这东西真麻烦,一不小心你就陷入坑中。一定要详细再详细的看文档。
文档参考地址:https://mp.weixin.qq.com/wiki/11/74ad127cc054f6b80759c40f77ec03db.html
基本实现总结
1.引入js文件:在需要调用JS接口的页面引入如下JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.0.0.js
2.配置js
$(function wxconfig(){
wx.config({
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: '', // 必填,公众号的唯一标识
timestamp: , // 必填,生成签名的时间戳
nonceStr: '', // 必填,生成签名的随机串
signature: '',// 必填,签名,见附录1
jsApiList: [] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});
wx.ready(function(){
// config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
});
wx.error(function(res){
// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
});
});
具体的了解请详情文档。这里需要特别强调的是wx.config的配置。
很有可能你会出现如下错误:
出现config:fail错误
这是由于传入的config参数不全导致,请确保传入正确的appId、timestamp、nonceStr、signature和需要使用的jsApiList
invalid signature签名错误
建议按如下顺序检查:
1.确认签名算法正确,可用 http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign 页面工具进行校验。
2.确认config中nonceStr(js中驼峰标准大写S), timestamp与用以签名中的对应noncestr, timestamp一致。
3.确认url是页面完整的url(请在当前页面alert(location.href.split(‘#’)[0])确认),包括’http(s)://’部分,以及’?’后面的GET参数部分,但不包括’#’hash后面的部分。
4.确认 config 中的 appid 与用来获取 jsapi_ticket 的 appid 一致。
5.确保一定缓存access_token和jsapi_ticket。
6.确保你获取用来签名的url是动态获取的,如上面的http://xxx.xxx.com/startRegister就是一个冬天的url。 如果是html的静态页面在前端通过ajax将url传到后台签名,前端需要用js获取当前页面除去’#’hash部分的链接(可用location.href.split(‘#’)[0]获取,而且需要encodeURIComponent),因为页面一旦分享,微信客户端会在你的链接末尾加入其它参数,如果不是动态获取当前链接,将导致分享后的页面签名失败。
7.一定要确保你进行配置的页面url与签名所用的url地址一致。如上面的配置是在http://xx.xx.com/startRegister这页面上配置的,那么你用于加密的url也要与此相同。
the permission value is offline verifying 错误
这个错误是因为config没有正确执行,或者是调用的JSAPI没有传入config的jsApiList参数中。建议按如下顺序检查:
确认config正确通过。
如果是在页面加载好时就调用了JSAPI,则必须写在wx.ready的回调中。
确认config的jsApiList参数包含了这个JSAPI。
permission denied 错误
该公众号没有权限使用这个JSAPI,或者是调用的JSAPI没有传入config的jsApiList参数中(部分接口需要认证之后才能使用)。
function not exist 错误
当前客户端版本不支持该接口,请升级到新版体验。
本人实例代码:
前台部分
$(function share() {
wx.config({
debug:true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId : '${WXConfig.appId}', // 必填,企业号的唯一标识,此处填写企业号corpid
timestamp:${WXConfig.timestamp}, // 必填,生成签名的时间
nonceStr : '${WXConfig.nonceStr}', // 必填,生成签名的随机串
signature : '${WXConfig.signature}',// 必填,签名,见附录1
jsApiList:${WXConfig.jsApiList} // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});
wx.ready(function() {
alert("readyOK");
// config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
});
wx.error(function(res) {
alert("err:" + res.val());
// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
});
});
后台部分
@RequestMapping(value = "/sharePage")
public String thCarSharePage(Map map) throws Exception {
String nonceStr = THCarUtils.getARandomString();
String timestamp = Long.toString(System.currentTimeMillis() / 1000);
String signature = WeiXinUtils.getWXSignature(WeiXinUtils.WEIXIN_JSAPI_TICKET, nonceStr, timestamp, SHARE_URL);
WXJSConfig config = new WXJSConfig(WeiXinUtils.APPID, SHARE_URL, timestamp, nonceStr, signature,
"['onMenuShareTimeline', 'onMenuShareAppMessage']");
map.put("WXConfig", config);
return "share/sharePage";
}
其中WXJSConfig是自己封装的类,里面的属性与配置一致,我就不给出了。
看到这,你可能又会因为获取ACCESS_TOKEN和JSAPI_TICKET而犯愁。在官方文档中对应这两个值给出了限制,意味着你每天获取它们的次数是有上限的,我们不可以每次请求都获取一次,我们需要对它们进行缓存,你可以将其保存在数据库中,也可以直接放在内存中。下面是个人的方式,仅供参考,可能不是很合理。
public class WeiXinUtils {
public static String WEIXIN_TOKEN = "";// 注意微信TOKEN值得有效期为2小时。每日上限调用2000此,所有我们设每个小时调用一次
public static String WEIXIN_JSAPI_TICKET = "";
public static final String APPID = "wxe2b46f1dbc599123";
private static final String SECRET = "87j3a7f8f5n86764b78d8769298187c33";
private static final long TIME = 6000000;// 毫秒 ,100分钟
// private static final long TIME = 10000;
private static int i = 0;
private static boolean haveStart = false;
static {
System.out.println("start get token");
if (!haveStart) {// 确保只调用一次
startChangeWXToken();
haveStart = true;
}
}
/**
* 定时获取获取微信的Token值
*/
public static void startChangeWXToken() {
TimerTask task = new TimerTask() {
@Override
public void run() {
WeiXinUtils.WEIXIN_TOKEN = JSON
.parseObject(ThcHttpUtils.thcGet("https://api.weixin.qq.com/cgi-bin/token",
"grant_type=client_credential&appid=" + APPID + "&secret=" + SECRET))
.getString("access_token");
startChangejsapiTicket();
}
};
Timer timer = new Timer();
timer.schedule(task, 0, TIME);
}
/**
* 定时获取JSAPI_TICKET
*/
private static void startChangejsapiTicket() {
boolean isOK = false;
while (!isOK) {
if (WEIXIN_TOKEN != null && !WEIXIN_TOKEN.isEmpty()) {
WeiXinUtils.WEIXIN_JSAPI_TICKET = JSON
.parseObject(ThcHttpUtils.thcGet("https://api.weixin.qq.com/cgi-bin/ticket/getticket",
"access_token=" + WEIXIN_TOKEN + "&type=jsapi"))
.getString("ticket");
isOK = true;
}
}
}
}
上面的APPID与SECRET是虚构的。上面通过定时器的方式每100分钟会调用一次方法,更新TOKEN与TICKET的值。
为了让项目部署到服务器上就是其有值,本人使用了静态块的方式,并在bean中进行声明。
上面这些是个人在微信开发过程中的小小总结。写的比较粗糙,希望对你由帮助。您在开发中你要确保相关公众号是正常的,相关权限有开通。所使用的域名与你在公众号中设置的一致。