正常分享状态显示:
分享异常状态显示:
一切变得那么的。。。无助!!!
开始以为在H5页面上添加一些东西即可,后来发现,完全不是自己想象的那个样子。
这个东西,对于一个从未用过微信JS的码农来说,或许要被带坑里去卡个几天!!!
以下是本人的一点点经验拿来和各位分享,希望刚接触到的少走一些弯路!
思路:
1.需要公众号一个,得到appid,appkey等等
2.需要在H5页面添加一些js方法,作用:调用微信api,获取access_token和jsapi_ticket【api的每天调用次数有限,每次调用返回的结果也不同,即,会改变之前的token,之前的就会失效,分享结果也失效】
3.调用api目的就是通过 access_token 和 jsapi_ticket 得到“nonceStr”和“signature”
4.将获得的参数注入H5页面中的【wx.config】中,加载、配置微信分享参数
over。。。
官方的示例代码:http://demo.open.weixin.qq.com/jssdk/sample.zip
官方文档:点击打开链接
【调用次数有限,请注意】
【确认签名算法是否正确工具】
http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign
=================以下是本人开发的全部代码,如有遗漏代码,麻烦留言提示一下,谢谢
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 +
"×tamp=" + timestamp +
"&url=" + url;
-
-
//6、将字符串进行sha1加密
-
String signature = WeChatUitl.getSHA1(params);
-
-
//7、微信appId
-
String appId = WeChatUitl.appIdWx;
-
-
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";
-
-
public
static
final String appIdWx =
"wx700000000000000";
// 微信appid---微信公众平台
-
public
static
final String appSecretWx =
"71**************************4";
// 微信AppSecret---微信公众平台
-
-
// 测试号,可以在微信公众平台直接创建一个,下面我会贴图考诉你怎么创建,这些都是要获取微信token用的
-
// public static final String appIdWx = "wx500000000000000"; // 微信appid---微信公众平台--测试号
-
// public static final String appSecretWx = "8e**************************ad"; // 微信AppSecret---微信公众平台--测试号
-
-
-
-
/**
-
* 获取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 +
"×tamp=" + 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代码
在需要分享的页面加入以下代码:
1.引入微信分享必须用到的 js
<script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js">
2.input标签
3.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>
-
===========================代码结束,接下来就是一个最麻烦的步骤了,公众平台配置
第二步:
1.注册/登录微信公众号,获取AppID和AppSecret
2.配置公众号业务域名和js安全域名
3,在公众号后台添加IP白名单
本机IP获取方式
调用“获取access_token”接口,返回结果。
错误信息显示ip无权限,就是这个。
在这里也一起把线上服务器的IP也一起设置进去,因为每次设置的时候都要找老板的手机来扫微信二维码,脑壳疼
注意,如果线上服务器ip地址有变动,必须修改微信公众号ip白名单配置
4.最好先去申请一个域名(花生壳),用于测试,不然只有发布到线上测试了【微信只认域名,不认ip+端口】
花生壳配置:
IDEA Tomcat 配置:
a.按照步骤下载文件,将文件复制到项目根目录和tomcat项目根目录
下载文件:MP_verify_1pnZudconNzHFayL.txt 注意不要修改文件名称
b.在浏览器能访问到文件就OK
比如项目名是:tb_project
本地测试调用:http://192.168.88.91:8080/tb_project/MP_verify_1pnZudconNzHFayL.txt
花生壳调用:http://5743xhb03479.xxx/MP_verify_1pnZudconNzHFayL.txt
到这里,就完全到位了,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,每次分享就不用去查询数据库了
祝君好运!!!