微信公众号开发,在公众号界面调用微信JS SDK接口,实现媒体(图片、语言、视频)上传,分析等功能之前,需要获取Config接口注入权限验证。
微信Config接口注入权限验证,需要如下参数:
wx.config({
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: '', // 必填,公众号的唯一标识
timestamp: , // 必填,生成签名的时间戳
nonceStr: '', // 必填,生成签名的随机串
signature: '',// 必填,签名
jsApiList: [] // 必填,需要使用的JS接口列表
});
那么如何获得Config接口注入权限验证的相关参数:其中appId,timestamp,nonceStr是自己后台随机生成的,但是 signatrue签名生成需要jsapi_ticket,并按照指定顺序将拼接好的参数字符串进行加密,生成签名,下面首先了解如何生成jsapi_ticket:
jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下,jsapi_ticket的有效期为7200秒,通过access_token来获取。
因为Access_token,Jsapi_ticket是有次数限制的,所以需要将数据生成并保持在缓存中,或者数据库中,过期之后,重新拉取。
下面给出Demo示例,仅供参考:
package com.inspur.tax.api.login.controller;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.bean.WxAccessToken;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.SignUtils;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.alibaba.fastjson.JSONObject;
import com.inspur.tax.api.login.entity.LoginUser;
import com.inspur.tax.api.login.service.ILoginUserService;
import com.inspur.tax.api.sms.cache.CacheManagerImpl;
import com.inspur.tax.api.util.WeixinUtil;
import com.inspur.tax.api.common.Const;
import com.inspur.tax.utils.DateUtils;
import com.inspur.tax.utils.FastJsonUtils;
import com.inspur.tax.utils.JwtUtil;
import javax.servlet.http.HttpServletRequest;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Slf4j
@AllArgsConstructor
@RestController
@RequestMapping("/WX/JsapiTicket")
public class WxJsapiTicketController {
private final WxMpService wxService;
@Autowired
private ILoginUserService loginUserService;
static CacheManagerImpl cacheManagerImpl = new CacheManagerImpl();
@RequestMapping("/wxConfig/{appid}")
public Map index(@PathVariable String appid,@RequestParam String urlx) {
Map wxConfig = new HashMap();
try {
/**
* 生成签名之前必须先了解一下jsapi_ticket,jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下,
* jsapi_ticket的有效期为7200秒,通过access_token来获取。由于获取jsapi_ticket的api调用次数非常有限,
* 频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存jsapi_ticket 。
参考以下文档获取access_token(有效期7200秒,开发者必须在自己的服务全局缓存access_token):
https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html
用第一步拿到的access_token 采用http GET方式请求获得jsapi_ticket(有效期7200秒,开发者必须在自己的服务全局缓存jsapi_ticket):
https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi
* */
//access_token 和jsapi_token 都是需要本地缓存的
//通过appid获得secret
String secret = Const.SERVICE_WxConfig.get(appid);
//判断是否在缓存中
String token2 = "";
boolean flagTimeOut = false;
boolean flagExist = false;
flagExist = cacheManagerImpl.isContains(appid+"AC");
if(flagExist) {
//判断是否过期
flagTimeOut = cacheManagerImpl.isTimeOut(appid+"AC");
if(flagTimeOut) {
cacheManagerImpl.clearByKey(appid+"AC");
}
}
if(!flagTimeOut && flagExist) {
token2 = cacheManagerImpl.getCacheByKey(appid+"AC").getDatas().toString();
}else {
String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+ appid + "&secret=" + secret;
JSONObject json = WeixinUtil.httpRequest(url, "GET", null);
token2=json.getString("access_token");
cacheManagerImpl.putCache(appid+"AC", token2, 6500*1000);
}
String jsapi_token = "";
flagExist = cacheManagerImpl.isContains(appid+"JT");
if(flagExist) {
//判断是否过期
flagTimeOut = cacheManagerImpl.isTimeOut(appid+"JT");
if(flagTimeOut) {
cacheManagerImpl.clearByKey(appid+"JT");
}
}
if(!flagTimeOut && flagExist) {
jsapi_token = cacheManagerImpl.getCacheByKey(appid+"JT").getDatas().toString();
}else {
String jsapiUrl = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+ token2 + "&type=jsapi";
JSONObject jsapiJson = WeixinUtil.httpRequest(jsapiUrl, "GET", null);
if (jsapiJson != null) {
jsapi_token = jsapiJson.getString("ticket");
}
cacheManagerImpl.putCache(appid+"JT", jsapi_token, 6500*1000);
}
wxConfig = WeixinUtil.getWxConfig(urlx, jsapi_token,appid);
} catch (Exception e) {
log.error("JsapiTicket,异常日志",e);
}
return wxConfig;
}
}
package com.inspur.tax.api.util;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import com.alibaba.fastjson.JSONObject;
public class WeixinUtil {
/**
* 方法名:httpRequest
* 详述:发送http请求
* 开发人员:souvc
* 创建时间:2016-1-5
* @param requestUrl
* @param requestMethod
* @param outputStr
* @return 说明返回值含义
* @throws 说明发生此异常的条件
*/
public static JSONObject httpRequest(String requestUrl,String requestMethod, String outputStr) {
JSONObject jsonObject = null;
StringBuffer buffer = new StringBuffer();
try {
URL url = new URL(requestUrl);
HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();
httpUrlConn.setDoOutput(true);
httpUrlConn.setDoInput(true);
httpUrlConn.setUseCaches(false);
httpUrlConn.setRequestMethod(requestMethod);
if ("GET".equalsIgnoreCase(requestMethod))
httpUrlConn.connect();
if (null != outputStr) {
OutputStream outputStream = httpUrlConn.getOutputStream();
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
InputStream inputStream = httpUrlConn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
inputStream = null;
httpUrlConn.disconnect();
jsonObject = JSONObject.parseObject(buffer.toString());
} catch (ConnectException ce) {
ce.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return jsonObject;
}
/**
* 方法名:getWxConfig
* 详述:获取微信的配置信息
* 开发人员:souvc
* 创建时间:2016-1-5
* @param request
* @return 说明返回值含义
* @throws 说明发生此异常的条件
*/
public static Map getWxConfig(String urlx,String jsapi,String appid) {
Map ret = new HashMap();
//HttpSession session=request.getSession();
String appId = appid; // 必填,公众号的唯一标识
String secret = "";
String requestUrl = urlx;
String timestamp = Long.toString(System.currentTimeMillis() / 1000); // 必填,生成签名的时间戳
String nonceStr = UUID.randomUUID().toString(); // 必填,生成签名的随机串
String signature = "";
// 注意这里参数名必须全部小写,且必须有序
System.out.println("jsapi_ticket:"+jsapi);
String sign = "jsapi_ticket=" + jsapi + "&noncestr=" + nonceStr+ "×tamp=" + timestamp + "&url=" + requestUrl;
try {
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
crypt.update(sign.getBytes("UTF-8"));
signature = byteToHex(crypt.digest());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
ret.put("appId", appId);
ret.put("timestamp", timestamp);
ret.put("nonceStr", nonceStr);
ret.put("signature", signature);
return ret;
}
/**
* 方法名:byteToHex
* 详述:字符串加密辅助方法
* 开发人员:souvc
* 创建时间:2016-1-5
* @param hash
* @return 说明返回值含义
* @throws 说明发生此异常的条件
*/
private static String byteToHex(final byte[] hash) {
Formatter formatter = new Formatter();
for (byte b : hash) {
formatter.format("%02x", b);
}
String result = formatter.toString();
formatter.close();
return result;
}
}
通过上面Demo,我们可以获取微信Config接口注入权限验证所需要的参数;然后就可以按照以下操作,调用微信JSSDK接口
步骤一:绑定域名
先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。
备注:登录后可在“开发者中心”查看对应的接口权限。
步骤二:引入JS文件
在需要调用JS接口的页面引入如下JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.6.0.js
如需进一步提升服务稳定性,当上述资源不可访问时,可改访问:http://res2.wx.qq.com/open/js/jweixin-1.6.0.js (支持https)。
备注:支持使用 AMD/CMD 标准模块加载方法加载
步骤三:通过config接口注入权限验证配置
所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用(同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用,目前Android微信客户端不支持pushState的H5新特性,所以使用pushState来实现web app的页面会导致签名失败,此问题会在Android6.2中修复)。
wx.config({
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: '', // 必填,公众号的唯一标识
timestamp: , // 必填,生成签名的时间戳
nonceStr: '', // 必填,生成签名的随机串
signature: '',// 必填,签名
jsApiList: [] // 必填,需要使用的JS接口列表
});
步骤四:通过ready接口处理成功验证
wx.ready(function(){
// config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
});
步骤五:通过error接口处理失败验证
wx.error(function(res){
// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
});
其他方法请参考官方文档:
https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html
参考文档:
https://www.cnblogs.com/haw2106/p/10345081.html
关注公众号的都知道: