微信内部 点击右上角 分享-H5页面-JS+java代码

app内部分享到微信,QQ都正常

再从微信打开,继续分享给朋友或者朋友圈,就变成下面这个样子了

一切变得那么的。。。无助!!!

开始以为在H5页面上添加一些东西即可,后来发现,完全不是自己想象的那个样子。

这个东西,对于一个从未用过微信JS的码农来说,或许要被带坑里去卡个几天!!!

以下是本人的一点点经验拿来和各位分享,希望刚接触到的少走一些弯路!

思路:

1.需要公众号一个,得到appid,appkey等等

2.当H5页面加载完成,ajax异步请求,调用微信api,获取access_token,获取jsapi_ticket【api的每天调用次数有限,每次调用返回的结果也不同,即,会改变之前的token,之前的就会失效,分享结果也失效】

3.调用api目的就是得到“nonceStr”和“signature”

4.将参数注入H5页面加载、配置微信分享参数【wx.config】

over。。。

官方的示例代码:http://demo.open.weixin.qq.com/jssdk/sample.zip

官方文档:点击打开链接

JAVA代码:


/****************************** js请求接口 ***************/

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 用途描述:微信内部点击分享Controller
 *
 * @author 
 * @version 1.0.0
 */
@RestController
@RequestMapping(value = "/wechat")
public class WeChatShareController {

    private final static Logger LOG = LoggerFactory.getLogger(WeChatShareController.class);

    @Autowired
    private WeChatShareService weChatShareService;

    /**
     * 获取微信加密信息
     *
     * @param request
     * @return
     */
    @RequestMapping(value = "/getsignature", method = {RequestMethod.GET})
    public Map<String, Object> toTranscript(@RequestParam(value = "url", required = true) String url) {
        Map<String, Object> data = new HashMap<String, Object>();

        try {   
			// 去数据库查询已保存的微信加密信息token等等(调用微信api获取token和保存的代码下面都有)
            Map<String, Object> wxInfo = weChatShareService.queryWechatInfo(url);
			
			data.put("wxInfo", wxInfo);
			
            return MsgCodeUtil.createMsg(MsgCodeUtil.CODE_SUCCESS, data);
        } catch (Exception e) {
            LOG.error("获取微信加密信息" + e.getMessage(), e);
			return MsgCodeUtil.createMsg(MsgCodeUtil.CODE_UNKNOWN_ERROR);
        }

    }
}


/****************************** WeChatShareService ***************/

/**
 * 用途描述:微信内部点击分享Service
 *
 * @version 1.0.0
 */
@Service
public class WeChatShareService {

    @Autowired
    private WechatShareTokenDao wechatShareTokenDao;


    /**
     * 获取微信加密信息
     *
     * @param url
     * @return
     * @throws Exception
     */
    public Map<String, Object> queryWechatInfo(String url) throws Exception {
        Map<String, Object> wxInfo = new HashMap<>();

        String accessToken = "";
        String jsapiTicket = "";
        //1、获取AccessToken和jsapiTicket,先从数据库获取最近一次保存的。
//        Map<String, Object> result = wechatShareTokenDao.queryWechatInfo();
//        if (null != result && !result.isEmpty()) {
//            accessToken = this.objToString(result.get("wechat_access_token"), "");
//            jsapiTicket = this.objToString(result.get("wechat_jsapi_ticket"), "");
//        }

        // 先不存储,直接调用api查询测试(注意:每日查询token次数有限,所以我们要自己定时存储到数据库,
        // 每个token是2小时过期,我们间隔30分钟去更新一个就差不多了,时间设置多久自己看着办)
        accessToken = WeChatUitl.getAccessToken();
        jsapiTicket = WeChatUitl.getTicket(accessToken);

        //3、时间戳和随机字符串
        long currentTimes = System.currentTimeMillis();  // 时间戳
        String noncestr = UUID.randomUUID().toString().replace("-", "").substring(0, 16);//随机字符串
        String timestamp = String.valueOf(currentTimes / 1000);// 时间戳

        //5、将参数排序并拼接字符串
        String params = "jsapi_ticket=" + jsapiTicket + "&noncestr=" + noncestr + "&timestamp=" + timestamp + "&url=" + url;

        //6、将字符串进行sha1加密
        String signature = WeChatUitl.getSHA1(params);

        //7、微信appId
        String appId = WeChatUitl.getAppIdWx();

        wxInfo.put("appId", appId);
        wxInfo.put("accessToken", accessToken);
        wxInfo.put("jsapiTicket", jsapiTicket);
        wxInfo.put("timestamp", timestamp);
        wxInfo.put("nonceStr", noncestr);
        wxInfo.put("params", params);
        wxInfo.put("signature", signature);

        return wxInfo;
    }


    public static String objToString(Object obj, String def) {
        return obj == null ? def : obj.toString();
    }

}






****************************** WeChatUitl ***************


import net.sf.json.JSONObject;

import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;

/**
 * 用途描述: 微信开发获取信息---微信开放平台
 * (这个方法内参数不要去改,说不定会出什么问题,除了tokenWx,appIdWx,appSecretWx,keyWx修改为自己对应值)
 *
 * @version 1.0.0
 */
public class WeChatUitl {
	// 获取token的描述,自己定义就可以了
    public static final String tokenWx = "*****_token";

    private static String appIdWx = "wx700000000000000"; // 微信appid---微信公众平台
    private static String appSecretWx = "71**************************4"; // 微信AppSecret---微信公众平台
	
	// 测试号,可以在微信公众平台直接创建一个,下面我会贴图考诉你怎么创建,这些都是要获取微信token用的
//    private static String appIdWx = "wx500000000000000"; // 微信appid---微信公众平台--测试号
//    private static String appSecretWx = "8e**************************ad"; // 微信AppSecret---微信公众平台--测试号

	// 这个是APP应用的标识,所有微信开发相关的项目(即应用)都有一个appkey,我们的公众号绑定了APP应用
    private static String keyWx = "7b**************************d66"; // 微信key     应用签名

    public static String getAppIdWx() {
        return appIdWx;
    }

    public static void setAppIdWx(String appIdWx) {
        WeChatUitl.appIdWx = appIdWx;
    }

    public static String getAppSecretWx() {
        return appSecretWx;
    }

    public static void setAppSecretWx(String appSecretWx) {
        WeChatUitl.appSecretWx = appSecretWx;
    }

    public static String getKeyWx() {
        return keyWx;
    }

    public static void setKeyWx(String keyWx) {
        WeChatUitl.keyWx = keyWx;
    }

    /**
     * 获取access_token
     *
     * @return
     */
    public static String getAccessToken() {
        String access_token = "";
        String grant_type = "client_credential";//获取access_token填写client_credential
        String AppId = appIdWx;//第三方用户唯一凭证
        String secret = appSecretWx;//第三方用户唯一凭证密钥,即appsecret
        //这个url链接地址和参数皆不能变
        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=" + grant_type + "&appid=" + AppId + "&secret=" + secret;

        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();
            InputStream is = http.getInputStream();
            int size = is.available();
            byte[] jsonBytes = new byte[size];
            is.read(jsonBytes);
            String message = new String(jsonBytes, "UTF-8");
            JSONObject demoJson = JSONObject.fromObject(message);
            System.out.println("JSON字符串:" + demoJson);
            access_token = demoJson.getString("access_token");
            is.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return access_token;
    }


    /**
     * 获取jsapi_ticket
     *
     * @param access_token
     * @return
     */
    public static String getTicket(String access_token) {
        String ticket = null;
        String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + access_token + "&type=jsapi";//这个url链接和参数不能变
        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();
            InputStream is = http.getInputStream();
            int size = is.available();
            byte[] jsonBytes = new byte[size];
            is.read(jsonBytes);
            String message = new String(jsonBytes, "UTF-8");
            JSONObject demoJson = JSONObject.fromObject(message);
            System.out.println("JSON字符串:" + demoJson);
            ticket = demoJson.getString("ticket");
            is.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ticket;
    }


    /**
     * SHA、SHA1加密
     *
     * @parameter: str:待加密字符串
     * @return: 加密串
     **/
    public static String getSHA1(String str) {
        try {
            MessageDigest digest = java.security.MessageDigest
                    .getInstance("SHA-1"); //如果是SHA加密只需要将"SHA-1"改成"SHA"即可
            digest.update(str.getBytes());
            byte messageDigest[] = digest.digest();
            // Create Hex String
            StringBuffer hexStr = new StringBuffer();
            // 字节数组转换为 十六进制 数
            for (int i = 0; i < messageDigest.length; i++) {
                String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
                if (shaHex.length() < 2) {
                    hexStr.append(0);
                }
                hexStr.append(shaHex);
            }
            return hexStr.toString();

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }
    
    

    public static void main(String[] args) {
        //1、获取AccessToken
        String accessToken = getAccessToken();
//        String accessToken = "6_Exc9VRFdPMLeF-4gPaJJGmoo-BJUGzgSJcs3vkT_y4eXPiQzRf1vdMvlVXNE85sfYH9AtQcdd-zptyD5t5S98VXSwIapyMoYjBNfvH7A11GZOoWs2u6agFlLS9NMqzTgN1N5V16BZrL1BnV_WTIaAIAHET";

        //2、获取Ticket
        String jsapi_ticket = getTicket(accessToken);

        //3、时间戳和随机字符串
        String noncestr = UUID.randomUUID().toString().replace("-", "").substring(0, 16);//随机字符串
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);//时间戳

        System.out.println("accessToken:" + accessToken + "\njsapi_ticket:" + jsapi_ticket + "\n时间戳:" + timestamp + "\n随机字符串:" + noncestr);

        //4、获取url
        String url = "http://localhost:8080/project/share";

        // 根据JSSDK上面的规则进行计算,这里比较简单,我就手动写啦
//        String[] ArrTmp = {"jsapi_ticket","timestamp","nonce","url"};
//        Arrays.sort(ArrTmp);
//        StringBuffer sf = new StringBuffer();
//        for(int i=0;i<ArrTmp.length;i++){
//            sf.append(ArrTmp[i]);
//        }

        //5、将参数排序并拼接字符串
        String str = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + noncestr + "&timestamp=" + timestamp + "&url=" + url;

        //6、将字符串进行sha1加密
        String signature = Constant.getSHA1(str);
        System.out.println("参数:" + str + "\n签名:" + signature);
    }


}




****************************** MsgCodeUtil工具类 ***************

import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang.StringUtils;

/**
 * 返回code及提示消息
 * @version 1.0.0
 */
public class MsgCodeUtil {
	/**
	 * 成功
	 */
	public final static int CODE_SUCCESS = 1;
	/**
	 * 失败
	 */
	public final static int CODE_FAILED = 2;
	/**
	 * 无权限 
	 */
	public final static int CODE_UNAUTHORIZED = 401;
	/**
	 * 未知错误
	 */
	public final static int CODE_UNKNOWN_ERROR = 1000;
	
	private final static String MSG_SUCCESS = "成功!";
	
	private final static String MSG_FAILED = "失败!";
	
	private final static String MSG_UNAUTHORIZED = "无权限访问!";
	
	private final static String MSG_UNKNOWN_ERROR = "系统出错了!";
	
	public static Map<String, Object> createMsg(int code, String msg, Object data) {
		Map<String, Object> message = new HashMap<String, Object>();
		message.put("code", code);
		message.put("msg", setMsg(code, msg));
		if (data != null) {
			message.put("data", data);
		}
		
		return message;
	}
	
	public static Map<String, Object> createMsg(int code, String msg) {
		return createMsg(code, msg, null);
	}
	
	public static Map<String, Object> createMsg(int code, Object obj) {
		return createMsg(code, null, obj);
	}
	
	public static Map<String, Object> createMsg(int code) {
		return createMsg(code, null, null);
	}
	
	private static String setMsg(int code, String msg) {
		if (code == CODE_SUCCESS) {
			return StringUtils.isBlank(msg) ? MSG_SUCCESS : msg;
		} 
		
		if (code == CODE_FAILED) {
			return StringUtils.isBlank(msg) ? MSG_FAILED : msg;
		}
		
		if (code == CODE_UNAUTHORIZED) {
			return StringUtils.isBlank(msg) ? MSG_UNAUTHORIZED : msg;
		}
		
		if (code == CODE_UNKNOWN_ERROR) {
			return StringUtils.isBlank(msg) ? MSG_UNKNOWN_ERROR : msg;
		}
		
		return msg;
	}
}

H5,js代码

<%--  IOS    JSON  赋值  getShareBean(); --%>
<input type="hidden" id="shareTitle" name="shareTitle" value="这个是分享标题"/>
<input type="hidden" id="shareContent" name="shareContent" value="这个是分享内容"/>
<input type="hidden" id="shareImage" name="shareImage" value="这里填一个图片全路径"/>

<c:import url="../common_footer.jsp"/>

<%--------------------------------------- 微信分享开始 ---------------------------------%>
<script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
<script>
    // 只有先这样取值,再赋值给下面的title,desc,imgUrl,link,不然IOS苹果端无法调用成功
    var shareTitle = $("#shareTitle").val();
    var shareContent = $("#shareContent").val();
    var shareImage = $("#shareImage").val();
	// 分享出去点击跳转链接必须用这个
    var shareUrl = location.href.split('#')[0].toString();

    var title = shareTitle;
    var desc = shareContent;
    // 分享的图片,最好是正方形,不是也没关系,但是一定是http模式,即绝对路径,而不是服务器路劲
    var imgUrl = shareImage;
    // 该链接域名或路径必须与当前页面对应的公众号JS安全域名一致-----这个特别重要,请看清楚
    // 这里的地址可以写死,也可以动态获取,但是一定不能带有微信分享后的参数,不然分享也是失败的
    var link = shareUrl;

</script>

<%----这个页面写公共的JS方法,在下面-----%>
<c:import url="../wechat_share.jsp"/>

<%----或者封装成js文件再引入-----%>
<%----<script src="js/wechat.js"></script>-----%>
<%--------------------------------------- 微信分享结束 ---------------------------------%>



/****************************** wechat_share.jsp ***************/

<script>
    // 当前页面访问路径
    var url = location.href.split('#')[0].toString();
    var getUrl = "wechat/getsignature";

    $.get(getUrl,
        {"url": url}).done(function (data) {
        // 注意这里的url,一定要这样写,也就是动态获取,不然也不会成功的。
//        console.log(data);
//        console.log(data.code);
        if (data.code == 1) {
            var wxInfo = data.wxInfo;
            if (wxInfo.signature != null) {
                wx.config({
                    debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
                    appId: wxInfo.appId, // 必填,公众号的唯一标识
                    timestamp: wxInfo.timestamp, // 必填,生成签名的时间戳
                    nonceStr: wxInfo.nonceStr, // 必填,生成签名的随机串
                    signature: wxInfo.signature,// 必填,签名,见附录1
                    jsApiList: [
                        'onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ', 'onMenuShareWeibo', 'onMenuShareQZone'
                    ] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
                });
            }
        }
    }).fail(function (msg) {
//        console.log("error:" + msg);
    });

    // 分享给朋友、QQ、微博
    var shareData = {
        "imgUrl": imgUrl,
        "title": title,
        "desc": desc,
        'link': link,
        success: function() {
//            layer.msg("分享成功~", {});
        },
        cancel: function() {
//            layer.msg("取消分享");
        }
    };

    // 分享到朋友圈
    var shareToTimeline = {
        "imgUrl": imgUrl,
        "title": title,
        'link': link,
        success: function() {
//            layer.msg("分享成功~", {});
        },
        cancel: function() {
//            layer.msg("取消分享");
        }
    }

    wx.ready(function () {
        wx.onMenuShareTimeline(shareToTimeline);        // 分享到微信朋友圈
        wx.onMenuShareAppMessage(shareData);            // 分享给微信朋友
        wx.onMenuShareQQ(shareData);                    // 分享到QQ
        wx.onMenuShareQZone(shareData);                 // 分享到QQ空间
        wx.onMenuShareWeibo(shareData);                 // 分享到微博

        wx.error(function (res) {
            alert(res.errMsg);
        });
    });

</script>


 

第二步:

先登录微信公众号平台

【调用次数有限,请注意】


确认签名算法是否正确

http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign

可用此页面工具进行校验

-------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------

第三步:

接下来最重要的步骤:

1,最好先去申请一个域名(花生壳),用于测试,不然只有发布到线上测试了【微信只认域名,不认ip+端口】

2,在公众号后台添加IP白名单

本机IP获取方式

调用“获取access_token”接口,返回结果。

错误信息显示ip无权限,就是这个

在这里也一起把线上服务器的IP也一起设置进去,因为每次设置的时候都要找老板的手机来扫微信二维码,脑壳疼

注意,如果线上服务器ip地址有变动,必须修改微信公众号ip白名单配置

第四步:

配置公众号业务域名和js安全域名

按照步骤下载文件,将文件复制到项目根目录tomcat项目根目录

在浏览器能访问到文件就OK





比如项目名是:tb_project
本地测试调用:localhost:8080/tb_project/querylist
花生壳调用接口方式:xxx.xicp.io/querylist



到这里,就完全到位了,over

关于定时保存token到自己的服务器,推荐用 spring scheduled,简单,方便,实用

spring_scheduled.xml配置文件

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:task="http://www.springframework.org/schema/task"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/task
    http://www.springframework.org/schema/task/spring-task-3.0.xsd">

    <!-- 设置定时任务 -->
    <task:annotation-driven/>

    <!--扫描包路径-->
    <context:component-scan base-package="com.nas.timed"/>

    <!--注意这边需要配置供扫描的类-->
    <bean id="pickDealOrderTask" class="com.***.***.pickTask"/>

    <!--开启定时任务-->
    <task:scheduled-tasks scheduler="scheduler">
        <task:scheduled ref="pickTask" method="tickers" cron="0 */30 * * * ?"/>  <!--每30分钟执行一次,cron表达式很多种写法,问一下度娘-->
    </task:scheduled-tasks>

    <!--最大线程数-->
    <task:scheduler id="scheduler" pool-size="9" />

</beans>

配置文件加好之后,PickTask这个类下面的【tickers】方法,30分钟执行一次。

具体使用方法,问度娘。

有关数据存储做好之后,最好再存储一下缓存,比如redis,每次分享就不用去查询数据库了

祝君好运!!!

猜你喜欢

转载自blog.csdn.net/qq_36473199/article/details/79535848