Wiwiz与微信Wi-Fi硬件鉴权协议对接案例说明(2015-10)

[2021年5月] 注意,微信公众平台已于2021年5月回收“微信连Wi-Fi”的能力,目前改功能已不可用。

新版微信提供了微信连WiFi功能。现在,通过微信可以实现下图这种更便捷的微信WiFi认证方式。

开发者可以利用微信公众平台的Wi-Fi硬件鉴权协议接口和Wiwiz系统的相关开发接口,轻松地实现上述认证方式。

步骤如下:
1.在微信公众平台中创建门店,开通微信连WiFi权限,并添加设备。设置SSID,并获取shopId、appId、secretKey。注意:添加设备时“设备类型”应选为“portal型设备”,“接入方式”选为“设备改造后接入”,如下图。

2.在Wiwiz Web面板中设置热点,将认证方式设为“电子招待券”,并添加一个电子招待券“1min”,并设置使用时长为1分钟。如下图。

3. 将认证页面类型设置为“自定义认证页面HTML”,将AuthPage.htm的源码复制到认证页面设置的“自定义HTML”项目中。
4. 将认证后页面类型设置为“自定义HTML”,将PostAuth.htm的源码复制到认证后页面设置的“自定义HTML”项目中。
5. 将WeixinListener.jsp保存与Web服务器(须支持JSP)。

注意事项:
1.PostAuth.htm和WeixinListener.jsp源码中的部分参数请按源码中的说明做替换处理。
2. v6.2.5以前的微信版本对该认证方式可能会有问题。

以下是本案例的源码:
AuthPage.htm

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Language" content="zh">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-Control" content="no-cache">
<meta name="viewport" content="width=device-width; initial-scale=1.0">
<title> 微信连WiFi </title>
<!-- 必须引入AuthPageScript.js -->
<script src="http://cp.wiwiz.com/as/AuthPageScript.js"></script>
<script>
/* 回调函数。“获取验证码”按钮按下后,将自动调用此函数。可根据code值自行改写该函数。 */
function WiwizSmsVerifyMsg(code) {
	if	  (code == "-1") {
		alert("手机号码不可为空!");
	} else if(code == "0") {
		alert("验证码已通过短信发送至您的手机,请注意查收。然后请在认证页面输入验证码。");
	} else if(code == "1") {
		alert("该热点不允许进行手机号码验证。如有疑问请您联系热点管理员。");
	} else if(code == "2") {
		alert("该热点不允许进行手机号码验证。如有疑问请您联系热点管理员。");
	} else if(code == "3") {
		alert("该手机号码不允许进行验证。如有疑问请您联系热点管理员。");
	} else if(code == "4") {
		alert("手机号码验证过于频繁,请稍后再试。");
	} else if(code == "5") {
		alert("该手机号码进行验证次数已超过今日上限。");
	} else if(code == "6") {
		alert("热点认证服务已暂停,不可进行手机验证。");
	} else if(code == "7") {
		alert("该热点手机验证次数已超过配额。请联系热点管理员。");
	} else if(code == "8") {
		alert("请求已超时,请刷新认证页面。");
	} else if(code == "9") {
		alert("请使用上一次通过短信接收到的验证码。");
	} else if(code == "99") {
		alert("验证短信发送失败。请检查手机号码的有效性,或联系热点管理员。");
	} else if(code == "999") {
		alert("系统异常,验证短信发送失败。请联系热点管理员。");
	} else {
		alert("系统异常。请联系热点管理员。");
	}
}
/* 回调函数。“认证”按钮按下后,如报错将自动调用此函数。可根据code值自行改写该函数。 */
function WiwizAuthPageError(code) {
	if	   (code == 1) {
		alert("您无法使用此网络,除非您认同此协议条款。");
	} else if(code == 2) {
		alert("请输入用户名。");
	} else if(code == 3) {
		alert("用户名或密码错误。");
	} else if(code == 4) {
		alert("电子招待券无效。");
	} else if(code == 5) {
		alert("超过该电子招待券的最大使用人数限制。");
	} else if(code == 6) {
		alert("超过最大在线用户数。");
	} else if(code == 7) {
		alert("请输入手机号码。");
	} else if(code == 8) {
		alert("热点已停用。");
	} else if(code == 32) {
		alert("账户存在异常,暂时锁定中。");
	} else if(code == 35) {
		alert("手机验证码错误或已超时。");
	} else if(code == 36) {
		alert("该手机号码不允许进行验证。如有疑问请您联系热点管理员。");
	} else if(code == 37) {
		alert("该手机号码进行验证次数已超过今日上限。");
	} else {
		alert("未知错误。错误码:"+ code);
	}
}
 
/* 认证完成前先调用此函数 */
function WiwizBeforeAuthDone() {
}
</script>
</head>
<body>
<form name="myform" id="myform" action="" method="post">
 
<!-- 设置时长为1分钟的电子招待券 -->
<input name="voucher" id="voucher" type="hidden" value="1min" />
 
<center>
<br>
 
微信连WiFi 认证页面示例
<br><br><br>
 
<!-- 用于开始触发认证的按钮,onclick事件必须调用WiwizStartAuth()函数 -->
<input type="button" name="login" value="   微信连WiFi   " onclick="WiwizStartAuth();" />
 
</center>
</form>
</body></html>

PostAuth.htm

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Language" content="zh">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-Control" content="no-cache">
<meta name="viewport" content="width=device-width; initial-scale=1.0">
<title> 正在打开微信 </title>
<!--MD5算法-->
<script>
var hexcase=0;var b64pad="";var chrsz=8;function hex_md5(s){return binl2hex(core_md5(str2binl(s),s.length*chrsz))}function b64_md5(s){return binl2b64(core_md5(str2binl(s),s.length*chrsz))}function str_md5(s){return binl2str(core_md5(str2binl(s),s.length*chrsz))}function hex_hmac_md5(key,data){return binl2hex(core_hmac_md5(key,data))}function b64_hmac_md5(key,data){return binl2b64(core_hmac_md5(key,data))}function str_hmac_md5(key,data){return binl2str(core_hmac_md5(key,data))}function md5_vm_test(){return hex_md5("abc")=="900150983cd24fb0d6963f7d28e17f72"}function core_md5(x,len){x[len>>5]|=0x80<<((len)%32);x[(((len+64)>>>9)<<4)+14]=len;var a=1732584193;var b=-271733879;var c=-1732584194;var d=271733878;for(var i=0;i<x.length;i+=16){var olda=a;var oldb=b;var oldc=c;var oldd=d;a=md5_ff(a,b,c,d,x[i+0],7,-680876936);d=md5_ff(d,a,b,c,x[i+1],12,-389564586);c=md5_ff(c,d,a,b,x[i+2],17,606105819);b=md5_ff(b,c,d,a,x[i+3],22,-1044525330);a=md5_ff(a,b,c,d,x[i+4],7,-176418897);d=md5_ff(d,a,b,c,x[i+5],12,1200080426);c=md5_ff(c,d,a,b,x[i+6],17,-1473231341);b=md5_ff(b,c,d,a,x[i+7],22,-45705983);a=md5_ff(a,b,c,d,x[i+8],7,1770035416);d=md5_ff(d,a,b,c,x[i+9],12,-1958414417);c=md5_ff(c,d,a,b,x[i+10],17,-42063);b=md5_ff(b,c,d,a,x[i+11],22,-1990404162);a=md5_ff(a,b,c,d,x[i+12],7,1804603682);d=md5_ff(d,a,b,c,x[i+13],12,-40341101);c=md5_ff(c,d,a,b,x[i+14],17,-1502002290);b=md5_ff(b,c,d,a,x[i+15],22,1236535329);a=md5_gg(a,b,c,d,x[i+1],5,-165796510);d=md5_gg(d,a,b,c,x[i+6],9,-1069501632);c=md5_gg(c,d,a,b,x[i+11],14,643717713);b=md5_gg(b,c,d,a,x[i+0],20,-373897302);a=md5_gg(a,b,c,d,x[i+5],5,-701558691);d=md5_gg(d,a,b,c,x[i+10],9,38016083);c=md5_gg(c,d,a,b,x[i+15],14,-660478335);b=md5_gg(b,c,d,a,x[i+4],20,-405537848);a=md5_gg(a,b,c,d,x[i+9],5,568446438);d=md5_gg(d,a,b,c,x[i+14],9,-1019803690);c=md5_gg(c,d,a,b,x[i+3],14,-187363961);b=md5_gg(b,c,d,a,x[i+8],20,1163531501);a=md5_gg(a,b,c,d,x[i+13],5,-1444681467);d=md5_gg(d,a,b,c,x[i+2],9,-51403784);c=md5_gg(c,d,a,b,x[i+7],14,1735328473);b=md5_gg(b,c,d,a,x[i+12],20,-1926607734);a=md5_hh(a,b,c,d,x[i+5],4,-378558);d=md5_hh(d,a,b,c,x[i+8],11,-2022574463);c=md5_hh(c,d,a,b,x[i+11],16,1839030562);b=md5_hh(b,c,d,a,x[i+14],23,-35309556);a=md5_hh(a,b,c,d,x[i+1],4,-1530992060);d=md5_hh(d,a,b,c,x[i+4],11,1272893353);c=md5_hh(c,d,a,b,x[i+7],16,-155497632);b=md5_hh(b,c,d,a,x[i+10],23,-1094730640);a=md5_hh(a,b,c,d,x[i+13],4,681279174);d=md5_hh(d,a,b,c,x[i+0],11,-358537222);c=md5_hh(c,d,a,b,x[i+3],16,-722521979);b=md5_hh(b,c,d,a,x[i+6],23,76029189);a=md5_hh(a,b,c,d,x[i+9],4,-640364487);d=md5_hh(d,a,b,c,x[i+12],11,-421815835);c=md5_hh(c,d,a,b,x[i+15],16,530742520);b=md5_hh(b,c,d,a,x[i+2],23,-995338651);a=md5_ii(a,b,c,d,x[i+0],6,-198630844);d=md5_ii(d,a,b,c,x[i+7],10,1126891415);c=md5_ii(c,d,a,b,x[i+14],15,-1416354905);b=md5_ii(b,c,d,a,x[i+5],21,-57434055);a=md5_ii(a,b,c,d,x[i+12],6,1700485571);d=md5_ii(d,a,b,c,x[i+3],10,-1894986606);c=md5_ii(c,d,a,b,x[i+10],15,-1051523);b=md5_ii(b,c,d,a,x[i+1],21,-2054922799);a=md5_ii(a,b,c,d,x[i+8],6,1873313359);d=md5_ii(d,a,b,c,x[i+15],10,-30611744);c=md5_ii(c,d,a,b,x[i+6],15,-1560198380);b=md5_ii(b,c,d,a,x[i+13],21,1309151649);a=md5_ii(a,b,c,d,x[i+4],6,-145523070);d=md5_ii(d,a,b,c,x[i+11],10,-1120210379);c=md5_ii(c,d,a,b,x[i+2],15,718787259);b=md5_ii(b,c,d,a,x[i+9],21,-343485551);a=safe_add(a,olda);b=safe_add(b,oldb);c=safe_add(c,oldc);d=safe_add(d,oldd)}return Array(a,b,c,d)}function md5_cmn(q,a,b,x,s,t){return safe_add(bit_rol(safe_add(safe_add(a,q),safe_add(x,t)),s),b)}function md5_ff(a,b,c,d,x,s,t){return md5_cmn((b&c)|((~b)&d),a,b,x,s,t)}function md5_gg(a,b,c,d,x,s,t){return md5_cmn((b&d)|(c&(~d)),a,b,x,s,t)}function md5_hh(a,b,c,d,x,s,t){return md5_cmn(b^c^d,a,b,x,s,t)}function md5_ii(a,b,c,d,x,s,t){return md5_cmn(c^(b|(~d)),a,b,x,s,t)}function core_hmac_md5(key,data){var bkey=str2binl(key);if(bkey.length>16)bkey=core_md5(bkey,key.length*chrsz);var ipad=Array(16),opad=Array(16);for(var i=0;i<16;i++){ipad[i]=bkey[i]^0x36363636;opad[i]=bkey[i]^0x5C5C5C5C}var hash=core_md5(ipad.concat(str2binl(data)),512+data.length*chrsz);return core_md5(opad.concat(hash),512+128)}function safe_add(x,y){var lsw=(x&0xFFFF)+(y&0xFFFF);var msw=(x>>16)+(y>>16)+(lsw>>16);return(msw<<16)|(lsw&0xFFFF)}function bit_rol(num,cnt){return(num<<cnt)|(num>>>(32-cnt))}function str2binl(str){var bin=Array();var mask=(1<<chrsz)-1;for(var i=0;i<str.length*chrsz;i+=chrsz)bin[i>>5]|=(str.charCodeAt(i/chrsz)&mask)<<(i%32);return bin}function binl2str(bin){var str="";var mask=(1<<chrsz)-1;for(var i=0;i<bin.length*32;i+=chrsz)str+=String.fromCharCode((bin[i>>5]>>>(i%32))&mask);return str}function binl2hex(binarray){var hex_tab=hexcase?"0123456789ABCDEF":"0123456789abcdef";var str="";for(var i=0;i<binarray.length*4;i++){str+=hex_tab.charAt((binarray[i>>2]>>((i%4)*8+4))&0xF)+hex_tab.charAt((binarray[i>>2]>>((i%4)*8))&0xF)}return str}function binl2b64(binarray){var tab="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";var str="";for(var i=0;i<binarray.length*4;i+=3){var triplet=(((binarray[i>>2]>>8*(i%4))&0xFF)<<16)|(((binarray[i+1>>2]>>8*((i+1)%4))&0xFF)<<8)|((binarray[i+2>>2]>>8*((i+2)%4))&0xFF);for(var j=0;j<4;j++){if(i*8+j*6>binarray.length*32)str+=b64pad;else str+=tab.charAt((triplet>>6*(3-j))&0x3F)}}return str}
</script>
<!--微信组件--> 
<script type="text/javascript" src="https://wifi.weixin.qq.com/resources/js/wechatticket/wechatutil.js" ></script>
<script>
/* 获取URL参数值 */
function getUrlParameter(name) {
   var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)","i");
   var r = window.location.search.substr(1).match(reg);
   if (r!=null) return decodeURIComponent(r[2]); return null;
}
 
/* 呼起微信 */
function doWeRedirect(){
 
	//在微信公众平台,创建门店并启用微信连WiFi权限后,可获取以下四个参数。
	var appId = 'wx310eea55b32cbec5';	
	var ssid = 'Wiwiz';
	var shop_id = '7361374';
	var secretkey = '125281cf148fd9c776e4e963df2d9489';
 
	var extend = getUrlParameter("t");	//Wiwiz传入的tokencode
	var mac = getUrlParameter("mac");	//Wiwiz传入的终端MAC地址
 
	var authUrl = 'http://192.168.2.110/as/test/wxtest/WeixinListener.jsp';	//请替换为你的服务器实际地址
 
	var timeStamp = new Date().getTime();
	var sign= hex_md5(appId + extend + timeStamp + shop_id + authUrl + mac + ssid + secretkey);
 
	Wechat_GotoRedirect(
		appId ,     
		extend ,    
		timeStamp,
		sign,      
		shop_id,  
		authUrl,
		mac,
		ssid,
		''
	);
}
</script>
</head>
<body onload="doWeRedirect();">
<center>
<br>
正在打开微信,请稍候...
 
</center>
</body>
</html>

WeixinListener.jsp

<%@ page import="java.io.*"%><%@ page import="java.util.*"%><%@ page import="java.text.*"%><%@ page import="java.net.*"%><%!
/* 获取当前时间 */
public static String getTime() {
	Calendar c = Calendar.getInstance();
	Date dt = c.getTime();
	SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	return sdf.format(dt);
}
 
/* 时间加减 */
public static String addTime(String time, int field, int amount) throws ParseException {
	SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");	
 
	Date dCurEndTime = sdf.parse(time);
 
	Calendar c = Calendar.getInstance();
	c.setTime(dCurEndTime);
 
	c.add(field, amount);
 
	Date dt = c.getTime();
	return sdf.format(dt);
}	
%><%
 
String extend = request.getParameter("extend");	//调用呼起微信JSAPI时传递的extend参数(Wiwiz传入的tokencode) 
String openId = request.getParameter("openId");	//用户的微信openId
String tid = request.getParameter("tid");		//为加密后的用户手机号码(仅作网监部门备案使用)
 
String userkey = "XXXXXXXXXXXXXXX";			//Wiwiz平台的User Key(!!!注意:请替换为用户的实际值!!!)
 
//------- Debug --------
System.out.println("[WeixinListener.jsp] extend = "+ extend);
System.out.println("[WeixinListener.jsp] openId = "+ openId);
System.out.println("[WeixinListener.jsp] tid = "+ tid);
//----------------------
 
if(extend != null) {
	//String set_endtime=URLEncoder.encode("", "UTF-8");	//不限上网时长
	String set_endtime=URLEncoder.encode(addTime(getTime(), Calendar.MINUTE, 5), "UTF-8");	//允许上网5分钟
 
	//调用Wiwiz Auth API的“认证连接的断开时间动态设置接口”
	URL url = new URL("http://cp.wiwiz.com/as/s/login2/?set_endtime="+ set_endtime +"&t="+ extend +"&userkey="+ userkey);
	HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
	httpConn.connect();
 
	BufferedReader in = new BufferedReader(
			new InputStreamReader(httpConn.getInputStream(), "UTF-8"));
	String responseStr = "";
	String line;
	while ((line = in.readLine()) != null) {
		responseStr += line;
	}
	in.close();
	httpConn.disconnect();
 
	//------- Debug --------
	System.out.println("[WeixinListener.jsp] responseStr = "+ responseStr);
	//----------------------
 
	if(responseStr.startsWith("ERR")) {
		//如遇到错误则返回HTTP403,使告知微信连接失败。
		response.setStatus(403);
	} else {
		//如成功,则返回HTTP200,使告知微信连接成功。
		response.setStatus(200);
	}
}
%>

Comments are closed.