-
java微信分享朋友圈_java开发微信分享到朋友圈功能
2021-02-12 17:46:31微信分享功能开发用了一天时间,把微信发送给朋友和分享到朋友圈功能开发出来,在这里给大家分享一下,避免大家走弯路。一、服务器端程序package com.wiimedia.controller;import java.io.IOException;import java....微信分享功能开发
用了一天时间,把微信发送给朋友和分享到朋友圈功能开发出来,在这里给大家分享一下,避免大家走弯路。
一、服务器端程序package com.wiimedia.controller;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.google.gson.Gson;
import com.wiimedia.model.Ticket;
import com.wiimedia.service.ArticleSolrService;
import com.wiimedia.service.TicketRepository;
import com.wiimedia.service.TicketRepositorySolr;
import com.wiimedia.utils.GetRandomStr;
import com.wiimedia.utils.SignatureBean;
import com.wiimedia.utils.weixin.WeixinUtil;
/**
*
*
*
Project:mryl_phone_v2
*
*
Package:com.wiimedia.controller
*
*
Description:微信分享Controller
*
*
Company:Wiimedia
*
*@Athor:SongJia
*
*@Date:2016-7-15 上午09:34:10
*
*/
@Controller
@RequestMapping("/WeixinshareController/Api/Inteface")
public class WeixinshareController {
@Autowired
private TicketRepositorySolr ticketRepositorySolr;
@RequestMapping("/getSignature")
public String getSignature( HttpServletRequest request,
HttpServletResponse response) throws IOException, ParseException{
//获取签名页面链接
String url = request.getParameter("url");
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//从数据库中获取标签,并检查标签是否过期
Ticket oldticket = ticketRepositorySolr.getTicketById("20160114wiimediamrylsong1152");
if(oldticket==null){//第一次访问,标签不存在。
executeTicket(response,"1",url,format);
return null;
}else{//标签存在,判断标签是否超时
String oldAcquiretime = oldticket.getAcquiretime();
long difference=format.parse(format.format(new Date())).getTime()-format.parse(oldAcquiretime).getTime();
if(difference>7100000){//标签超时,重新到微信服务器请求标签超时时间为7200秒(7200000毫秒)
executeTicket(response,"2",url,format);
return null;
}else{//标签未超时
/**
* 注意事项
* 1.签名用的noncestr和timestamp必须与wx.config中的nonceStr和timestamp相同。
* 2.签名用的url必须是调用JS接口页面的完整URL。
* 3.出于安全考虑,开发者必须在服务器端实现签名的逻辑。
*
****根据第1点要求 signature 配置的时候很容易出错,需要把生成 Ticket的 noncestr和 timestamp传给客户端***
*/
String signature = signature(oldticket.getTicket(),oldticket.getTimestamp(),oldticket.getNoncestr(),url);
SignatureBean signatureBean = new SignatureBean();
signatureBean.setNoncestr(oldticket.getNoncestr());
signatureBean.setSignature(signature);
signatureBean.setTimestamp(oldticket.getTimestamp());
signatureBean.setUrl(url);
response.setContentType("text/html;charset=UTF-8");
response.getWriter().print(new Gson().toJson(signatureBean));
return null;
}
}
}
/**
*
*
Project:mryl_phone_v2
*
*
:mryl_phone_v2
*
*
Description:更新和获取ticket的方法,因为用的solr所以更新和新增是一样的ID无则添加,有责更新
*
*
Company:Wiimedia
*
*@Athor:SongJia
*
*@Date:2016-7-15 上午09:45:00
*
*/
public void executeTicket(HttpServletResponse response,String flag,String url,SimpleDateFormat format) throws IOException{
//获取签名随即字符串
GetRandomStr randomStr = new GetRandomStr();
String noncestr = randomStr.getRandomString(15);
//获取签名时间戳
String timestamp = Long.toString(System.currentTimeMillis());
//请求accessToken
String accessTokenUrl ="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=您的APPID&secret=您的密匙";
String tokenJson = WeixinUtil.httpRequest(accessTokenUrl, "GET", null);
Gson gson = new Gson();
ShareAccess_Token token = gson.fromJson(tokenJson, ShareAccess_Token.class);
String to= token.getAccess_token();
//获取标签
String urlTicket ="https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+to+"&type=jsapi";
String ticketJson = WeixinUtil.httpRequest(urlTicket, "GET", null);
Ticket ticket = gson.fromJson(ticketJson, Ticket.class);
String t = ticket.getTicket();
//String uuid = UUID.randomUUID().toString().trim().replaceAll("-", "");
//我的Ticket ID是写死的
String acquiretime = format.format(new Date());
ticket.setTid("20160114wiimediamrylsong1152");
ticket.setAcquiretime(acquiretime);
ticket.setTimestamp(timestamp);
ticket.setNoncestr(noncestr);
//因为用的SOLR所以更新和添加的方法是一样的,可以根据自己具体需求进行修改,本文不再贴出代码.
if(flag.equals("2")){
ticketRepositorySolr.addTicketToSolr(ticket);
}else{
ticketRepositorySolr.addTicketToSolr(ticket);
}
/**
* 注意事项
* 1.签名用的noncestr和timestamp必须与wx.config中的nonceStr和timestamp相同。
* 2.签名用的url必须是调用JS接口页面的完整URL。
* 3.出于安全考虑,开发者必须在服务器端实现签名的逻辑。
*
*根据第1点要求 signature 配置的时候很容易出错,需要把生成 Ticket的 noncestr和 timestamp传给客户端*
*/
String signature = signature(t,timestamp,noncestr,url);
SignatureBean signatureBean = new SignatureBean();
signatureBean.setNoncestr(noncestr);
signatureBean.setSignature(signature);
signatureBean.setTimestamp(timestamp);
signatureBean.setUrl(url);
response.setContentType("text/html;charset=UTF-8");
response.getWriter().print(new Gson().toJson(signatureBean));
}
/**
*
*
Project:mryl_phone_v2
*
*
:mryl_phone_v2
*
*
Description:根据标签,时间戳,密匙,URL进行签名
*
*
Company:Wiimedia
*
*@Athor:SongJia
*
*@Date:2016-7-15 上午09:37:13
*
*/
private String signature(String jsapi_ticket, String timestamp, String noncestr, String url) {
jsapi_ticket = "jsapi_ticket=" + jsapi_ticket;
timestamp = "timestamp=" + timestamp;
noncestr = "noncestr=" + noncestr;
url = "url=" + url;
String[] arr = new String[] { jsapi_ticket, timestamp, noncestr, url };
// 将token、timestamp、nonce,url参数进行字典序排序
Arrays.sort(arr);
StringBuilder content = new StringBuilder();
for (int i = 0; i < arr.length; i++) {
content.append(arr[i]);
if (i != arr.length - 1) {
content.append("&");
}
}
MessageDigest md = null;
String tmpStr = null;
try {
md = MessageDigest.getInstance("SHA-1");
// 将三个参数字符串拼接成一个字符串进行sha1加密
byte[] digest = md.digest(content.toString().getBytes());
tmpStr = byteToStr(digest);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
content = null;
return tmpStr;
}
/**
* 将字节转换为十六进制字符串
*
* @param mByte
* @return
*/
private static String byteToHexStr(byte mByte) {
char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
char[] tempArr = new char[2];
tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
tempArr[1] = Digit[mByte & 0X0F];
String s = new String(tempArr);
return s;
}
/**
* 将字节数组转换为十六进制字符串
*
* @param byteArray
* @return
*/
private static String byteToStr(byte[] byteArray) {
String strDigest = "";
for (int i = 0; i < byteArray.length; i++) {
strDigest += byteToHexStr(byteArray[i]);
}
return strDigest;
}
class ShareAccess_Token{
private String access_token;
private String expires_in;
public String getAccess_token() {
return access_token;
}
public void setAccess_token(String accessToken) {
access_token = accessToken;
}
public String getExpires_in() {
return expires_in;
}
public void setExpires_in(String expiresIn) {
expires_in = expiresIn;
}
}
}
二、客户端代码.
var url = window.location.href;
var articleId = "";
var shareTitle="明日医疗资讯";
var shareImgUrl="";
var userinfo = localStorage.getItem("_userinfo");
var timestamp;
var noncestr;
var signature;
//获取签名
$.ajax({
type: "GET",
url: "WeixinshareController/Api/Inteface/getSignature",
//data:{timestamp:timestamp,noncestr:noncestr,url:url},
data:{url:url},
success: function(data){
var objData=JSON.parse(data);
timestamp=objData.timestamp;
noncestr=objData.noncestr;
signature=objData.signature;
console.log(objData);
wxShare();
}
});
function wxShare(){
wx.config({
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: '您的appid', // 和获取Ticke的必须一样------必填,公众号的唯一标识
timestamp:timestamp, // 必填,生成签名的时间戳
nonceStr: noncestr, // 必填,生成签名的随机串
signature: signature,// 必填,签名,见附录1
jsApiList: [
'onMenuShareAppMessage'
] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});
}
wx.ready(function(){
//config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,
//config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关
//接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
//----------“分享给朋友”
wx.onMenuShareAppMessage({
title: "明日医疗资讯", // 分享标题
desc: shareTitle, // 分享描述
link: url, // 分享链接
imgUrl: shareImgUrl, // 分享图标
type: '', // 分享类型,music、video或link,不填默认为link
dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空
success: function () {
// 用户确认分享后执行的回调函数、
},
cancel: function () {
// 用户取消分享后执行的回调函数
}
});
//------------"分享到朋友圈"
wx.onMenuShareTimeline({
title: '明日医疗资讯', // 分享标题
link: '', // 分享链接
imgUrl: shareImgUrl, // 分享图标
success: function () {
// 用户确认分享后执行的回调函数
},
cancel: function () {
// 用户取消分享后执行的回调函数
}
});
//-------------分享到QQ
wx.onMenuShareQQ({
title: '明日医疗资讯', // 分享标题
desc: shareTitle, // 分享描述
link: '', // 分享链接
imgUrl: shareImgUrl, // 分享图标
success: function () {
// 用户确认分享后执行的回调函数
},
cancel: function () {
// 用户取消分享后执行的回调函数
}
});
//-------------分享到QQ空间
wx.onMenuShareQZone({
title: '明日医疗资讯', // 分享标题
desc: shareTitle, // 分享描述
link: '', // 分享链接
imgUrl: shareImgUrl, // 分享图标
success: function () {
// 用户确认分享后执行的回调函数
},
cancel: function () {
// 用户取消分享后执行的回调函数
}
});
});
三、服务器需要的工具类和Model
① Ticketpackage com.wiimedia.model;
public class Ticket{
private String tid;
private String ticket;
private String errcode;
private String errmsg;
private String expires_in;
private String acquiretime;
private String noncestr;
private String timestamp;
public Ticket(String tid, String ticket, String errcode, String errmsg,
String expiresIn, String acquiretime, String noncestr,
String timestamp) {
super();
this.tid = tid;
this.ticket = ticket;
this.errcode = errcode;
this.errmsg = errmsg;
expires_in = expiresIn;
this.acquiretime = acquiretime;
this.noncestr = noncestr;
this.timestamp = timestamp;
}
public String getTid() {
return tid;
}
public void setTid(String tid) {
this.tid = tid;
}
public String getTicket() {
return ticket;
}
public void setTicket(String ticket) {
this.ticket = ticket;
}
public String getErrcode() {
return errcode;
}
public void setErrcode(String errcode) {
this.errcode = errcode;
}
public String getErrmsg() {
return errmsg;
}
public void setErrmsg(String errmsg) {
this.errmsg = errmsg;
}
public String getExpires_in() {
return expires_in;
}
public void setExpires_in(String expiresIn) {
expires_in = expiresIn;
}
public String getAcquiretime() {
return acquiretime;
}
public void setAcquiretime(String acquiretime) {
this.acquiretime = acquiretime;
}
public String getNoncestr() {
return noncestr;
}
public void setNoncestr(String noncestr) {
this.noncestr = noncestr;
}
public String getTimestamp() {
return timestamp;
}
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
}
② 添加到数据库的业务根据自己需要进行实现.
③ GetRandomStrpackage com.wiimedia.utils;
import java.util.Random;
public class GetRandomStr {
/**
*
*
Project:mryl_phone_v2
*
*
:mryl_phone_v2
*
*
Description:生成随即字符串
*
*
Company:Wiimedia
*
*@Athor:SongJia
*
*@Date:2016-7-14 上午11:14:46
*
*/
public String getRandomString(int length) {
String base = "abcdefghijklmnopqrstuvwxyz0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
}
④ SignatureBeanpackage com.wiimedia.utils;
public class SignatureBean {
private String noncestr;
private String url;
private String timestamp;
private String signature;
public String getNoncestr() {
return noncestr;
}
public void setNoncestr(String noncestr) {
this.noncestr = noncestr;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getTimestamp() {
return timestamp;
}
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
public String getSignature() {
return signature;
}
public void setSignature(String signature) {
this.signature = signature;
}
}
⑤ WeixinUtilpackage com.wiimedia.utils.weixin;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.URL;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
/**
*
*
Project:mryl_phone_v2
*
*
:mryl_phone_v2
*
*
Description:公众平台接口工具类
*
*
Company:Wiimedia
*
*@Athor:SongJia
*
*@Date:2016-7-15 上午09:37:13
*
*/
public class WeixinUtil {
/**
* 发起https请求并获取结果
*
* @param requestUrl 请求地址
* @param requestMethod 请求方式(GET、POST)
* @param outputStr 提交的数据
* @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)
*/
public static String httpRequest(String requestUrl, String requestMethod, String outputStr) {
StringBuffer buffer = new StringBuffer();
try {
// 创建SSLContext对象,并使用我们指定的信任管理器初始化
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();
return buffer.toString();
} catch (ConnectException ce) {
ce.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
}
四、至此,分享功能已经开发完成,但是,在生成signature的时候会遇到很多问题,这里提供一些wx.config失败的排错方法。
① 确认自己的生成的signature是否正确
在微信提供的http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign进行校验
② wx.config中使用的noncestr, timestamp与用以签名中的对应noncestr, timestamp是否一致一致…如上面(一.服务器代码)
(有可能因为JS页面加载顺序问题,服务器生成的signature,noncestr,timestamp在wx.config中没有获取到)。
③ 确认url是页面完整的url,包括GET参数部分
需要去掉#后面的部分
④ config 中的 appid 与用来获取 jsapi_ticket 的 appid 是否一致
⑤ 报错{errmsg:config:ok}是debug的正常返回把调试模式关掉就OK
wx.config debug: false,
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持PHP中文网。
更多java开发微信分享到朋友圈功能相关文章请关注PHP中文网!
本文原创发布php中文网,转载请注明出处,感谢您的尊重!
-
微信公众号分享到朋友圈的实现方式
2017-09-20 12:52:26一、概述微信JS-SDK是微信公众平台面向网页开发者提供的基于微信内的网页开发工具包。 通过使用微信JS-SDK,网页开发者可借助微信高效地使用拍照、选图、语音、位置等手机系统的能力,同时可以直接使用微信分享、扫...一、概述
微信JS-SDK是微信公众平台面向网页开发者提供的基于微信内的网页开发工具包。
通过使用微信JS-SDK,网页开发者可借助微信高效地使用拍照、选图、语音、位置等手机系统的能力,同时可以直接使用微信分享、扫一扫、卡券、支付等微信特有的能力,为微信用户提供更优质的网页体验。
此文档面向网页开发者介绍微信JS-SDK如何使用及相关注意事项。
JSSDK使用步骤步骤一:绑定域名
先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。
备注:登录后可在“开发者中心”查看对应的接口权限。步骤二:引入JS文件
在需要调用JS接口的页面引入如下JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.2.0.js
备注:支持使用 AMD/CMD 标准模块加载方法加载步骤三:通过config接口注入权限验证配置
所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用(同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用,目前Android微信客户端不支持pushState的H5新特性,所以使用pushState来实现web app的页面会导致签名失败,此问题会在Android6.2中修复)。
wx.config({ debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId: '', // 必填,公众号的唯一标识 timestamp: , // 必填,生成签名的时间戳 nonceStr: '', // 必填,生成签名的随机串 signature: '',// 必填,签名,见附录1 jsApiList: [] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2 });
步骤四:通过ready接口处理成功验证
wx.ready(function(){ // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。 });
步骤五:通过error接口处理失败验证
wx.error(function(res){ // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。 });
接口调用说明
所有接口通过wx对象(也可使用jWeixin对象)来调用,参数是一个对象,除了每个接口本身需要传的参数之外,还有以下通用参数:
1.success:接口调用成功时执行的回调函数。
2.fail:接口调用失败时执行的回调函数。
3.complete:接口调用完成时执行的回调函数,无论成功或失败都会执行。
4.cancel:用户点击取消时的回调函数,仅部分有用户取消操作的api才会用到。
5.trigger: 监听Menu中的按钮点击时触发的方法,该方法仅支持Menu中的相关接口。
备注:不要尝试在trigger中使用ajax异步请求修改本次分享的内容,因为客户端分享操作是一个同步操作,这时候使用ajax的回包会还没有返回。
以上几个函数都带有一个参数,类型为对象,其中除了每个接口本身返回的数据之外,还有一个通用属性errMsg,其值格式如下:
调用成功时:”xxx:ok” ,其中xxx为调用的接口名
用户取消时:”xxx:cancel”,其中xxx为调用的接口名
调用失败时:其值为具体错误信息以上代码是微信公众平台官网文档配置步骤! 官方文档
二、如何配置config?
- debug: 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
- appId:公众号的唯一标识,可以登录微信公众号平台获取
- timestamp:生成签名的时间戳
- nonceStr:生成签名的随机串
- signature:签名,见附录1
- jsApiList: 需要使用的JS接口列表,所有JS接口列表见附录2
以上这些信息中timestamp、nonceStr、signature这三项是调用接口后端给返回的! signature标签会牵扯到 sha1加密算法
如何找到appId标识?
登录公众号平台->基本配置:
timestamp是什么?
它就个很普通时间戳,如js是的(+new Date())一样,这个是后端开发给提供的.
nonceStr是什么?
它就是很普通的随机数,不长于 32 位;
signature是什么?
这个是一个签名,config中最复杂的一项了,要想拿到它的值可不容易。我们会单独说一下如何拿到这上签名值、
如何获取signature签名值?
具体的解释说明大家看官网文档吧的附录1-JS-SDK使用权限签名算法;
说说在获取到signature之前需要先获取其它几个值:
第一个access_tonken:
获取方式说明:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140183
https请求方式: GET
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET刚才说了appid的获取方式,现在说一下secret的获取方式:
对的是这里,这个值是谁设置的?一般情况找微信对应的开发人员问一下,切记这个自己不能随便重置的,这个值是开发人员在其它地方有使用的,如果你重置了可能导致公众号不能正常使用;
access_tonken有效期7200秒,开发者必须在自己的服务全局缓存access_token我们只需要了解一下就可以,不需要我们缓存它;
成功返回如下JSON:
{"access_token":"ACCESS_TOKEN","expires_in":7200}
第二个jsapi_ticket
https请求方式: GET
https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi这里只有access_token是我们上一步获取的,type=jsapi按原样拼接;这时我们会拿到jsapi_ticket
有效期7200秒,开发者必须在自己的服务全局缓存jsapi_ticket我们只需要了解一下就可以,不需要我们缓存它;
成功返回如下JSON:
{ "errcode":0, "errmsg":"ok", "ticket":"bxLdikRXVbTP5u5sUoXNKd8-41ZO3MhKoyN5OfkWITA", "expires_in":7200 }
获得jsapi_ticket之后,就可以生成JS-SDK权限验证的签名了。
第三个signature:
签名算法
签名生成规则如下:参与签名的字段包括noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳), url(当前网页的URL,不包含#及其后面部分) 。对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1。这里需要注意的是所有参数名均为小写字符。对string1作sha1加密,字段名和字段值都采用原始值,不进行URL 转义。
即signature=sha1(string1)。生成签名所需要的字段:
noncestr=Wm3WZYTPz0wzccnW jsapi_ticket=bxLdikRXVbTP5u5sUoXNKd8-41ZO3MhKoyN5OfkWITA timestamp=1414587457 url=http://mp.weixin.qq.com?params=value
上面这几个字段上几步都说过了,在这里要说一下url:
这个url对应的地址是访问当前页面的URL地址,不包括“#”号后需的数据;var url = location.protocol + "//"+location.hostname+location.pathname+location.search
步骤1. 对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1:
jsapi_ticket=bxLdikRXV2bTP5u5sU2oXNKd8-41ZO3Mh111111KoyN51O0fkWITAbxLdikRXVbTP5u5sUoXNKd8-41ZO3MhKoyN5OfkWITAqg&noncestr=Wm3WZYTPz0wzccnW×tamp=1414587457&url=http://mp.weixin.qq.com?params=value
步骤2. 对string1进行sha1签名,得到signature:
0f9d95740ceb90c2ea083d5c99sdadsasdasde7ed
注意事项
1.签名用的noncestr和timestamp必须与wx.config中的nonceStr和timestamp相同。
2.签名用的url必须是调用JS接口页面的完整URL。
3.出于安全考虑,开发者必须在服务器端实现签名的逻辑。
如出现invalid signature 等错误详见附录5常见错误及解决办法。config配置说了完了我们再说说怎么来分享朋友圈的配置:
先上代码:wx.ready(function(){ wx.onMenuShareTimeline({ title: '', // 分享标题 link: '', // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致 imgUrl: '', // 分享图标 success: function () { // 用户确认分享后执行的回调函数 }, cancel: function () { // 用户取消分享后执行的回调函数 } }); });
这里没有什么好说的了,说一下link的公众号JS安全域名是在那里配置的:
进入公众号->设置->公众号设置->点击设置(弹框中都有说明)好友打开打朋友圈,如果需要open_id的话怎么办?
如果你的微信公众号分享出去的地址是需要open_id的话,open_id是敏感字段,不能在link分享地址带出去,那么就要考虑如何在对方的微信中访问到它,这就需要网页授权了。
关于风页授权文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842
以下就是解决方式:
参考链接(请在微信客户端中打开此链接体验):
scope为snsapi_base
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx520c15f417810387&redirect_uri=https%3A%2F%2Fchong.qq.com%2Fphp%2Findex.php%3Fd%3D%26c%3DwxAdapter%26m%3DmobileDeal%26showwxpaytitle%3D1%26vb2ctag%3D4_2030_5_1194_60&response_type=code&scope=snsapi_bas
e&state=123#wechat_redirect这个链接中的参数字段我们都能很容易拿得到,直接把个放到link中是不能使用的,原因这是open.weixin.qq.com域名和我们配置的JS安全域名不一致是不行的。怎么办?解决办法就是做一个跳转页面。
我们新建一个html文件起名为oauth2.html,html中部分代码:
// 其实这里的redirect_uri是通过接受参数获取的不是写死在这里的,在这里说明一下; var redirect_uri = 'https%3A%2F%2Fchong.qq.com%2Fphp%2Findex.php%3Fd%3D%26c%3DwxAdapter%26m%3DmobileDeal%26showwxpaytitle%3D1%26vb2ctag%3D4_2030_5_1194_60'; var url = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx5110c15fwewe555417810387&redirect_uri='+redirect_uri+'&response_type=code&scope=snsapi_base&state=123#wechat_redirect ' location.href = url;
如何隐藏微信窗口右上角点击打开里面的分享朋友圈或分享给好友的功能:
// 这里是列表说明字段 var menuList = [ "menuItem:editTag",//编辑标签: "menuItem:delete",//删除: "menuItem:copyUrl",//复制链接: "menuItem:originPage",//原网页: "menuItem:readMode",//阅读模式: "menuItem:openWithQQBrowser",//在QQ浏览器中打开: "menuItem:openWithSafari",//在Safari中打开: "menuItem:share:email",//邮件: "menuItem:share:brand",//一些特殊公众号: "menuItem:share:appMessage", //发送给朋友 "menuItem:share:timeline", // 分享到朋友圈 "menuItem:share:qq",// 分享到QQ "menuItem:share:weiboApp",// 分享到Weibo "menuItem:favorite",// 收藏 "menuItem:share:facebook", //分享到FB "menuItem:share:QZone" //分享到 QQ 空间 ];
wx.hideMenuItems({ menuList: menuList// 要隐藏的菜单项,只能隐藏“传播类”和“保护类”按钮,所有menu项见附录3 });
以上就是微信分享功能的操作了,还有一些其它的操作相对来说比较简单了,这分享这块最难的还是配置上,刚入手的同学可能找不到头绪,主要还是在签名这里(me在这里吃过kui,幸好到demo,让开发的同学配置一下,前端直接调用接口,微信也是建议让后端配置,出于安全考虑,开发者必须在服务器端实现签名的逻辑),不过微信公众号文档已经给了签名算法的DEMO了(https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115)
本关分享的理念,但由于自己写作表达水平有限,有些描述不清楚的地方欢迎留言指正,谢谢!
-
java开发微信分享到朋友圈功能
2020-09-02 00:21:02主要为大家详细介绍了java开发微信发送给朋友和分享到朋友圈功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 -
SpringMVC实现微信链接分享到朋友圈显示图片功能微信JS-SDK调用步骤
2019-04-03 16:35:46SpringMVC实现微信链接分享到朋友圈显示图片功能微信JS-SDK调用步骤 一、登录微信平台 login url:https://mp.weixin.qq.com/ name: your name pwd: password 二、步骤 获取 access_token 1、登录平台 ...SpringMVC实现微信链接分享到朋友圈显示图片功能微信JS-SDK调用步骤
一、登录微信平台
login url:https://mp.weixin.qq.com/
name: your name
pwd: password
二、步骤 获取 access_token
1、登录平台
2、获取基本配置信息: 开发 --- 基本配置 , 获取信息如下:
- 开发者ID(AppID) : xxx。 (直接获取)
- 开发者密码(AppSecret):若忘记,需重置。 且需要管理员身份验证。 (xx)
- IP白名单:设置IP白名单后,获取 access_token接口才可调用成功。
3、获取 acccess_token: ( 获取文档 , 调试工具 )
- 请求方式: GET
- url: 获取URL
- 参数说明如下:
参数
是否必须
说明
grant_type
是
获取access_token填写 client_credential
appid
是
第三方用户唯一凭证
secret
是
第三方用户唯一凭证密钥,即appsecret
三、设置 JS 接口安全域名 (文档 )
1、路径: 设置 --- 公众号设置 --- 功能设置 --- JS接口安全域名 。
2、需要先将 MP_verify*** 开头的文件上传到服务器,才能保存成功。
3、图片参考如下:
四、获取 jsapi_ticket
1、文档 : 【 附录1-JS-SDK使用权限签名算法】
2、请求方式: GET
3、url : 获取URL
4、参数: access_token 。 (获取方式: 【二-3】)
五、签名算法
1、文档 : 【签名算法】
2、签名生成规则如下:参与签名的字段包括noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳), url(当前网页的URL,不包含#及其后面部分) 。对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1。这里需要注意的是所有参数名均为小写字符。对string1作sha1加密,字段名和字段值都采用原始值,不进行URL 转义。
3、步骤1:
jsapi_ticket=xxxx&noncestr=Wm3WZYTPz0wzccnW×tamp=1414587457&url=http://mp.weixin.qq.com?params=value
4、对string1进行sha1签名,得到 signature: (服务端实现)
5、注意事项如下:
- 签名用的noncestr和timestamp必须与 wx.config 中的nonceStr和timestamp相同。
- 签名用的url必须是调用JS接口页面的完整URL。
- 出于安全考虑,开发者必须在服务器端实现签名的逻辑。
- 如出现invalid signature 等错误详见附录5常见错误及解决办法。
6、微信 JS 接口签名校验工具: 签名校验
六、java版加密参考
1、定义 WeiXinConfig.java 存储相关配置信息 --- 主要字段参考
/** * description: 微信配置相关 信息 * @version v1.0 * @author w * @date 2019年2月22日下午3:12:11 **/ public class WeiXinConfig { private String timestamp = DateUtils.getDate("yyyyMMdd"); // 生成签名的时间戳 private String noncestr = "HaHa_Sir" ; // 生成签名的随机串 private String signature ; // 签名 private String ticket ; // private String url ; // 当前页面 url // ============= getter , setter ================= // }
2、定义WeiXinUtils.java 实现 微信 signature 签名算法 sha-1 加密
import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import org.slf4j.Logger; /** * description: 微信 signature 签名算法 sha-1 加密 * @version v1.0 * @author w * @date 2019年2月25日下午5:24:53 **/ public class WeiXinUtils { private static final Logger logger = org.slf4j.LoggerFactory.getLogger(WeiXinUtils.class); /** * description: 获取微信签名加密后的字符串 * @return String * @version v1.0 * @author w * @date 2019年2月25日 下午5:26:39 */ public static String signature( String str) { MessageDigest messageDigest = null ; logger.info("signature str :" + str); try { messageDigest = MessageDigest.getInstance("SHA-1"); byte[] digest = messageDigest.digest(str.getBytes()); return byteToStr(digest).toLowerCase(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return null; } /** * description: 微信 sha-1 加密后字符串返回 * @param config * @return String * @version v1.0 * @author w * @date 2019年2月25日 下午5:35:14 */ public static String signature(WeiXinConfig config) { String str = concatStr(config); return signature(str); } /** * description: 需要加密的参数,字符串拼接 * @param config * @return String * @version v1.0 * @author w * @date 2019年2月25日 下午5:42:28 */ public static String concatStr(WeiXinConfig config) { StringBuffer sb = new StringBuffer(); sb.append("jsapi_ticket=").append(config.getTicket()).append("&"); sb.append("noncestr=").append(config.getNoncestr()).append("&"); sb.append("timestamp=").append(config.getTimestamp()).append("&"); sb.append("url=").append(config.getUrl()); return sb.toString(); } /** * 将字节数组转换为十六进制字符串 * @param byteArray * @return */ private static String byteToStr(byte[] byteArray) { String strDigest = ""; for (int i = 0; i < byteArray.length; i++) { strDigest += byteToHexStr(byteArray[i]); } return strDigest; } /** * 将字节转换为十六进制字符串 * @param mByte * @return */ private static String byteToHexStr(byte mByte) { char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; char[] tempArr = new char[2]; tempArr[0] = Digit[(mByte >>> 4) & 0X0F]; tempArr[1] = Digit[mByte & 0X0F]; String s = new String(tempArr); return s; } }
七、SpringMVC提供微信配置相关信息
1、提供微信配置相关信息
/** * description: 获取微信 config 相关的配置信息 * @param config * @return WeiXinConfig * @version v1.0 * @author w * @date 2019年2月26日 上午11:49:27 */ @RequestMapping(value = { "/weixin/config"}) @ResponseBody public WeiXinConfig configInfo(WeiXinConfig config) { config.setTicket(JsapiTicketUtil.getJsapiTicket()); String signature = WeiXinUtils.signature(config); config.setSignature(signature); return config; }
八、前端调用相关配置信息
1、引入依赖 js 文件
<script src="/js/jquery-1.8.3.min.js" type="text/javascript" charset="utf-8"></script> <script src="http://res.wx.qq.com/open/js/jweixin-1.4.0.js" type="text/javascript"></script>
2、前端调用相关配置信息
<script type="text/javascript"> var url = location.href.split('#')[0]; $.ajax({ url : '${ctx}/weixin/config', type:'post', data:{"url":url}, cache:false, success:function(data){ wx.config({ debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参 appId: data.appId, // 必填,公众号的唯一标识 timestamp:data.timestamp , // 必填,生成签名的时间戳 nonceStr: data.noncestr, // 必填,生成签名的随机串 signature:data.signature, // 必填,签名 jsApiList: ["checkJsApi","onMenuShareTimeline","onMenuShareAppMessage" ,"updateTimelineShareData"] // 必填,需要使用的JS接口列表 }); } }); wx.ready(function () { //需在用户可能点击分享按钮前就先调用 // 获取“分享到朋友圈”按钮点击状态及自定义分享内容接口(即将废弃) wx.onMenuShareTimeline({ title: "分享标题123321", // 分享标题 link: location.href, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致 imgUrl: 'http://xx.com/static/img/vx-logo.jpg', // 分享图标 success: function () { // 分享成功,可进行分享数据统计 } }); // 获取“分享给朋友”按钮点击状态及自定义分享内容接口(即将废弃) wx.onMenuShareAppMessage({ title: $("head>title").text(), // 分享标题 desc: $("#span_description").text(), // 分享描述 link: location.href, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致 imgUrl: 'http://xx.com/static/img/vx-logo.jpg', // 分享图标 type: 'link', // 分享类型,music、video或link,不填默认为link dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空 success: function () { // 用户点击了分享后执行的回调函数 } }); }); </script>
九、功能测试
1、功能开发完成后,需部署到 【三-设置 JS 接口安全域名】的服务器下。
2、将已经配置分享功能页面URL粘贴到 微信web开发者工具 、或使用 扫一扫 二维码、或将URL发送到微信中打开。
3、微信web开发者工具 调试截图如下:
4、微信使用JS-SDK分享前后对比
十、总结
1、主要步骤如下:
- 获取 AppID 、 AppSecret 、添加IP白名单
- 获取access_token , 获取 jsapi_ticket
- 实现 signature sha-1 签名算法加密
- 前端调用配置信息
- 功能测试
2、获取 access_token 、jsapi_ticket 可在java服务端封装对应工具类进行获取。 如: HttpClient , java原生的 HttpURLConnection 等等。
3、微信开发文档中要求缓存 access_token,可使用 Redis , Ehcache 来实现,或者自行定义 静态全局变量也可以。 为了便于理解,本示例中没有提供。
4、调试过程中遇到错误请参考: 附录5-常见错误及解决方法 (tips: Ctrl+F ,快速定位)
5、上一操作错误都排除后,还是遇到 invalid signature 错误,请参考:invalid signature 签名无效 解决思路 。
十一、参考资料
1、 官方文档
3、微信web开发者工具 、Windows 64位版本 、 Windows 32位版本 、 Mac版本
-
微信小程序怎样分享一个链接到朋友圈,小程序分享到朋友圈,微信小程序如何分享到朋友圈,小程序链接分享到...
2020-07-07 11:26:01重磅消息重磅消息重磅消息重磅消息重磅消息重磅消息重磅消息 ...目前小程序已经支持分享到朋友圈, 只有安卓机好使。 体验地址:扫码进入小程序 ---- 点击右上角三个点 1.先看案例 2.微信开发文档: ...------2020-07-07------
重磅消息重磅消息重磅消息重磅消息重磅消息重磅消息重磅消息
目前小程序已经支持分享到朋友圈,
只有安卓机好使。
测试机型:小米9 miui12 20.7.2 微信版本7.0.16
体验地址:扫码进入小程序 ---- 点击右上角三个点
技术公众号
1.先看案例
2.微信开发文档:
https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share-timeline.html
https://developers.weixin.qq.com/miniprogram/dev/reference/api/Page.html#onShareTimeline
https://developers.weixin.qq.com/miniprogram/dev/api/share/wx.showShareMenu.html3.代码
//index.js //获取应用实例 const app = getApp() Page({ data: { }, onLoad: function () { wx.showShareMenu({ menus: ['shareAppMessage', 'shareTimeline'], success(res) { console.log(res) }, fail(e) { console.log(e) } }) }, onShareAppMessage(){ }, onShareTimeline(){ } })
4.我的微信 LJT-917
-
微信自定义分享到朋友圈标题,图片,链接
2020-09-29 15:35:52微信自定义朋友, 朋友圈分享 第一步: 验证服务器可用性 1: 进入 开发-基本配置-根据要求修改服务器配置 2: 在上图中编辑的服务器url对应的本网站文件中编写验证代码, 用于验证消息的确来自微信服务器 $signature =... -
【Android开发日记】微信API 分享到朋友圈
2014-07-22 01:28:372.下载SDK,将libammsdk.jar 拷到自己工程的libs中,添加。 代码: //创建实例 WeChatapi = WXAPIFactory.createWXAPI(ShareMainActivity.this, Constants.WeChat_APP_ID,true); //检查版本是否支持... -
微信公众号开发 自定义分享 从前台到Java后台 调用微信JS接口分享朋友圈
2017-09-15 01:21:2620180811写在前面的话 有很多人遇到问题之后问我,结果大多数是因为配置问题,所以请详细阅读前面的配置步骤。 20181016注意事项 ...id=mp1421141115中的分享接口,需要修改接口名称和参数列表... -
微信分享到朋友圈QQ.rar
2019-08-02 10:26:40微信分享到朋友圈QQ 使用.NET webform版本开发 亲测可以使用 -
h5 微信分享朋友和朋友圈
2018-04-06 17:57:07转自:https://www.cnblogs.com/liangzia/p/7569443.htmlh5 微信分享朋友和朋友圈生成JS-SDK权限验证签名实现发送给朋友和分享到朋友圈时内容参数自定义 一、微信JS-SDK1. 获得Access Tokenaccess token的获得方法... -
微信h5实现分享给朋友url改变_h5 微信分享朋友和朋友圈
2020-12-23 13:24:35生成JS-SDK权限验证签名实现发送给朋友和分享到朋友圈时内容参数自定义一、微信JS-SDK1. 获得Access Tokenaccess token的获得方法在前面有介绍,详情见微信公众平台开发(26) ACCESS TOKEN2. 获取jsapi_ticket生成... -
微信开发(2):微信js sdk分享朋友圈,朋友,获取config接口注入权限验证(java)
2017-08-05 16:53:50进行微信开发已经一阵子了,从最初的什么也不懂,到微信授权登录,分享,更改底部菜单,素材管理,等。 今天记录一下微信jssdk 的分享给朋友的功能,获取config接口注入。 官方文档走一下 简单说:四步走 1.绑定... -
微信公众号开发--微信JS-SDK分享到朋友圈和分享给朋友
2019-04-19 13:47:14微信公众号开发--微信JS-SDK分享到朋友圈和分享给朋友 -
python 朋友圈接口_微信开发Python微信-- 分享接口(分享到朋友圈、朋友、空间)...
2021-02-09 12:33:47本文将带你了解微信开发Python微信-- 分享接口(分享到朋友圈、朋友、空间),希望本文对大家学微信有所帮助。生成JS-SDK权限验证的签名获取signature(签名)首先要获得1、#获得jsapi_ticket2、#获取当前页面的url#获取... -
微信开发-发送给朋友,分享到朋友圈开发
2016-07-15 10:34:41微信分享功能 -
微信开发(2):微信js sdk分享朋友圈,朋友,获取config接口注入权限验证(转)...
2019-02-01 10:56:00进行微信开发已经一阵子了,从最初的什么也不懂,到微信授权登录,分享,更改底部菜单,素材管理,等。 今天记录一下微信jssdk 的分享给朋友的功能,获取config接口注入。 官方文档走一下 简单说:四步走 1.绑定域名... -
微信自定义分享功能;分享朋友,分享到朋友圈
2018-08-21 10:05:25经常在微信朋友圈分享一些好的文章或者让人哭或笑的段子,就在手机右上角的三个竖点一键分享就ok了,那么对于分享到朋友圈是怎么实现的呢?对于那种活动分享送流量是怎么定位分享者的呢?而想要将文章发送给朋友又是... -
java微信分享朋友圈_java_java开发微信分享到朋友圈功能,微信分享功能开发 用了一天 - phpStudy...
2021-02-12 17:46:31java开发微信分享到朋友圈功能微信分享功能开发用了一天时间,把微信发送给朋友和分享到朋友圈功能开发出来,在这里给大家分享一下,避免大家走弯路。一、服务器端程序package com.wiimedia.controller;import java.... -
asp.net 微信分享到朋友圈,分享给朋友接口
2016-09-27 09:41:13微信分享到朋友圈,分享给朋友说明: 转载:http://www.cnblogs.com/ysyn/archive/2015/07/23/4665897.html、 引言: 工作中开发微信网站,简称微网站。由于微网站的分享内容是系统自动选取的... -
java开发的微信分享到朋友圈功能
2016-12-12 18:03:00微信分享到朋友圈其实是微信中的一个内置功能,但当我们想自定义分享的内容的时候,就得自己写js代码调用微信的jsapi。 过程大体分为两步: 一、当你打开一个页面(你项目中需要加入分享朋友圈功能的页面)时... -
java微信公众平台分享朋友圈
2016-01-31 12:26:31首先微信其实已经自带分享到朋友圈,朋友,qq空间等功能,对于开发微信专门提供了一个接口,可以根据需要修改一些配置。例如修改要分享内容的头像,链接,描述等。 开发步骤: 1.在公众平台配置js-sdk接口 ... -
微信分享到朋友圈接口用法
2017-09-22 11:45:48share:timeline', function (argv) {})不再可以使用,但是微信提供的新的方法 js-SDK, 官方接口请参见 微信JSSDK说明文档 ,具体的方法官方文档都说的比较清楚了,本文只是记录下开发过程中遇到的问题以及解决方式。... -
【微信开发】-- 微信分享功能(分享到朋友和朋友圈显示图片和简介)
2018-06-05 13:45:02http://www.cnblogs.com/stoneniqiu/p/6286599.htmlhttps://www.cnblogs.com/zhengxu/articles/6743301.html这是微信分享的文章,如果小伙伴正在做这个功能的话,可以参看一下,希望对你有帮助。...
-
图的应用拓扑排序
-
松翰单片机SN8P2711B移动电源控制板ALTIUM设计硬件原理图+PCB+软件程序文件.zip
-
Android自动化测试
-
学习记录
-
试题总结
-
ffmpeg-win32-v3.2.4.zip
-
面向内存云的协调器选举策略
-
rhel7.7 x64安装oracle 11g 11.2.0.4_部署测试.txt
-
MySQL 高可用工具 heartbeat 实战部署详解
-
CSS 面试总结
-
svn: Can‘t read stdin: End of file found
-
元宵 | TcaplusDB君邀您来猜灯谜!
-
深究字符编码的奥秘,与乱码说再见
-
基于MEMS陀螺仪的光学图像拼接
-
关于易锦,web攻防渗透就业
-
基于python的dango框架购物商城毕业设计毕设源代码使用教程
-
时滞遗传网络的稳定性和Neimark-Sacker分叉分析
-
Hackintool.3.5.3__HeiPG.cn.dmg
-
BiLSTM.rar
-
如何做好需求管理_分享版.pdf