博主使用jfinal框架
微信开发文档
https://mp.weixin.qq.com/wiki/11/74ad127cc054f6b80759c40f77ec03db.html
对接过程:
1.在公众号上面填写好域名指向、获取功能权限
2.看开发文档、获取签名算法、获取前端接口。
3.获取access_token、jsapi_ticket
4.验证
出现的问题:
签名不一致、
最后发现是写入txt文件的时候加了"\n\s";
建议:
从:http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign
这个验签中string1 与你打印出来一至的话
就是可能是timestamp、noncestr、jsapi_ticket出问题
如果是外写jsapi_ticket更要注意。
其他问题想看文档的:
附录5-常见错误及解决方法
签名算法用官网的例子 Sign类:
import java.util.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.io.UnsupportedEncodingException;
public class Sign {
public static void main(String[] args) {
String jsapi_ticket = "jsapi_ticket";
// 注意 URL 一定要动态获取,不能 hardcode
String url = "http://example.com";
Map<String, String> ret = sign(jsapi_ticket, url);
for (Map.Entry entry : ret.entrySet()) {
System.out.println(entry.getKey() + ", " + entry.getValue());
}
}
public static Map<String, String> sign(String jsapi_ticket, String url) {
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;
// System.out.println(string1);
try {
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
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;
}
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 (int)(System.currentTimeMillis() / 1000)+"";
}
}
写个周期任务 Task
由于微信的限制访问次数
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* Created by cwq on 2017/3/4.
* jdk 1.8
*/
public class Task {
private Runnable runnable = new Runnable() {
public void run() {
System.out.println("默认一个小时一次执行任务 !!");
}
};//执行线程
private long initialDelay = 0;//初始化延时
private long period = 1;//两次开始执行最小间隔时间
private TimeUnit unit = TimeUnit.HOURS;//计时单位
public Runnable getRunnable() {
return runnable;
}
public void setRunnable(Runnable runnable) {
this.runnable = runnable;
}
public long getInitialDelay() {
return initialDelay;
}
public void setInitialDelay(long initialDelay) {
this.initialDelay = initialDelay;
}
public long getPeriod() {
return period;
}
public void setPeriod(long period) {
this.period = period;
}
public TimeUnit getUnit() {
return unit;
}
public void setUnit(TimeUnit unit) {
this.unit = unit;
}
public void run() {
ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
service.scheduleAtFixedRate(runnable, initialDelay, period, unit);
}
调用类
package com.se.activities.utils.wechatjs.task;
import com.se.activities.utils.WechatConfig;
import com.se.activities.utils.wechatjs.FileOperation;
import com.se.sdk.oauth.util.HttpKit;
import com.se.utils.PropertyUtil;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
/**
* Created by cwq on 2017/3/4.
* jdk 1.8
*/
public class WeChatJsApiTask {
private final static Logger logger = LoggerFactory.getLogger(WeChatJsApiTask.class);//日志
private static String getJsApiAccessToken = "https://api.weixin.qq.com/cgi-bin/token?" +
"grant_type=client_credential&appid=APPID&secret=APPSECRET";//参考以下文档获取access_token(有效期7200秒,开发者必须在自己的服务全局缓存access_token)
private static String getJsApiTicket = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?" +
"access_token=ACCESS_TOKEN&type=jsapi";
private static String appId = "xxx";// 公众号的唯一标识
private static String appSecret = "xxx";//公众号的appsecret
public static String activitiesPicDir = "";//博主使用存文件的方式存tick 文件物理路径
private Runnable runnable = new Runnable() {
public void run() {
getTick();
}
};//执行线程
//更新微信的 js_ticket
private void getTick(){
String url = getJsApiAccessToken.replace("APPID", appId).replace("APPSECRET", appSecret);
String res = HttpKit.get(url);
String access_token = "0";
try {
JSONObject jsonObject = new JSONObject(res);
// {"access_token":"ACCESS_TOKEN","expires_in":7200}
// {"errcode":40013,"errmsg":"invalid appid"}
if(jsonObject.get("access_token")!=null) access_token = jsonObject.getString("access_token");
if(jsonObject.get("access_token")==null) {
System.out.println("@WeChatJsApiTask 取access_token失败:["+res+"]");
}
if(!access_token.equals("0")){
url = getJsApiTicket.replace("ACCESS_TOKEN", access_token);
res = HttpKit.get(url);
// System.out.println(res);
jsonObject = new JSONObject(res);
if(jsonObject.getString("errmsg").equals("ok")){
String ticket = jsonObject.getString("ticket");
File file = new File(activitiesPicDir+ File.separatorChar+"JsApiTask.txt");
FileOperation.createFile(file);
FileOperation.writeTxtFile(ticket,file);
System.out.println("@WeChatJsApiTask 取js_ticket:["+ticket+"]");
}else{
System.out.println("@WeChatJsApiTask 取ticket失败:["+res+"]");
}
}
/*
{"errcode":0,"errmsg":"ok",
"ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA",
"expires_in":7200
}
*/
} catch (JSONException e) {
logger.error("转换jsonObject有错,或者不在参数", e);
}
}
public void run() {
Task task = new Task();
task.setRunnable(runnable);
task.run();
System.out.println("执行周期性任务任务>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
System.out.println("微信JS-SDK凭证更新>>>>>>>");
System.out.println("初始化延时>>>>>>>>>>>>>>>" + task.getInitialDelay());
System.out.println("两次开始执行最小间隔时间>>>" + task.getPeriod());
System.out.println("计时单位>>>>>>>>>>>>>>>>>" + task.getUnit());
System.out.println("执行周期性任务任务<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
}
}
写入文件用到的类
package com.se.activities.utils.wechatjs;
import java.io.*;
/**
* Created by cwq on 2017/3/4.
* jdk 1.8
*/
public class FileOperation {
/**
* 创建文件
*
* @param fileName
* @return
*/
public static boolean createFile(File fileName) {
boolean flag = false;
try {
if (!fileName.exists()) {
fileName.createNewFile();
flag = true;
}
} catch (Exception e) {
e.printStackTrace();
}
return flag;
}
/**
* 读TXT文件内容
*
* @param fileName
* @return
*/
public static String readTxtFile(File fileName){
String result = "";
FileReader fileReader = null;
BufferedReader bufferedReader = null;
try {
fileReader = new FileReader(fileName);
bufferedReader = new BufferedReader(fileReader);
try {
String read = "";
while ((read = bufferedReader.readLine()) != null) {
result = result + read;//可怜的博主在这里+了"/n/s" 然后签名一直都不成功
}
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (bufferedReader != null) {
bufferedReader.close();
}
if (fileReader != null) {
fileReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
/**
*
* @param content
* @param fileName
* @return
* @throws Exception
*/
public static boolean writeTxtFile(String content, File fileName){
RandomAccessFile mm = null;
boolean flag = false;
FileOutputStream o = null;
try {
o = new FileOutputStream(fileName);
o.write(content.getBytes("utf-8"));
o.close();
flag = true;
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (mm != null) {
mm.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return flag;
}
}
控制器加签名 返回
HttpServletRequest req = getRequest();
String httpUrl = req.getScheme() + "://" + req.getServerName() + req.getContextPath()
+ req.getServletPath() + "?" + req.getQueryString();
httpUrl = httpUrl.split("#")[0];
Map<String, String> JsApiSign = createJsApiSign(URLDecoder.decode(httpUrl));
setAttr("JsApiSign", JsApiSign);
Map<String, String> JsApi = createJsApiSign(httpUrl);
JsApi.put("imgUrl", index_child + "/dow_app_icon.svg");
setAttr("JsApi", JsApi);
创建签名
private Map<String, String> createJsApiSign(String url) {
String jsapi_ticket = FileOperation.readTxtFile(new File(activitiesPicDir + File.separatorChar + "JsApiTask.txt"));
System.out.println("@handle createJsApiSign[jsapi_ticket:" + jsapi_ticket + "]");
Map<String, String> ret = Sign.sign(jsapi_ticket, url);
ret.put("appId", appId);
return ret;
}
jsp调用文件
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<c:if test="${JsApiSign!=null}">
<%--<script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>--%>
<script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
<script>
wx.config({
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: '${JsApiSign.appId}', // 必填,公众号的唯一标识
timestamp:${JsApiSign.timestamp}, // 必填,生成签名的时间戳
nonceStr: '${JsApiSign.nonceStr}', // 必填,生成签名的随机串
signature: '${JsApiSign.signature}',// 必填,签名,见附录1
jsApiList: ["onMenuShareTimeline", "onMenuShareAppMessage"] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});
var link = "${JsApiSign.url}";
if (link.indexOf("is_share") == -1) link = link + "&is_share=1";
wx.ready(function () {
wx.onMenuShareTimeline({
title: '', // 分享标题
link: link, // 分享链接
imgUrl: '${JsApi.imgUrl}', // 分享图标
success: function () {
// 用户确认分享后执行的回调函数
weChatShare(1);//import的时候 页面回调的方法
},
cancel: function () {
// 用户取消分享后执行的回调函数
}
});
wx.onMenuShareAppMessage({
title: '', // 分享标题
desc: '', // 分享描述
link: link, // 分享链接
imgUrl: '${JsApi.imgUrl}', // 分享图标
type: '', // 分享类型,music、video或link,不填默认为link
dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空
success: function () {
weChatShare(2);
},
cancel: function () {
}
});
});
</script>
</c:if>