原创声明:本文来源于本人另一博客【微信JS-SDK之地理位置的获取,集成百度地图实现在线地图搜索】原创作品,绝非他处摘取,转载请联系博主

本次讲解微信开发第三篇:获取用户地址位置信息,是非常常用的功能,特别是服务行业公众号,尤为需要该功能,本次讲解的就是如何调用微信JS-SDK接口,获取用户位置信息,并结合百度地铁,实现在线地图搜索,与在线导航。

微信JS-SDK之地理位置的获取与在线导航,集成百度地图实现在线地图搜索_第1张图片微信JS-SDK之地理位置的获取与在线导航,集成百度地图实现在线地图搜索_第2张图片

官方文档地址:https://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html

在这粘贴上二篇博文链接,方便大家访问:

微信公众号开发《一》OAuth2.0网页授权认证获取用户的详细信息,实现自动登陆

微信公众号开发《二》发送模板消息实现消息业务实时通知

1.何为JS-SDK:微信JS-SDK是微信公众平台面向网页开发者提供的基于微信内的网页开发工具包。网页开发者可借助微信高效地使用拍照、选图、语音、位置等手机系统的能力,同时可以直接使用微信分享、扫一扫、卡券、支付等微信特有的能力,为微信用户提供更优质的网页体验。简单来说:就是在自己公众平台后台配置后,可直接调用的功能接口。

那如何配置呢?下面讲解下配置步骤:示例讲解是基于测试公众号,如何使用测试公众号,可以参考第一篇文章

1.在公众号后台绑定域名:测试公众号登录就可看见如下图。正式公众号配置位置:“公众号设置”的“功能设置”里填写“JS接口安全域名”

微信JS-SDK之地理位置的获取与在线导航,集成百度地图实现在线地图搜索_第3张图片

2.页面中引入接口JS文件,下载地址:http://res.wx.qq.com/open/js/jweixin-1.2.0.js

注意:JS最新版本为1.2.0,为了适配IOS开发,最好使用最新版本号JS,1.1.0版本可能会造成有些功能调用失败,有兴趣的可以看看官方文档:https://mp.weixin.qq.com/advanced/wiki?t=t=resource/res_main&id=mp1483682025_enmey

3.获取必须参数:

wx.config({
    debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
    appId: '', // 必填,公众号的唯一标识
    timestamp: , // 必填,生成签名的时间戳
    nonceStr: '', // 必填,生成签名的随机串
    signature: '',// 必填,签名
    jsApiList: [
		'openLocation',
		'getLocation'
	] // 必填,需要使用的JS接口列表,更多接口可看官方文档
});

看调用接口,可知,现在缺timestamp,signature,nonceStr。不用想的太复杂,还是那个原则,没什么获取什么,

获取signature签名,生成签名之前必须先了解一下jsapi_ticket,jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下,jsapi_ticket的有效期为7200秒,通过access_token来获取。由于获取jsapi_ticket的api调用次数非常有限,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存jsapi_ticket。

写个工具类封装获取方法

public class SHA1 { //sha算法
    private final int[] abcde = { 
            0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 
        }; 
    // 摘要数据存储数组 
    private int[] digestInt = new int[5]; 
    // 计算过程中的临时数据存储数组 
    private int[] tmpData = new int[80]; 
    // 计算sha-1摘要 
    private int process_input_bytes(byte[] bytedata) { 
        // 初试化常量 
        System.arraycopy(abcde, 0, digestInt, 0, abcde.length); 
        // 格式化输入字节数组,补10及长度数据 
        byte[] newbyte = byteArrayFormatData(bytedata); 
        // 获取数据摘要计算的数据单元个数 
        int MCount = newbyte.length / 64; 
        // 循环对每个数据单元进行摘要计算 
        for (int pos = 0; pos < MCount; pos++) { 
            // 将每个单元的数据转换成16个整型数据,并保存到tmpData的前16个数组元素中 
            for (int j = 0; j < 16; j++) { 
                tmpData[j] = byteArrayToInt(newbyte, (pos * 64) + (j * 4)); 
            } 
            // 摘要计算函数 
            encrypt(); 
        } 
        return 20; 
    } 
    // 格式化输入字节数组格式 
    private byte[] byteArrayFormatData(byte[] bytedata) { 
        // 补0数量 
        int zeros = 0; 
        // 补位后总位数 
        int size = 0; 
        // 原始数据长度 
        int n = bytedata.length; 
        // 模64后的剩余位数 
        int m = n % 64; 
        // 计算添加0的个数以及添加10后的总长度 
        if (m < 56) { 
            zeros = 55 - m; 
            size = n - m + 64; 
        } else if (m == 56) { 
            zeros = 63; 
            size = n + 8 + 64; 
        } else { 
            zeros = 63 - m + 56; 
            size = (n + 64) - m + 64; 
        } 
        // 补位后生成的新数组内容 
        byte[] newbyte = new byte[size]; 
        // 复制数组的前面部分 
        System.arraycopy(bytedata, 0, newbyte, 0, n); 
        // 获得数组Append数据元素的位置 
        int l = n; 
        // 补1操作 
        newbyte[l++] = (byte) 0x80; 
        // 补0操作 
        for (int i = 0; i < zeros; i++) { 
            newbyte[l++] = (byte) 0x00; 
        } 
        // 计算数据长度,补数据长度位共8字节,长整型 
        long N = (long) n * 8; 
        byte h8 = (byte) (N & 0xFF); 
        byte h7 = (byte) ((N >> 8) & 0xFF); 
        byte h6 = (byte) ((N >> 16) & 0xFF); 
        byte h5 = (byte) ((N >> 24) & 0xFF); 
        byte h4 = (byte) ((N >> 32) & 0xFF); 
        byte h3 = (byte) ((N >> 40) & 0xFF); 
        byte h2 = (byte) ((N >> 48) & 0xFF); 
        byte h1 = (byte) (N >> 56); 
        newbyte[l++] = h1; 
        newbyte[l++] = h2; 
        newbyte[l++] = h3; 
        newbyte[l++] = h4; 
        newbyte[l++] = h5; 
        newbyte[l++] = h6; 
        newbyte[l++] = h7; 
        newbyte[l++] = h8; 
        return newbyte; 
    } 
    private int f1(int x, int y, int z) { 
        return (x & y) | (~x & z); 
    } 
    private int f2(int x, int y, int z) { 
        return x ^ y ^ z; 
    } 
    private int f3(int x, int y, int z) { 
        return (x & y) | (x & z) | (y & z); 
    } 
    private int f4(int x, int y) { 
        return (x << y) | x >>> (32 - y); 
    } 
    // 单元摘要计算函数 
    private void encrypt() { 
        for (int i = 16; i <= 79; i++) { 
            tmpData[i] = f4(tmpData[i - 3] ^ tmpData[i - 8] ^ tmpData[i - 14] ^ 
                    tmpData[i - 16], 1); 
        } 
        int[] tmpabcde = new int[5]; 
        for (int i1 = 0; i1 < tmpabcde.length; i1++) { 
            tmpabcde[i1] = digestInt[i1]; 
        } 
        for (int j = 0; j <= 19; j++) { 
            int tmp = f4(tmpabcde[0], 5) + 
                f1(tmpabcde[1], tmpabcde[2], tmpabcde[3]) + tmpabcde[4] + 
                tmpData[j] + 0x5a827999; 
            tmpabcde[4] = tmpabcde[3]; 
            tmpabcde[3] = tmpabcde[2]; 
            tmpabcde[2] = f4(tmpabcde[1], 30); 
            tmpabcde[1] = tmpabcde[0]; 
            tmpabcde[0] = tmp; 
        } 
        for (int k = 20; k <= 39; k++) { 
            int tmp = f4(tmpabcde[0], 5) + 
                f2(tmpabcde[1], tmpabcde[2], tmpabcde[3]) + tmpabcde[4] + 
                tmpData[k] + 0x6ed9eba1; 
            tmpabcde[4] = tmpabcde[3]; 
            tmpabcde[3] = tmpabcde[2]; 
            tmpabcde[2] = f4(tmpabcde[1], 30); 
            tmpabcde[1] = tmpabcde[0]; 
            tmpabcde[0] = tmp; 
        } 
        for (int l = 40; l <= 59; l++) { 
            int tmp = f4(tmpabcde[0], 5) + 
                f3(tmpabcde[1], tmpabcde[2], tmpabcde[3]) + tmpabcde[4] + 
                tmpData[l] + 0x8f1bbcdc; 
            tmpabcde[4] = tmpabcde[3]; 
            tmpabcde[3] = tmpabcde[2]; 
            tmpabcde[2] = f4(tmpabcde[1], 30); 
            tmpabcde[1] = tmpabcde[0]; 
            tmpabcde[0] = tmp; 
        } 
        for (int m = 60; m <= 79; m++) { 
            int tmp = f4(tmpabcde[0], 5) + 
                f2(tmpabcde[1], tmpabcde[2], tmpabcde[3]) + tmpabcde[4] + 
                tmpData[m] + 0xca62c1d6; 
            tmpabcde[4] = tmpabcde[3]; 
            tmpabcde[3] = tmpabcde[2]; 
            tmpabcde[2] = f4(tmpabcde[1], 30); 
            tmpabcde[1] = tmpabcde[0]; 
            tmpabcde[0] = tmp; 
        } 
        for (int i2 = 0; i2 < tmpabcde.length; i2++) { 
            digestInt[i2] = digestInt[i2] + tmpabcde[i2]; 
        } 
        for (int n = 0; n < tmpData.length; n++) { 
            tmpData[n] = 0; 
        } 
    } 
    // 4字节数组转换为整数 
    private int byteArrayToInt(byte[] bytedata, int i) { 
        return ((bytedata[i] & 0xff) << 24) | ((bytedata[i + 1] & 0xff) << 16) | 
        ((bytedata[i + 2] & 0xff) << 8) | (bytedata[i + 3] & 0xff); 
    } 
    // 整数转换为4字节数组 
    private void intToByteArray(int intValue, byte[] byteData, int i) { 
        byteData[i] = (byte) (intValue >>> 24); 
        byteData[i + 1] = (byte) (intValue >>> 16); 
        byteData[i + 2] = (byte) (intValue >>> 8); 
        byteData[i + 3] = (byte) intValue; 
    } 
    // 将字节转换为十六进制字符串 
    private static String byteToHexString(byte ib) { 
        char[] Digit = { 
                '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 
                'D', 'E', 'F' 
            }; 
        char[] ob = new char[2]; 
        ob[0] = Digit[(ib >>> 4) & 0X0F]; 
        ob[1] = Digit[ib & 0X0F]; 
        String s = new String(ob); 
        return s; 
    } 
    // 将字节数组转换为十六进制字符串 
    private static String byteArrayToHexString(byte[] bytearray) { 
        String strDigest = ""; 
        for (int i = 0; i < bytearray.length; i++) { 
            strDigest += byteToHexString(bytearray[i]); 
        } 
        return strDigest; 
    } 
    // 计算sha-1摘要,返回相应的字节数组 
    public byte[] getDigestOfBytes(byte[] byteData) { 
        process_input_bytes(byteData); 
        byte[] digest = new byte[20]; 
        for (int i = 0; i < digestInt.length; i++) { 
            intToByteArray(digestInt[i], digest, i * 4); 
        } 
        return digest; 
    } 
    // 计算sha-1摘要,返回相应的十六进制字符串 
    public String getDigestOfString(byte[] byteData) { 
        return byteArrayToHexString(getDigestOfBytes(byteData)); 
    } 
    public static void main(String[] args) { 
        String data = "123456"; 
        System.out.println(data); 
        String digest = new SHA1().getDigestOfString(data.getBytes()); 
        System.out.println(digest);
       
       // System.out.println( ToMD5.convertSHA1(data).toUpperCase());
    } 
}

 

/**
 * Ticket封装类
 * @author lh
 */
public class Ticket {
	private String errcode;
	private String errmsg;
	private String ticket;
	private String expires_in;
	//省略get,set方法
}
/**
 * Signature封装类
 * @author lh
 */
public class SignatureInfo {
	private String signature;
	private String timestamp;
	private String noncestr;
	private String url;
	//省略get,set方法
}

获取ticket

/**
 * 获取ticket
 * @param accessToken
 * @return
 */
public static Ticket getTicket(String accessToken){
	Ticket ticket = new Ticket();
	String getTicket = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";
	String url = getTicket.replace("ACCESS_TOKEN",accessToken);
	JSONObject jsonObject = httpRequest(url, "POST", null);
	if (null != jsonObject) {
		if (0 != jsonObject.getInt("errcode")) {
			log.error("获取ticket失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg"));
		}else{
			ticket.setErrcode(jsonObject.getString("errcode"));
			ticket.setErrmsg(jsonObject.getString("errmsg"));
			ticket.setExpires_in(jsonObject.getString("expires_in"));
			ticket.setTicket(jsonObject.getString("ticket"));
		}
	}
	return ticket;
}

获取Signature方法

/**
 * 签名算法
 * @param ticket
 * @return
 */
public static SignatureInfo getSignature(SignatureInfo sign,Ticket ticket){
	String data = "jsapi_ticket="+ticket.getTicket()+"&noncestr="+sign.getNoncestr()+"×tamp="+sign.getTimestamp()+"&url="+sign.getUrl();
	String signature =  new SHA1().getDigestOfString(data.getBytes()); 
	sign.setSignature(signature);
	log.info("signature="+sign.getSignature());
	return sign;
}

最后综合上面方法,封装主函数

public String jssdk_demo(HttpServletRequest request,HttpServletResponse response){
	String param = request.getQueryString();//获取请求参数
	String url = request.getServletPath();//获取请求路径(不带参数)
	if(param!=null){
		url = url+"?"+param;//组合成完整请求URL
	}
	String projectnameP = request.getContextPath();
    String projectName = projectnameP.substring(projectnameP.lastIndexOf('/')+1,projectnameP.length());  //获取工程名,如testW
	if(!"".equals(projectName)){
		projectName ="/"+projectName;
	}
	String port = String.valueOf(request.getServerPort());//获取端口号
	if(!"80".equals(port)){//不是80端口时需加端口号
		port = ":"+port;
	}else{
		port = "";
	}
	String strBackUrl = "http://" + request.getServerName()+port+projectName+url;//完整的请求路径http://192.168.1.117/testW/+路径
	AccessToken token = null;
	if(TimedTask.accessToken==null || TimedTask.accessToken.getToken()==""){//token失效,重新获取,获取方法参考第二篇博文,在这由于篇幅问题暂不列出
		token = WeixinUtil.getAccessToken(TimedTask.appid, TimedTask.appsecret);/
	}else{
		token = TimedTask.accessToken;
	}
	Ticket ticket = null;
	if(TimedTask.ticket ==null || TimedTask.ticket.getTicket()==""){
		ticket = WeixinUtil.getTicket(token.getToken());//获取ticket
	}else{
		ticket = TimedTask.ticket;
	}
	SignatureInfo siInfo = new SignatureInfo();
	siInfo.setNoncestr(RandomStringUtils.randomAlphanumeric(20));//随机字符串
	siInfo.setTimestamp(String.valueOf(System.currentTimeMillis()));//随机时间截
	siInfo.setUrl(strBackUrl);
	siInfo = WeixinUtil.getSignature(siInfo, ticket);
	request.setAttribute("siInfo",siInfo);
	return "weixin/jssdk_demo";
}

到此需要准备的数据就已经全部完成了,现在我们看看前台jssdk_demo.jsp页面是如何调用的

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>





jssdk_demo




//百度地图没有密匙的可以去官网申请

 









var phoneWidth = parseInt(window.screen.width);
var phoneScale = phoneWidth/640;
var ua = navigator.userAgent;
if (/Android (\d+\.\d+)/.test(ua)){
    var version = parseFloat(RegExp.$1);
    if(version>2.3){
        document.write("");
    }else{
        document.write("");
    }
}else{
    document.write("");
}
$(function(){
	$(".nav a").click(function(){
		$('.nav a').removeClass('active');
		$(this).addClass('active');
		var nav = $(this).attr("nav");
		$("section div[class='desc']").each(function(i,val){
			if($(this).attr("nav")==nav){
				$(this).show();
			}else{
				$(this).hide();
			}
		});
	});
});
var signature = $("#signature").val();
var nonceStr = $("#nonceStr").val();
var timestamp = $("#timestamp").val();
var weixinOperId = $("#weixinOperId").val();
wx.config({
    debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
    appId: weixinOperId, // 必填,公众号的唯一标识
    timestamp:timestamp, // 必填,生成签名的时间戳
    nonceStr: nonceStr, // 必填,生成签名的随机串
    signature:signature,// 必填,签名
    jsApiList: [ 
        'openLocation',
		'getLocation'
    ] // 必填,需要使用的JS接口列表,所有JS接口列表见官方文档附录2
});
wx.ready(function(){
	getLocation();
});
wx.error(function(res){
	//alert("error");
});


$(function () {
	$("#wdialogTitle2").text("信息提示");
    $("#wdialogConfirm2").click(function(i,val){
    	$("#wdialog2").hide();
    });
    $(".warn_message").text("");
    //刷新定位
    $(".SelectCityWrap .cityTit .r").click(function () {
        $(this).addClass("hover");
        getLocation();
    });
    $("b.search").click(function () {
        searchMap($("#indexSearchBox").val());
        return false;
    });
});

var latitude,longitude;
//获取当前地址
function getLocation(){
	wx.getLocation({
	    type: 'gcj02', // 默认为wgs84的gps坐标,如果要返回直接给openLocation用的火星坐标,可传入'gcj02'
	    success: function (res) {
	        latitude = res.latitude; // 纬度,浮点数,范围为90 ~ -90
	        longitude = res.longitude; // 经度,浮点数,范围为180 ~ -180。
	        var speed = res.speed; // 速度,以米/每秒计
	        var accuracy = res.accuracy; // 位置精度
	        getAddressInfo2(longitude,latitude);
	    },cancel: function (res){
	        $("#wdialogContent2").text("用户拒绝授权获取地理位置");
        	$("#wdialog2").show();
	    }
	});
}

var addId = "choseAdd";
var longiText = "longitude";
var latiText = "latitude";
//打开百度地图并标记当前定位地址
function showMapArea(){
	var myGeo = new BMap.Geocoder();
	var pt = new BMap.Point(longitude,latitude);
 	translateCallback2 = function (point){
 		createBaiduMap(point.lng,point.lat,1);
	};
    setTimeout(function(){
    	BMap.Convertor.translate(pt,2,translateCallback2);     //火星经纬度转成百度坐标,2变成0时,则是默认的wgs84的gps坐标转换成百度坐标
	}, 100);
}
//调用百度地图API,经纬度转化成实际地址
function getAddressInfo2(lon,lat) {
	var myGeo = new BMap.Geocoder();
	var pt = new BMap.Point(lon,lat);
 	translateCallback2 = function (point){
		myGeo.getLocation(point, function(rs) {
			var addComp = rs.addressComponents;
			var addr = addComp.province+ addComp.city + addComp.district+ addComp.street+ addComp.streetNumber;
			$("#nowAdd").text(addr);
		});
	};
    setTimeout(function(){
    	BMap.Convertor.translate(pt,2,translateCallback2);     //火星经纬度转成百度坐标
	}, 100);
}
function returnAdd(){
	$("#addressArea").show();
	$("#mapArea").hide();
}
function confirmC(){
	var address = $("#choseAdd").text();
	var longiText = $("#longitude").val();
	var latiText = $("#latitude").val();
	if(latiText=="" || longitude==""){
		$("#wdialogContent2").text("请先选择您当前位置");
     	$("#wdialog2").show();
	}
}
function getNavi(){
	wx.openLocation({
	    latitude: latitude, // 纬度,浮点数,范围为90 ~ -90
	    longitude: longitude, // 经度,浮点数,范围为180 ~ -180。
	    name: '我现在在这里', // 位置名
	    address: $("#nowAdd").text(), // 地址详情说明
	    scale: 20, // 地图缩放级别,×××值,范围从1~28。默认为最大
	    infoUrl: 'https://www.baidu.com/' // 在查看位置界面底部显示的超链接,可点击跳转
	});
}


	
		
		    
		        当前地址:${nowAdd}
		        
		    
		    
				
			    	当前地址导航
			    	地图选择