在做一个在微信分享文章的项目,在微信分享的链接要有描述和图片必须调用微信的JS-SDK。而且现在(201904)只有企业认证的微信公众号/服务号微信才给分享接口的调用权限,普通/个人微信号公众号没有。
而且现在(201904)只有企业认证的微信公众号/服务号微信才给分享接口的调用权限,普通/个人微信号公众号没有,某宝光买一个认证的企业号就是几大百,微信开发真不容易ε=(´ο`*)))
还有一个需要注意的是,微信封域名可厉害了,普通域名撑不住多久,得想办法搞到备过案的二级不死(主域名被封,二级域名一样可以用,换个前缀就行)域名。
基本调用流程是这样的,进入要分享页面的时候调用后台接口,后台接口请求微信获取调用凭证(Ticket)(注意Ticket调用获取是有调用上限的,两小时失效,也就是说两小时就得调用一次),JS-SDK拿到凭证后注册相关函数,点击分享到朋友圈或分享给朋友时触发相关函数设置描述和图片
搞到企业认证的微信公众号
有企业认证的号才有权调用微信分享接口,查看自己有哪些接口可在下面网址查看,如果你的业务需要的接口个人号也能满足,那就不要搞,具体哪搞就看你有没有渠道和够不够聪明了┓( ´∀` )┏
查看有哪些接口调用权限(登录微信公众号->导航到左下角->接口权限):https://mp.weixin.qq.com/advanced/advanced?action=table&token=192206203&lang=zh_CN
设置安全域名
登录公众号->左边导航找到设置->功能设置->JS接口安全域名,会让你在你的服务器目录添加认证文件,就是一个普通的文本你文件
获取appID和AppSecret
登录公众号->左边导航找到开发->基本配置->公众号开发信息,注意AppSecret需自己保存
设置IP白名单
登录公众号->左边导航找到开发->基本配置->公众号开发信息->设置IP白名单,获取微信凭证时需要指定白名单里面的IP
前台调用
- 页面引入js
<script src="https://res.wx.qq.com/open/js/jweixin-1.4.0.js"></script> // 备注:切记一定要是 https 不然后面会使IOS系统不好用
var wxshare = function (aid) {
var url = window.location.href.split('#')[0] // 当前页面的url ps:'#'前边的路径
url = encodeURIComponent(url); //强烈注意:这里需要这样写
var req = basePath + 'front/wxshare';
$.ajax({
url: req,
type: 'post',
data: {url:url,aid:aid},
dataType: 'jsonp',
success: function (_data_) {
if (_data_ && _data_.resCode == 0){
var res = _data_.res;
wx.config({
debug: true, //是否 开启调试模式,建议调试的时候debug 改为true,页面会自动弹窗说明调用成功或失败
appId: res.appId, // 必填,公众号的唯一标识
timestamp: res.timestamp, // 必填,生成签名的时间戳
nonceStr: res.nonceStr, // 必填,生成签名的随机串
signature: res.signature, // 必填,签名,见附录1
jsApiList: [
'checkJsApi',
'updateAppMessageShareData',
'updateTimelineShareData',
'onMenuShareAppMessage',
'onMenuShareTimeline',
'showOptionMenu',
] // 必填,需要使用的JS接口列表 声明
});
wx.error(function(res) {
console.log("微信接口初始化失败;错误信息:"+res.toString());
});
window.onload = function() {
console.log("当前域名是:"+res.url);
}
wx.ready(function() {
//自定义“分享给朋友”及“分享到QQ”按钮的分享内容(1.4.0)
wx.updateAppMessageShareData({
debug: true,
title: res.title, // 分享标题
desc: res.desc, // 分享描述
link: res.url, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: res.cover, // 分享图标
success: function() {
// 用户点击了分享后执行的回调函数
}
});
//自定义“分享到朋友圈”及“分享到QQ空间”按钮的分享内容(1.4.0)
wx.updateTimelineShareData({
debug: true,
title: res.title, // 分享标题
desc: res.desc, // 分享描述
link: res.url, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: res.cover, // 分享图标
success: function() {
// 用户点击了分享后执行的回调函数
}
});
});
} else {
console.log(req + '请求失败!' + _data_.msg);
}
},
error: function (err) {
console.log(req + '请求错误!' + err);
}
});
};
wxshare(aid);//aid为文章id
后台调用
@RequestMapping(value = "/wxshare",produces="text/html;charset=UTF-8")
@ResponseBody
public Object wxshare(String url,String aid){
try{
String ticket = "";//注意此处TICKET需全局保存,微信有调用上限,可以写定时任务一小时或者两小时调用一次
url = URLDecoder.decode(url,"UTF-8");//注意URL需解码
Map<String,String> res = WeixinUtil.sign(ticket,url);//签名后获取JS-SDK获取的一系列参数
//你的逻辑
return suc(res);
} catch (Exception e){
logger.error(e.getMessage(),e);
return err(e.getMessage());
}
}
WeixinUtil代码
package com.top.util;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.sf.json.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.MessageDigest;
import java.util.*;
public class WeixinUtil {
private static Logger logger = LogManager.getLogger(WeixinUtil.class);
private static ObjectMapper objectMapper = new ObjectMapper();
/**
* 获取微信Ticket相关常量
*/
public static String appId = "你的appid";
public static String secret = "你的appsecret";
public static String GRANT_TYPE = "client_credential";
public static String GET_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token";
public static String API_TYPE = "jsapi";
public static String TICKET_URL_TEST = "https://api.weixin.qq.com/cgi-bin/ticket/getticket";
public static void main(String[] args) throws Exception{
String url= "url=http%3A%2F%2Faaa.crhmovie.cn%2Farticle%2F1";
String token = getWeiXinShareToken(appId,secret);
String ticket = getJsApiTicket(token);
Map<String,String> res = sign(ticket,url);
System.out.println(res);
}
public static boolean signatureCheck(String token,String timeStamp,String nonce,String signature) throws Exception{
List<String > list = new ArrayList<String>(3){
public String toString(){
return this.get(0)+this.get(1)+this.get(2) ;
}
} ;
list.add(token) ;
list.add(timeStamp) ;
list.add(nonce) ;
Collections.sort(list) ;
MessageDigest md = MessageDigest.getInstance("SHA-1") ;
byte[] digest = md.digest(list.toString().getBytes()) ;
String testStr = WeixinUtil.byteArrayToHexString(digest) ;
if(testStr.equalsIgnoreCase(signature.toUpperCase())){
return true;
}else{
return false ;
}
}
public static String signature(String jsapiTicket,String nonceStr,Long timestamp,String url) throws Exception{
String str = String.format("jsapi_ticket=%s&noncestr=%s×tamp=%d&url=%s",
jsapiTicket,nonceStr,timestamp,url) ;
MessageDigest md = MessageDigest.getInstance("SHA-1") ;
byte[] digest = md.digest(str.getBytes()) ;
String sigStr = WeixinUtil.byteArrayToHexString(digest) ;
return sigStr ;
}
public static String byteArrayToHexString(byte[] array){
String strDigest = "" ;
for(int i = 0 ;i<array.length;i++){
strDigest+=byteToHexString(array[i]) ;
}
return strDigest ;
}
public static String byteToHexString(byte ib){
char[] Digit = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'} ;
char [] ob = new char[2] ;
ob[0] = Digit[(ib >>> 4) & 0X0F] ;
ob[1] = Digit[ib &0X0F] ;
String s = new String(ob) ;
return s ;
}
public static String getWeiXinShareToken(String appId,String secret) throws Exception{
Map<String,String> maps = new HashMap<String,String>() ;
maps.put("grant_type", GRANT_TYPE) ;
maps.put("appid",appId) ;
maps.put("secret",secret);
String result = httpGet(GET_TOKEN_URL,maps) ;
JSONObject jsonObject = JSONObject.fromObject(result) ;
String access_token = (String) jsonObject.get("access_token") ;
Integer expires_in = (Integer) jsonObject.get("expires_in") ;
logger.info("weixin---get--token--"+result);
if(access_token !=null && expires_in!=null && expires_in==7200)
return access_token;
else
throw new Exception(result);
}
public static String getJsApiTicket(String token) throws Exception{
Map<String,String> maps = new HashMap<String,String>() ;
maps.put("access_token",token);
maps.put("type", API_TYPE);
String result = httpGet(TICKET_URL_TEST,maps) ;
System.out.println("weixin---get--ticket--"+result);
JSONObject jsonObject = JSONObject.fromObject(result) ;
Integer errcode = (Integer) jsonObject.get("errcode") ;
if(errcode==null || (errcode!=null &&errcode!=0)){
throw new Exception("getJsApi Ticket is failed:" + result) ;
}else{
String ticket = (String) jsonObject.get("ticket") ;
return ticket ;
}
}
/**
*
* @param jsapi_ticket
* @param url
* @return
* @throws Exception
*/
public static Map<String, String> sign(String jsapi_ticket, String url) throws Exception {
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
+ "×tamp=" + timestamp + "&url=" + url;
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
crypt.update(string1.getBytes("UTF-8"));
signature = byteToHex(crypt.digest());
ret.put("url", url);
//注意这里 要加上自己的appId
ret.put("appId", appId);
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;
}
private static String create_nonce_str() {
return UUID.randomUUID().toString();
}
private static String create_timestamp() {
return Long.toString(System.currentTimeMillis() / 1000);
}
/**
* http请求工具类,post请求
*
* @param url url
* @param params 参数值 仅支持String和list两种类型
* @return
* @throws Exception
*/
public static String httpPost(String url, Map<String, Object> params) throws Exception {
DefaultHttpClient defaultHttpClient = null;
BufferedReader bufferedReader = null;
try {
defaultHttpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(url);
httpPost.setHeader("Content-Type", "application/json;charset=ut-8");
if (params != null) {
//转换为json格式并打印,不需要的你们可以不要
String jsonParams = objectMapper.writeValueAsString(params);
logger.info("参数值:{}", jsonParams);
HttpEntity httpEntity = new StringEntity(jsonParams, "utf-8");
httpPost.setEntity(httpEntity);
}
HttpResponse httpResponse = defaultHttpClient.execute(httpPost);
if (httpResponse.getStatusLine().getStatusCode() != 200) {
String errorLog="请求失败,errorCode:"+httpResponse.getStatusLine().getStatusCode();
logger.info(errorLog);
throw new Exception(url+errorLog);
}
//读取返回信息
String output;
bufferedReader=new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent(),"utf-8"));
StringBuilder stringBuilder=new StringBuilder();
while ((output=bufferedReader.readLine())!=null){
stringBuilder.append(output);
}
return stringBuilder.toString();
} catch (ClientProtocolException e) {
e.printStackTrace();
throw e;
}catch (IOException e){
e.printStackTrace();
throw e;
}finally {
if(defaultHttpClient!=null)
defaultHttpClient.getConnectionManager().shutdown();
if(bufferedReader!=null)
bufferedReader.close();
}
}
/**
* http请求工具类,get请求
* @param url
* @param params
* @return
* @throws Exception
*/
public static String httpGet(String url, Map<String, String> params) throws Exception {
DefaultHttpClient defaultHttpClient = null;
BufferedReader bufferedReader = null;
try {
defaultHttpClient = new DefaultHttpClient();
if(params!=null){
StringBuilder stringBuilder=new StringBuilder();
Iterator<String> iterator=params.keySet().iterator();
String key;
while (iterator.hasNext()){
key=iterator.next();
Object val=params.get(key);
if(val instanceof List){
List v= (List) val;
for (Object o:v){
stringBuilder.append(key).append("=").append(o.toString()).append("&");
}
}else{
stringBuilder.append(key).append("=").append(val.toString()).append("&");
}
}
stringBuilder.deleteCharAt(stringBuilder.length()-1);
url=url+"?"+stringBuilder.toString();
logger.info("url:{}",url);
}
HttpGet httpGet = new HttpGet(url);
httpGet.setHeader("Content-Type", "application/json;charset=ut-8");
HttpResponse httpResponse = defaultHttpClient.execute(httpGet);
if (httpResponse.getStatusLine().getStatusCode() != 200) {
String errorLog="请求失败,errorCode:"+httpResponse.getStatusLine().getStatusCode();
logger.info(errorLog);
throw new Exception(url+errorLog);
}
//读取返回信息
String charSet="utf-8";
// if(resonseCharSet!=null && resonseCharSet.length>0)
// charSet=resonseCharSet[0];
String output;
bufferedReader=new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent(),charSet));
StringBuilder dataBuilder=new StringBuilder();
while ((output=bufferedReader.readLine())!=null){
dataBuilder.append(output);
}
return dataBuilder.toString();
} catch (ClientProtocolException e) {
e.printStackTrace();
throw e;
}catch (IOException e){
e.printStackTrace();
throw e;
}finally {
if(defaultHttpClient!=null)
defaultHttpClient.getConnectionManager().shutdown();
if(bufferedReader!=null)
bufferedReader.close();
}
}
}
排错
请看微信开发文档的服务5:附录5-常见错误及解决方法
https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115
网友评论