博主使用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>