微信提供了微信公众号开发者手册,官方地址:https://mp.weixin.qq.com/
公众号内许多复杂的业务场景,都是通过网页形式来提供服务,这时需要用到: 微信JS-SDK。以地理位置接口为例进行阐述。
微信公众号请求网页授权之前,开发者需要先登记授权回调域名。
注意:这里填写的是域名,且为全域名。授权后该域名下的其他页面都可以授权,例如:www.qq.com为回调域名,配置以后此域名下面的页面http://www.qq.com/music.html 、 http://www.qq.com/login.html 都可以进行OAuth2.0鉴权。
官方:微信公众平台 >> 微信网页开发 >> 微信网页授权
官方:
在需要调用JS接口的页面引入如下JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.4.0.js
如需进一步提升服务稳定性,当上述资源不可访问时,可改访问:http://res2.wx.qq.com/open/js/jweixin-1.4.0.js (支持https)。
备注:支持使用 AMD/CMD 标准模块加载方法加载
所有引用微信JS-SDK的页面都需要提供权限验证信息,否则无法调用。同样的URL只需要配置一次即可,URL发生变化时需要重新调用配置信息进行验证。
wx.config({
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: '', // 必填,公众号的唯一标识
timestamp: , // 必填,生成签名的时间戳
nonceStr: '', // 必填,生成签名的随机串
signature: '',// 必填,签名
jsApiList: [] // 必填,需要使用的JS接口列表
});
这里涉及到两类配置信息的获取,一类是appId所属的公众号标识类配置信息,另一类是签名。JS接口列表可以根据需求在微信公众平台附录中获取。这里所写的是两个地理接口的相关js接口:
jsApiList: ['openLocation','getLocation'] // 必填,需要使用的JS接口列表
接口配置信息主要用于消息接口的配置。公众号是以微信用户的一个联系人形式存在的,消息会话是公众号与用户交互的基础。在这里填写的URL主要用于接收用户信息,进行业务操作后返回信息,没有单独的页面链接。如果不是H5开发的话这里可以空着不写。
这里填写的域名就是上面1点提到的回调域名绑定授权,具体请看上面1. 域名绑定
生成签名之前,我们要先获取两个参数:access_token 和 jsapi_ticket 。这两个参数的有效期为7200秒,所以一般是放在全局变量中。
官方:微信公众平台 >> 开始开发 >> 获取access_token
接口调用请求说明:
https请求方式: GET
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
public static String getAccessToken(String appId, String secret, String accessTokenUrl) {
HttpClient client = HttpClients.createDefault();
// 获得Http客户端(可以理解为:你得先有一个浏览器;注意:实际上HttpClient与浏览器是不一样的)
HttpGet get = new HttpGet(MessageFormat.format(accessTokenUrl,appId,secret));
// 创建Get请求
try {
String accessToken = (String) CacheUtil.get("wxCache", "accessToken");
//这里把accessToken放在全局变量中,通过CacheUtil缓存工具类判断accessToken是否失效
if (StringUtils.isBlank(accessToken) || StringUtils.isEmpty(accessToken)) {
HttpResponse response = client.execute(get);
// 由客户端执行(发送)Get请求
HttpEntity entity = response.getEntity();
// 从响应模型中获取响应实体
String result = EntityUtils.toString(entity, "UTF-8");
Map accessTokenMap = JSON.parseObject(result);
accessToken = (String)accessTokenMap.get("access_token");
//将响应实体进行编码转换
CacheUtil.put("wxCache", "accessToken", accessToken);
//将accessToken存入缓存变量中
}
return accessToken;
} catch (ClientProtocolException e) {
log.error(e.getMessage());
} catch (IOException e) {
log.error(e.getMessage());
}
return null;
}
用同样的get请求方法,获得jsapi_ticket。与access_token性质相似,有效期为7200秒,建议放在全局变量中,便于下次请求的时候判断是否仍在有效期内。
public static String getTicket(String accessToken,String ticketUrl) {
HttpClient client = HttpClients.createDefault();
HttpGet get = new HttpGet(MessageFormat.format(ticketUrl,accessToken));
try {
String ticket = (String) CacheUtil.get("wxCache", "jsapi_ticket");
if (StringUtils.isBlank(ticket) || StringUtils.isEmpty(ticket)) {
HttpResponse response = client.execute(get);
HttpEntity entity = response.getEntity();
String result = EntityUtils.toString(entity, "UTF-8");
Map jsApiTicketMap = JSON.parseObject(result);
ticket = (String)jsApiTicketMap.get("ticket");
CacheUtil.put("wxCache", "jsapi_ticket", ticket);
}
return ticket;
} catch (ClientProtocolException e) {
log.error(e.getMessage());
} catch (IOException e) {
log.error(e.getMessage());
}
return null;
}
获得jsapi_ticket之后,就可以生成JS-SDK权限验证的签名了。
签名生成规则如下:
参与签名的字段包括noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳), url(当前网页的URL,不包含#及其后面部分) 。
//生成时间戳和随机字符串
String noncestr = UUID.randomUUID().toString().replace("-", "").substring(0, 16);
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
时间戳和随机字符串都是随机生成的,但是随机生成规则不同,注意区分。
//获取url
String url = pathUrl;
url必须与用户进入页面的url相同,从前台传入,防止因为参数不同而引起的url不同,从而获取签名失败。
对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1:
//将参数排序并拼接字符串
String sortString = WeChatCommonUtil.sort(url, timestamp, ticket, noncestr);
/**
* @Param [signature, timestamp, ticket, echostr]
* @Return java.lang.String
* @Description: 对所有待签名参数按照字段名的ASCII 码从小到大排序
*/
public static String sort(String url, String timestamp, String ticket, String noncestr) {
return "jsapi_ticket=" + ticket + "&noncestr=" + noncestr + "×tamp=" + timestamp + "&url=" + url;
}
对string1进行sha1签名,得到signature:
至此,得到权限验证配置所有信息。
以下步骤官网都有参考:
wx.ready(function(){
// config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作.
// 所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。
// 对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
});
这里没有写到error接口,不够严谨。
wx.error(function(res){
// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
});
wx.checkJsApi({
jsApiList: [‘chooseImage’], // 需要检测的JS接口列表
success: function(res) {
// 以键值对的形式返回,可用的api值true,不可用为false
// 如:{“checkResult”:{“chooseImage”:true},“errMsg”:“checkJsApi:ok”}
}
});
需要检测的JS接口与config中填写的 jsApiList 基本一致。
config信息验证后,才会调用ready方法,所以将checkJsApi放在ready方法中执行。
使用微信内置地图查看位置接口:用于打开导航地图,获取目的地坐标定位。
wx.openLocation({
latitude: , // 纬度,浮点数,范围为90 ~ -90
longitude: , // 经度,浮点数,范围为180 ~ -180。
name: ’ ', // 位置名
address: ’ ', // 地址详情说明
scale: 1, // 地图缩放级别,整形值,范围从1~28。默认为最大
infoUrl: ‘’ // 在查看位置界面底部显示的超链接,可点击跳转
});
获取地理位置接口:主要用于获取自身坐标定位。
wx.getLocation({
type: ‘wgs84’, // 默认为wgs84的gps坐标,如果要返回直接给openLocation用的火星坐标,可传入’gcj02’
success: function (res) {
var latitude = res.latitude; // 纬度,浮点数,范围为90 ~ -90
var longitude = res.longitude; // 经度,浮点数,范围为180 ~ -180。
var speed = res.speed; // 速度,以米/每秒计
var accuracy = res.accuracy; // 位置精度
}
});
注意:两个接口的经纬度都是浮点数类型,否则无法调用。
wx.ready(function () {
// config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作.
// 所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。
// 对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
wx.checkJsApi({
jsApiList: ['checkJsApi', 'openLocation', 'getLocation'], // 需要检测的JS接口列表,所有JS接口列表见附录2,
success: function (res) {
// 以键值对的形式返回,可用的api值true,不可用为false
// 如:{"checkResult":{"chooseImage":true},"errMsg":"checkJsApi:ok"}
}
});
wx.getLocation({
type: 'gcj02', // 默认为wgs84的gps坐标,如果要返回直接给openLocation用的火星坐标,可传入'gcj02'
dataType: "json",
success: function (res) {
var latitude = res.latitude; // 纬度,浮点数,范围为90 ~ -90
var longitude = res.longitude; // 经度,浮点数,范围为180 ~ -180。
var speed = res.speed; // 速度,以米/每秒计
var accuracy = res.accuracy; // 位置精度
}
});
$(".nav-to").on ('click','a',function () {
wx.openLocation({
latitude: parseFloat($(".nav-to").attr("lat")), // 纬度,浮点数,范围为90 ~ -90
longitude:parseFloat($(".nav-to").attr("lng")), // 经度,浮点数,范围为180 ~ -180。
name: $(".nav-to").attr("name"), // 位置名
address: '', // 地址详情说明
scale: 10, // 地图缩放级别,整形值,范围从1~28。默认为最大
infoUrl: '' // 在查看位置界面底部显示的超链接,可点击跳转
});
});
});
后续补充:
事实证明我真是too young too native = =
背景交代 与真正的公众号进行对接(以上都是用微信提供的测试公众号)时,除了以上步骤还有其他验证是在测试公众号上所不需要的,在此remark一下。
根据要求把服务器的IP加入到IP白名单中,这样就不会被拦截了。
注意:确保可以访问!!!
不管是根目录还是路径目录,都要确保通过所填写的域名都可以访问到这个文件。同时微信所访问的其他路径也需要在这个路径目录下。
踩坑记录:如果程序设置了权限认证,则要记得放开认证,否则永远也无法访问到。