如何使用微信的JSSDK分享功能

久违的新文章发布。。我的锅
这篇文章是在写的太长,可以慢慢看一下,伪代码,毕竟简书上面,望大家见谅!!!
先看官方文档:微信jssdk官方文档
首先得有我们自己的公众号对吧,而且token要验证过了哈,如果不会,我可以写文章教大家,有留言我就写

继续老的节奏,按步骤来做

STEP 1

我们进入公众号测试平台,如图所示:

并且打开开发者文档

查看一下权限

最好认证一下公众号无论是订阅号还是服务号,都是有必要的!如果是真心想做好微信端的,有利无害!
这里以我申请的测试号为例,权限相对比较充足

没打马赛克哈

STEP 2

Controller层可以这么写
@RequestMapping(value="/share", method = {RequestMethod.GET, RequestMethod.POST})
@ResponseBody
protected Mapshare(String url) throws ServletException, IOException {
System.out.println("inter share");
String ticket=null;

EhcacheUtil cache = EhcacheUtil.initCache("sys-js_tickt-cache");
//我使用了ehcache的缓存

System.out.println(cache);
Object aa=cache.get("jst");
//从我们自己定义的cache块中去出我们放入的对象(如果是第一次运行则其为null)

if(aa!=null){
ticket=aa.toString(); //赋值给我们的ticket字段
}else{
//appid,appSecret自行写成static final string ,我就不贴出我的了
AccessToken token = WeixinUtil.getAccessToken(appId, appSecret);
// AccessToken类为我们自己写的接受token的一个entity类,很简单,你也直接用string替代
//WeixinUtil.getAccessToken(string,string)方法见下面
String toke=token.getToken();
JSToken jt=WeixinUtil.getTicket(toke);
//我们获取到了token后,需要再将token已get方式传给getTicket()方法,见下面
ticket=jt.getJsapi_ticket();
cache.put("jst", ticket);
//放进缓存,以便下次使用
}

System.out.println("ticket:"+ticket+"url:"+url);
Mapret = WeixinUtil.sign(ticket, url);
//此处对ticket,url,noncestr,timestamp进行签名排序
ret.put("appid", appId);
for (Map.Entry entry : ret.entrySet()) {
System.out.println(entry.getKey() + ", " + entry.getValue());
},

return ret;
}

缓存存储的原因请看下面:access_TOKEN 获取每天是有限制次数的!!!而且官方说明:
access_token是公众号的全局唯一票据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。
公众平台的API调用所需的access_token的使用及生成方式说明:
1、为了保密appsecrect,第三方需要一个access_token获取和刷新的中控服务器。而其他业务逻辑服务器所使用的access_token均来自于该中控服务器,不应该各自去刷新,否则会造成access_token覆盖而影响业务;
2、目前access_token的有效期通过返回的expire_in来传达,目前是7200秒之内的值。中控服务器需要根据这个有效时间提前去刷新新access_token。在刷新过程中,中控服务器对外输出的依然是老access_token,此时公众平台后台会保证在刷新短时间内,新老access_token都可用,这保证了第三方业务的平滑过渡;
3、access_token的有效时间可能会在未来有调整,所以中控服务器不仅需要内部定时主动刷新,还需要提供被动刷新access_token的接口,这样便于业务服务器在API调用获知access_token已超时的情况下,可以触发access_token的刷新流程。
具体参数配置可以看一下文章末尾的附录1

STEP 3

WeixinUtil工具类写法,一下方法均在此类中
所涉及的jar包,可以看文章末尾附录2
package com.fredia.weixin;
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.Date;
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 net.sf.json.JSONException;
import net.sf.json.JSONObject;

public class WeixinUtil{
public final static String access_token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";

 public final static String JS_url ="https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi"; 

public  static String js_tick="";

public static AccessToken getAccessToken(String appid, String appsecret)
throws JSONException {
AccessToken accessToken = null;
String requestUrl = access_token_url.replace("APPID", appid).replace("APPSECRET", appsecret);
JSONObject jsonObject = httpRequest(requestUrl, "GET", null);
// 如果请求成功
if (null != jsonObject) {
try {
accessToken = new AccessToken(); accessToken.setToken(jsonObject.getString("access_token")); accessToken.setExpiresIn(jsonObject.getInt("expires_in"));
} catch (JSONException e) {
accessToken = null;
// 获取token失败//
log.error("获取token失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg"));
System.out.println("获取token失败 errcode:{} errmsg:{}"+jsonObject.getInt("errcode")+jsonObject.getString("errmsg"));
} }
return accessToken;
}

public static JSToken getTicket(String token)
throws JSONException {
JSToken jstoken = null;
String requestUrl = JS_url.replace("ACCESS_TOKEN", token);
JSONObject jsonObject = httpRequest(requestUrl, "GET", null);
// 如果请求成功
if (null != jsonObject) {
try {
jstoken = new JSToken();
jstoken.setJsapi_ticket(jsonObject.getString("ticket"));
jstoken.setExpiresIn(jsonObject.getInt("expires_in"));
} catch (JSONException e) {
jstoken = null;
System.out.println("获取ticket失败 errcode:{} errmsg:{}"+jsonObject.getInt("errcode")+jsonObject.getString("errmsg"));
} }
return jstoken;
}

public static Map sign(String jsapi_ticket, String url) {
Map ret = new HashMap();
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;
System.out.println(string1);
try {
//SHA-1加密方式,jar包有
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
//字符串utf-8处理
crypt.update(string1.getBytes("UTF-8"));
//将数据二进制转换为十六进制
signature = byteToHex(crypt.digest());
}

catch (NoSuchAlgorithmException e)
{e.printStackTrace();}catch (UnsupportedEncodingException e)
{e.printStackTrace();}

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

//byteTohex 方法
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 create_nonce_str() {
return UUID.randomUUID().toString();
}

//生成当前时间戳的方法
private static String create_timestamp() {
return Long.toString(System.currentTimeMillis() / 1000);
}

自己重写httprequest请求方法
/**

  • 描述: 发起https请求并获取结果
  • @param requestUrl 请求地址
  • @param requestMethod 请求方式(GET、POST)
  • @param outputStr 提交的数据
  • @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)
    */
    //网上一大堆的下面方法的模板
    public static JSONObject httpRequest(String requestUrl, String requestMethod, String outputStr) {
    JSONObject jsonObject = null;
    StringBuffer buffer = new StringBuffer();

try {
// 创建SSLContext对象,并使用我们指定的信任管理器初始化
//MyX509TrustManager方法网上有教程
作用?哼哼:
通过X509TrustManager接口实现自己创建的证书
TrustManager[] tm = { new MyX509TrustManager() };
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
sslContext.init(null, tm, new java.security.SecureRandom());
// 从上述SSLContext对象中得到SSLSocketFactory对象
SSLSocketFactory ssf = sslContext.getSocketFactory();
URL url = new URL(requestUrl);
HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();
httpUrlConn.setSSLSocketFactory(ssf);
httpUrlConn.setDoOutput(true);
httpUrlConn.setDoInput(true);
httpUrlConn.setUseCaches(false);
// 设置请求方式(GET/POST)
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.fromObject(buffer.toString());
System.out.println("hehe");
} catch (ConnectException ce) {
// log.error("Weixin server connection timed out.");
System.out.println("Weixin server connection timed out.");
} catch (Exception e) {
// log.error("https request error:{}", e);
System.out.println("https request error:{}");
}

return jsonObject;
}

}

STEP 4

自己封装一个ehcache 所需功能的方法
package com.ehcache
import java.util.List;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
/**

  • 将Ehcache 包装一层
    */
    public class EhcacheUtil {
    private final Ehcache cache;
    private EhcacheUtil(Ehcache ehcache) {
    cache = ehcache;
    }

//初始化缓存块
public static EhcacheUtil initCache(String cacheName) {
CacheManager cacheManager = (CacheManager) BeanFactoryUtil.getContext()
.getBean("ehCacheManager");
Ehcache cache = cacheManager.getCache(cacheName);
if (cache == null) {
cacheManager.addCache(cacheName);
} else {
cache = cacheManager.getCache(cacheName);
}

return new EhcacheUtil(cache);
}

public boolean containsKey(Object key) {
return cache.isElementInMemory(key) || cache.isElementOnDisk(key);
}

public void put(Object key, Object value) {
Element element = new Element(key, value);
cache.put(element);
}

public Object get(Object key) {
Element element = cache.get(key);
if (element == null) {
return null;
}

return element.getObjectValue();
}

public void remove(Object key) {
cache.remove(key);
}

public void removeAllKey() {
cache.removeAll();
}

public List getKeys() {
return cache.getKeys();
}

}

STEP 5

下面我们该懂jQuery了,不管使用是的jsp 还是freemarker之类的模板引擎
页面js的写法如下
$(function(){
var url=location.href.split('#')[0];// 获取当前访问浏览器的地址的"#“前的地址

核心是下面
$.ajax({
//ajax将我当前页面的url传给我的share controller
type: "POST",
url: '/weixin/share',
dataType: 'json',
data: {url:url},
success: function(result){
//成功返回给页面时,我们回调wx.config方法。可以放在ajax里面,亲测
wx.config({
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: result.appid, // 必填,公众号的唯一标识
timestamp: result.timestamp, // 必填,生成签名的时间戳
nonceStr: result.noncestr, // 必填,生成签名的随机串
signature: result.signature,// 必填,签名
// 检测自己所将要使用的jssdk api是否有权限
jsApiList: [
'checkJsApi',
'onMenuShareTimeline',
'onMenuShareAppMessage',
]
});
}
});

});
wx.ready(function () {
// 1 判断当前版本是否支持指定 JS 接口,支持批量判断
document.querySelector('#checkJsApi').onclick = function () {
wx.checkJsApi({
jsApiList: [
'checkJsApi',
'onMenuShareTimeline',
'onMenuShareAppMessage',
],
success: function (res) {
alert(JSON.stringify(res));
}
});
};

//最好以触发时间来进行操作,如果是自动加载的话,容易出现点击无响应情况,毕竟自动加载是有顺序的
// 2. 监听“分享给朋友”,按钮点击、自定义分享内容及分享结果接口
document.querySelector('#onMenuShareAppMessage').onclick = function () {

wx.onMenuShareTimeline({
//下面字段都可以动态,以静态字符串示例
title: '邀请好友',
desc: 'fredia王瘦子',
link: url,
imgUrl: '?88',
trigger: function (res) {
},
success: function (res) {
alert('已分享');
},
cancel: function (res) {
alert('已取消');
},
fail: function (res) {
alert(JSON.stringify(res));
}
});

wx.onMenuShareAppMessage({
title:' 邀请好友',
desc: '快来吧!',
link: url,
imgUrl: 'http://www.ftoow.com/attachment/Mon_1603/145_2_84ec990ab4f8b11.jpg?88',
trigger: function (res) {
},
success: function (res) {
alert('已分享');
},
cancel: function (res) {
alert('已取消');
},
fail: function (res) {
alert(JSON.stringify(res));
}
});

});
//jssdk- 错误的处理方案方法
wx.error(function (res) {
alert(res.errMsg);

});
在页面上创建id=“checkJsApi”,id=“onMenuShareAppMessage”的button,可以进行测试一下

STEP 6

调试工具选择
推荐使用官方web开发者工具,虽然慢了点,但是毕竟是官方的,对于调试还是很不错的!

附录1

ehcache配置
配置自定义缓存
maxElementsInMemory:缓存中允许创建的最大对象数
eternal:缓存中对象是否为永久的,如果是,超时设置将被忽略,对象从不过期。
timeToIdleSeconds:缓存数据的钝化时间,也就是在一个元素消亡之前,
两次访问时间的最大时间间隔值,这只能在元素不是永久驻留时有效,
如果该值是 0 就意味着元素可以停顿无穷长的时间。
timeToLiveSeconds:缓存数据的生存时间,也就是一个元素从构建到消亡的最大时间间隔值,
这只能在元素不是永久驻留时有效,如果该值是0就意味着元素可以停顿无穷长的时间。
overflowToDisk:内存不足时,是否启用磁盘缓存。
memoryStoreEvictionPolicy:缓存满了之后的淘汰算法。

附录2

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,817评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,329评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,354评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,498评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,600评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,829评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,979评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,722评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,189评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,519评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,654评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,329评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,940评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,762评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,993评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,382评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,543评论 2 349

推荐阅读更多精彩内容