1.微信测试公众号申请:
微信公众平台http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
2.记录下申请的测试公众号的appid和appsecret
3.配置内网穿透:本篇使用的是natapp NATAPP-内网穿透 基于ngrok的国内高速内网映射工具
设置本机的内网穿透代理,使用免费版。
4.配置js安全域名:其中js安全域名的设置不能携带http://。
5.扫码关注测试公众号:因为测试公众号,在手机端测试时要关注公众号。
1.vue前端框架
2.jdk 1.8
3.springboot
1.后端代码逻辑主要是:根据appid及appsecret获取token,再根据token获取jsapi_ticket,然后再根据jsapi_ticket,noncestr、timestamp、url拼接成字符串,然后加密生成签名,然后将url(调用扫一扫的页面地址)、jsapi_ticket、nonceStr、timestamp、signature、appId封装后传给前端进行微信验证。
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class HttpClient {
public static String doGet(String url, Map param) {
// 创建Httpclient对象
CloseableHttpClient httpclient = HttpClients.createDefault();
String resultString = "";
CloseableHttpResponse response = null;
try {
// 创建uri
URIBuilder builder = new URIBuilder(url);
if (param != null) {
for (String key : param.keySet()) {
builder.addParameter(key, param.get(key));
}
}
URI uri = builder.build();
// 创建http GET请求
HttpGet httpGet = new HttpGet(uri);
// 执行请求
response = httpclient.execute(httpGet);
// 判断返回状态是否为200
if (response.getStatusLine().getStatusCode() == 200) {
resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (response != null) {
response.close();
}
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
public static String doGet(String url) {
return doGet(url, null);
}
public static String doPost(String url, Map param) {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
// 创建参数列表
if (param != null) {
List paramList = new ArrayList<>();
for (String key : param.keySet()) {
paramList.add(new BasicNameValuePair(key, param.get(key)));
}
// 模拟表单
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList);
httpPost.setEntity(entity);
}
// 执行http请求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
public static String doPost(String url) {
return doPost(url, null);
}
public static String doPostJson(String url, String json) {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
// 创建请求内容
StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
httpPost.setEntity(entity);
// 执行http请求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
}
此工具主要用于请求微信链接获取数据
import cn.px.base.pojo.page.LayuiPageFactory;
import cn.px.blh.core.wx.WxTokenEntity;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
@Component
public class WxUtil {
//获取token
private static final String TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token";
//微信APPID
public static String APP_ID;
//微信秘钥
private static String SECRET;
//微信公众号token在redis中存储时的key值
public static final String GZH_TOKEN = "wxgzh-access-token";
//获取JSAPI_TICKET
private static final String JSAPI_TICKET = "https://api.weixin.qq.com/cgi-bin/ticket/getticket";
//微信公众号jsapi_ticket在redis中存储的key值
public static final String JSAPI_KEY = "wxgzh-jsapi-ticket";
//JSAPI_KEY在redis中的失效时间
public static final Integer JSAPI_KEY_EXPIRED_TIME = 7000;
@Value("${wx.appId}")
public void setAppId(String appId) {
WxUtil.APP_ID = appId;
}
@Value("${wx.secret}")
public void setSecret(String secret) {
WxUtil.SECRET = secret;
}
/**
* 获取微信token
* @return
*/
public static WxTokenEntity getAccessToken() {
Map param = new HashMap<>(16);
param.put("grant_type", "client_credential");
param.put("appid", APP_ID);
param.put("secret", SECRET);
String wxResult = HttpClient.doGet(TOKEN_URL,param);
WxTokenEntity model = JSON.toJavaObject(JSONObject.parseObject(wxResult),WxTokenEntity.class);
return model;
}
/**
* 获得jsapi_ticket
*/
public static String getJsApiTicket(String token) {
String url = JSAPI_TICKET
+ "?access_token=" + token
+ "&type=jsapi";
String response = HttpClient.doGet(url);
if (StringUtils.isBlank(response)) {
return null;
}
JSONObject jsonObject = JSONObject.parseObject(response);
String ticket = jsonObject.getString("ticket");
return ticket;
}
}
import cn.px.base.util.CacheUtil;
import cn.px.blh.core.util.WxUtil;
import cn.px.blh.core.wx.WxTokenEntity;
import cn.stylefeng.roses.core.util.ToolUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Service;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@Service
public class WxJsApiService {
protected Logger logger = LogManager.getLogger();
/**
* 获取签名
* @param url
* @return
*/
public Map sign(String url) {
Map resultMap = new HashMap<>(16);
//这里的jsapi_ticket是获取的jsapi_ticket。
String jsapiTicket = this.getJsApiTicket();
//这里签名中的nonceStr要与前端页面config中的nonceStr保持一致,所以这里获取并生成签名之后,还要将其原值传到前端
if(ToolUtil.isEmpty(jsapiTicket)){
return resultMap;
}
String nonceStr = createNonceStr();
//nonceStr
String timestamp = createTimestamp();
String string1;
String signature = "";
//注意这里参数名必须全部小写,且必须有序
string1 = "jsapi_ticket=" + jsapiTicket +
"&noncestr=" + nonceStr +
"×tamp=" + timestamp +
"&url=" + url;
System.out.println("string1:"+string1);
try
{
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
crypt.update(string1.getBytes("UTF-8"));
signature = byteToHex(crypt.digest());
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
resultMap.put("url", url);
resultMap.put("jsapi_ticket", jsapiTicket);
resultMap.put("nonceStr", nonceStr);
resultMap.put("timestamp", timestamp);
resultMap.put("signature", signature);
resultMap.put("appId", WxUtil.APP_ID);
return resultMap;
}
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;
}
private static String createNonceStr() {
return UUID.randomUUID().toString();
}
private static String createTimestamp() {
return Long.toString(System.currentTimeMillis() / 1000);
}
public String getJsApiTicket(){
String ticket = (String) CacheUtil.getCache().get(WxUtil.JSAPI_KEY);
if(ToolUtil.isEmpty(ticket)){
if(ToolUtil.isEmpty(getToken())){
logger.error(String.format("获取token出错"));
}else{
ticket = WxUtil.getJsApiTicket(getToken());
CacheUtil.getCache().set(WxUtil.JSAPI_KEY,ticket,WxUtil.JSAPI_KEY_EXPIRED_TIME);
}
}
return ticket;
}
private String getToken(){
//此处获取token先判断缓存中是否存在,如果没有则调取放入缓存
String token = (String) CacheUtil.getCache().get(WxUtil.GZH_TOKEN);
if(ToolUtil.isEmpty(token)){
WxTokenEntity wxTokenEntity = WxUtil.getAccessToken();
if(ToolUtil.isEmpty(wxTokenEntity.getAccess_token())){
//获取token出错
logger.error(String.format("获取token出错,错误编码:{%s};错误提示:{%s}",wxTokenEntity.getErrcode(),wxTokenEntity.getErrmsg()));
}else{
token = wxTokenEntity.getAccess_token();
CacheUtil.getCache().set(WxUtil.GZH_TOKEN,token,Integer.parseInt(wxTokenEntity.getExpires_in()));
}
}
return token;
}
}
其中token及jsApiTicket需放入缓存中,防止过多调用微信链接造成资源浪费,缓存工具类大家可以根据自己项目来实现,这里不再贴出。
/**
* 获取微信公众号信息
*/
@ApiOperation("获取微信公众号信息")
@PostMapping(value = "/getSign")
@ResponseBody
public Object scanJsApi(@Param("tokenUrl") String tokenUrl) {
Map res = wxJsApiService.sign(tokenUrl);
if(ToolUtil.isEmpty(res)){
return setModelMap(HttpCode.BAD_REQUEST,"获取公众号信息失败");
}else{
return super.setSuccessModelMap(res);
}
}
1.首先使用到微信的wexin-jsapi:
安装:npm install weixin-jsapi --save
页面引入:import wx from "weixin-jsapi";
2.初始化微信参数
//初始化微信参数
wxLoadConfig:function(){
var vm = this;
return new Promise((resolve, reject) => {
// alert(window.location.href.split("#")[0]);
vm.$root.api.getSign(window.location.href.split("#")[0]).then(res => {
//获取数据
if (res.httpCode == 200 || res.httpCode == '200') {
var timestamp = res.data.timestamp;
var noncestr = res.data.nonceStr;
var signature = res.data.signature;
var appId = res.data.appId;
wx.config({
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
// debug : true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: appId, // 必填,公众号的唯一标识
timestamp: timestamp, // 必填,生成签名的时间戳
nonceStr: noncestr, // 必填,生成签名的随机串
signature: signature, // 必填,签名
jsApiList: [
"scanQRCode"
] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});
wx.checkJsApi({
jsApiList:['scanQRCode'],
success:function (res) {
alert(res);
}
});
} else {
}
resolve();
}).catch(err => {
reject(err);
})
})
},
3.调取扫一扫
//调取扫一扫
takeScan:function(){
wx.ready(function() {
wx.scanQRCode({
needResult: 1, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果,
scanType: ["qrCode", "barCode"], // 可以指定扫二维码还是一维码,默认二者都有
success: function(res) {
var result = res.resultStr; // 当needResult 为 1 时,扫码返回的结果
alert(res);
},
error: function(res) {
alert(res);
}
});
});
},
vue前端页面代码
/* eslint-disable no-undef */