微信公众号开发——Java

步骤一:需要先进入公众号平台进行JS接口安全域名的设置。

​ 绑定域名一定要去掉 “http://”或者"https://",如下

JS接口安全域名  xxx.xxx.com

步骤二:引入微信jssdk.js文件

​ 示例文件 (x.x.x为版本号)

官方:https://res.wx.qq.com/open/js/jweixin-x.x.x.js

最重要是在java中获取jsapi_ticket,并且通过一系列操作得到一个签名;主要步骤是:

1、获取access_token(普通access_token),通过access_token获取到jsapi_ticket;access_token是公众号的全局唯一接口调用凭据,公众号调用各接口都需要access_token,access_token有效期只有2小时,最后用缓存存储,定时刷新
接口调用请求

https请求方式: GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

获取access_token代码:

private static String getAccessToken(String appid, String appSecret) {
        String token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
        String requestUrl = token_url.replace("APPID", appid).replace("APPSECRET", appSecret);
        // 发起GET请求获取凭证
        JSONObject jsonObject = getJsApiJSONObjectParam(requestUrl);
        String access_token = null;
        if (null != jsonObject) {
            access_token = jsonObject.getString("access_token");
        }
        return access_token;
    }


public static JSONObject getJsApiJSONObjectParam(String url) {
        JSONObject jsonResult = new JSONObject();
        InputStream is = null;
        try {
            URL urlGet = new URL(url);
            HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();
            http.setRequestMethod("GET"); // 必须是get方式请求
            http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            http.setDoOutput(true);
            http.setDoInput(true);
            System.setProperty("sun.net.client.defaultConnectTimeout", "30000");// 连接超时30秒
            System.setProperty("sun.net.client.defaultReadTimeout", "30000"); // 读取超时30秒
            http.connect();
            is = http.getInputStream();
            int size = is.available();
            byte[] jsonBytes = new byte[size];
            is.read(jsonBytes);
            String message = new String(jsonBytes, "UTF-8");
            jsonResult = JSONObject.parseObject(message);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            IOUtils.closeQuietly(is);
        }
        return jsonResult;
    }

用拿到的access_token 采用http GET方式请求获得jsapi_ticket(有效期7200秒,开发者必须在自己的服务全局缓存jsapi_ticket):

https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi

获取jsapi_ticket代码:

    private static String getJsApiTicket(String access_token) {
        String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";
        String requestUrl = url.replace("ACCESS_TOKEN", access_token);
        // 发起GET请求获取凭证
        JSONObject jsonObject = getJsApiJSONObjectParam(requestUrl);
        // 重试一次
        if ("40001".equals(jsonObject.getString("errcode"))) {
            access_token = getAccessToken(jsSdkConstants.getAppId(), jsSdkConstants.getAppSecret());
            cacheUtil.setCacheValue(Cache.WECHAT_ACCESS_TOKEN, access_token, 7200);
            requestUrl = url.replace("ACCESS_TOKEN", access_token);
            jsonObject = getJsApiJSONObjectParam(requestUrl);
        }
        String ticket = null;
        if (null != jsonObject) {
            ticket = jsonObject.getString("ticket");
        }
        return ticket;
    }


 public static JSONObject getJsApiJSONObjectParam(String url) {
        JSONObject jsonResult = new JSONObject();
        InputStream is = null;
        try {
            URL urlGet = new URL(url);
            HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();
            http.setRequestMethod("GET"); // 必须是get方式请求
            http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            http.setDoOutput(true);
            http.setDoInput(true);
            System.setProperty("sun.net.client.defaultConnectTimeout", "30000");// 连接超时30秒
            System.setProperty("sun.net.client.defaultReadTimeout", "30000"); // 读取超时30秒
            http.connect();
            is = http.getInputStream();
            int size = is.available();
            byte[] jsonBytes = new byte[size];
            is.read(jsonBytes);
            String message = new String(jsonBytes, "UTF-8");
            jsonResult = JSONObject.parseObject(message);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            IOUtils.closeQuietly(is);
        }
        return jsonResult;
    }
生成JS-SDK权限验证签名

签名生成规则如下:参与签名的字段包括noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳), url(当前网页的URL,不包含#及其后面部分) 。对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1。这里需要注意的是所有参数名均为小写字符。对string1作sha1加密,字段名和字段值都采用原始值,不进行URL 转义。

 private static Map<String, String> sign(String jsapi_ticket, String url) {
        Map<String, String> ret = new HashMap<String, String>();
        String nonce_str = create_nonce_str();
        String timestamp = create_timestamp();
        String string1;
        String signature = "";

        // 注意这里参数名必须全部小写,且必须有序
        string1 = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + nonce_str + "&timestamp=" + timestamp + "&url=" + url;
        try {
            MessageDigest crypt = MessageDigest.getInstance("SHA-1");
            crypt.reset();
            crypt.update(string1.getBytes("UTF-8"));
            signature = byteToHex(crypt.digest());
        } catch (NoSuchAlgorithmException e) {
            logger.error(e.getMessage(), e);
        } catch (UnsupportedEncodingException e) {
            logger.error(e.getMessage(), e);
        }

        ret.put("url", url);
        ret.put("jsapi_ticket", jsapi_ticket);
        ret.put("nonceStr", nonce_str);
        ret.put("timestamp", timestamp);
        ret.put("signature", signature);

        return ret;
    }

    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;
    }

缓存工具类

@Component
public final class CacheUtil {

	@Resource
	private RedisTemplate<String, String> redisTemplate;

	/**
	 * 缓存取值
	 * 
	 * @param key
	 * @return
	 */
	public String getCacheValue(String key) {
		return this.redisTemplate.opsForValue().get(key);
	}

	/**
	 * 缓存赋值
	 * 
	 * @param key
	 * @param value
	 */
	public void setCacheValue(String key, String value) {
		this.redisTemplate.opsForValue().set(key, value);
	}

	/**
	 * 删除缓存
	 * 
	 * @param key
	 * @return
	 */
	public void delCacheValue(String key) {
		this.redisTemplate.delete(key);
	}

	/**
	 * 缓存赋值并设置有效时间
	 * 
	 * @param key
	 * @param value
	 * @param number 数据
	 * @param unit   例如:TimeUnit.DAYS
	 */
	public void setCacheValue(String key, String value, long number, TimeUnit unit) {
		this.redisTemplate.opsForValue().set(key, value, number, unit);
	}

	/**
	 * 获取有效期
	 * 
	 * @param key
	 */
	public void getExpireTime(String key) {
		this.redisTemplate.getExpire(key);
	}

	/**
	 * 通过 TimeUnit 获取有效期
	 * 
	 * @param key
	 * @param unit
	 */
	public void getExpireTime(String key, TimeUnit unit) {
		this.redisTemplate.getExpire(key, unit);
	}

	/**
	 * 获取指定条件key值
	 * 
	 * @param pattern *:所有
	 * @return
	 */
	public Set<String> getCacheKeys(String pattern) {
		return this.redisTemplate.keys(pattern);
	}

}

以上生成Map返回给前端

    /**
     * 获取微信签名
     * 通过url生成signature,noncestr,timestamp
     * @param
     * @return
     */
    public static Map<String, String> getJsApiParam(String url) {
        Map<String, String> resultMap = new HashMap<String, String>();
        String access_token = (String) TokenCacheHelper.getInstance().get(Cache.WECHAT_ACCESS_TOKEN);
        if (StringUtils.isEmpty(access_token)) {
            access_token = getAccessToken(jsSdkConstants.getAppId(), jsSdkConstants.getAppSecret());
            TokenCacheHelper.getInstance().put(Cache.WECHAT_ACCESS_TOKEN, access_token, 7200);
        }
        String jsapi_ticket = (String) FbOmsSystemCacheUtil.get(Cache.WECHAT, "jsapi_ticket");// 从系统缓存中获取调用票据
        if (StringUtils.isEmpty(jsapi_ticket)) {
            jsapi_ticket = getJsApiTicket(access_token);
            FbOmsSystemCacheUtil.put(Cache.WECHAT, "jsapi_ticket", jsapi_ticket);
            access_token = (String) TokenCacheHelper.getInstance().get(Cache.WECHAT_ACCESS_TOKEN);
        }
        resultMap = sign(jsapi_ticket, url);
        resultMap.put("appId", jsSdkConstants.getAppId());
        resultMap.put("access_token", access_token);
        return resultMap;
    }

步骤三:通过config接口注入权限验证配置

在页面初始化的时候,调用上面方面,填充到wx.config里面

wx.config({
  debug: true, // 开启调试模式
  appId: '', // 必填,公众号的唯一标识
  timestamp: , // 必填,生成签名的时间戳
  nonceStr: '', // 必填,生成签名的随机串
  signature: '',// 必填,签名
  jsApiList: [] // 必填,需要使用的JS接口列表
});

返回示例:

"config", {
    	appId: "XXXXXXXXXXXXXXXXXXXXX"
        beta: false
        debug: true
        jsApiList: (2) ["onMenuShareTimeline", "onMenuShareAppMessage"] 
        nonceStr: "d28ed4e1-e88a-44c6-926e-de84d498dfa6"
        signature: "db117be7526d7443f33fd4a469977d38e74f1fa9"
        timestamp: "1627348841"
		}

步骤四:通过ready接口处理成功验证

wx.ready(function(){
  // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
})

步骤五:通过error接口处理失败验证

wx.error(function(res){  // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。});

然后就可以调用相关API接口,接口Demo链接

https://www.weixinsxy.com/jssdk/#menu-card

前端代码:**

import { getJsApiParam } from './services/wx';
async function config() {
  const index = window.location.href.indexOf('#');
  const url = window.location.href.substring(0, index);
  const res = await getJsApiParam({ url: url });
  if (!res) {
    return res;
  }
  const { appId, nonceStr, signature, timestamp } = res;
  window.wx.config({
    debug: true,
    appId,
    timestamp,
    nonceStr,
    signature,
    jsApiList: ['onMenuShareTimeline', 'onMenuShareAppMessage'], // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
    beta: false,
  });
}
config();

---------------------------------------------------------------------分割线--------------------------------------------------------------

function share(pmCode: string, skuName: string) {
    const OnMenuShareAppMessageOptions: any = {
      title: 'XXXX',
      desc: `商品名【${skuName}】,请点击查看分享`,
      link: 'http://www.baidu.com/',
      type: 'link',
      imgUrl: `http://www.baidu.com/imges.png`,
    };
    window.wx.ready(function () {
      window.wx.onMenuShareAppMessage(OnMenuShareAppMessageOptions);
    });
  }
重点:

获取access_token需要新增Ip白名单,否则获取不了会报40164错误

可以通过微信公众号提供的在线测试接口测试是否能获取access_token

https://mp.weixin.qq.com/debug
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值