java微信分享

微信參考文檔https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html

微信官方文檔

步驟一:綁定域名

先登錄微信公衆平臺進入“公衆號設置”的“功能設置”裏填寫“JS接口安全域名”。

備註:登錄後可在“開發者中心”查看對應的接口權限。

        添加步驟:

            1.下載txt文件(MP_verify_8W1dJO7OTGYxxxx.txt),放到項目根目錄下;

            2.添加項目訪問域名地址,點擊保存。
 

步驟二:引入JS文件

在需要調用JS接口的頁面引入如下JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.4.0.js

如需進一步提升服務穩定性,當上述資源不可訪問時,可改訪問:http://res2.wx.qq.com/open/js/jweixin-1.4.0.js (支持https)。

備註:支持使用 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: '',// 必填,簽名
  jsApiList: [] // 必填,需要使用的JS接口列表
});

簽名算法見文末的附錄1,所有JS接口列表見文末的附錄2

 

步驟四:通過ready接口處理成功驗證

wx.ready(function(){
  // config信息驗證後會執行ready方法,所有接口調用都必須在config接口獲得結果之後,config是一個客戶端的異步操作,所以如果需要在頁面加載時就調用相關接口,則須把相關接口放在ready函數中調用來確保正確執行。對於用戶觸發時才調用的接口,則可以直接調用,不需要放在ready函數中。
});

 

步驟五:通過error接口處理失敗驗證

wx.error(function(res){
  // config信息驗證失敗會執行error函數,如簽名過期導致驗證失敗,具體錯誤信息可以打開config的debug模式查看,也可以在返回的res參數中查看,對於SPA可以在這裏更新簽名。
});

 代碼:

前端頁面  

需要引入http://res.wx.qq.com/open/js/jweixin-1.4.0.js

<script>
    
     $(function(){
            $.ajax({
                type : "post",
                url : "/wx/wxShare",
                dataType : "json",
                async : true,
                data:{url:'需要分享頁面的url'},
                success : function(data) {
                wx.config({
                        debug: false,////生產環境需要關閉debug模式
                        appId: data.appid,//appId通過微信服務號後臺查看
                        timestamp: data.timestamp,//生成簽名的時間戳
                        nonceStr: data.nonceStr,//生成簽名的隨機字符串
                        signature: data.signature,//簽名
                        jsApiList: [//需要調用的JS接口列表
                            'checkJsApi',//判斷當前客戶端版本是否支持指定JS接口
                            'onMenuShareTimeline',//分享給好友
                            'onMenuShareAppMessage'//分享到朋友圈
                        ]
                    });
                },
                error: function(xhr, status, error) {
                   // alert(status);
                    //alert(xhr.responseText);
                }
            })
            wx.ready(function () {
                wx.onMenuShareTimeline({
                   title: '',//分享的標題
                    desc: '朋友圈都被這個刷屏了,你也來曬一曬吧~', // 分享描述
                    link:'',  //注意這裏最好是http訪問全路徑 要不容易出問題 
                    imgUrl: '', // 分享圖標 http訪問全路徑
                    success: function () { 
                        // 用戶確認分享後執行的回調函數
                        //alert("分享成功");

                    }
                    cancel: function () { 
                        // 用戶取消分享後執行的回調函數
                        //alert("取消分享");
                    }  
                });
              //分享給朋友
              wx.onMenuShareAppMessage({
                     title: '',
                    desc: '朋友圈都被這個刷屏了,你也來曬一曬吧~', // 分享描述
                    link:'',
                    imgUrl: '', // 分享圖標 // 分享圖標
                    type: '', // 分享類型,music、video或link,不填默認爲link
                    dataUrl: '', // 如果type是music或video,則要提供數據鏈接,默認爲空
                    success: function () { 
                        // 用戶確認分享後執行的回調函數
                    },
                    cancel: function () { 
                        // 用戶取消分享後執行的回調函數
                    }
                });
                wx.error(function (res) {
                   // alert(res.errMsg);
                });
            });
        });
  </script>

後臺


import com.ljzforum.base.enums.APIResponseCode;
import com.ljzforum.base.vo.APIResultVo;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author : 恆果果
 * create at:  2019-12-09  10:48
 * @description: 微信分享等
 */
@RestController
public class WxController {

    @Resource
    private WXIConfig wxiConfig;

    @RequestMapping("/wxShare")
    public ModelAndView wxShare(String url){
    ModelAndView  view = new ModelAndView();
    view.addObject("data",wxShareN(url))
    return view ;
    }
}



    /**
     * 微信分享
     */
    public LinkedHashMap<String,Object> wxShareN(String url) {
        //微信分享
        LinkedHashMap<String,Object> map = new LinkedHashMap<>();

        try {
            String ticket = wxService.getJssdkTicketN();
            String shareUrl = url;
            map.put("appId", wxiConfig.getAppId());
            Map<String, String> signMap = WxJSSDKSign.sign(ticket, shareUrl);
            map.put("signMap",signMap);
        } catch (Exception e) {
            log.error("獲取微信的JSSDK接口失敗........", e);
        }
        return map;
    }

WXIConfig 


import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.ljzforum.common.constant.RedisContent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 微信接口的服務信息
 *
 * @author
 */
@Service
@Slf4j
public class WxService {
    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private WXIConfig wxiConfig;

    /**
     * 獲取JSSDK的祕鑰
     */
    public String getJssdkTicket(String access_token) {
        String ticket = (String) redisTemplate.opsForValue().get(RedisContent.WX_OPEN_TOKEN_TICKET + access_token);
        if (ticket == null) {
            Map<String, Object> params = new LinkedHashMap<String, Object>();
            params.put("access_token", access_token);
            params.put("type", "jsapi");
            JSONObject result = JSONUtil.parseObj(HttpUtil.get(WXIConfig.WX_JS_SDK_GET_TICKET, params));
            if (result.containsKey("errcode") && !result.getStr("errmsg").equals("ok")) {
                log.error("獲取ticket出錯:" + result.toString());
                return null;
            }
            ticket = result.getStr("ticket");
            redisTemplate.opsForValue().set(RedisContent.WX_OPEN_TOKEN_TICKET + access_token, ticket, 30, TimeUnit.MINUTES);
        }
        return ticket;
    }

    /**
     * 獲取JSSDK的祕鑰
     */
    public String getJssdkTicket() {
        String access_token = getCgiBinTokenN();
        if (access_token == null) {
            return null;
        }

        String ticket;
        try {
            ticket = getJssdkTicket(access_token);
        } catch (Exception e) {
            log.error("redisL裏的的token不正確,重新從接口獲取token");
            access_token = getCgiBinTokenN();
            ticket = getJssdkTicket(access_token);
        }
        return ticket;
    }

    /**
     * 獲取Token數據
     */
    public String getCgiBinTokenN() {
        String access_token = (String) redisTemplate.opsForValue().get(RedisContent.WX_CGI_BIN_TOKEN + wxiConfig.getAppId());
        if (access_token == null) {
            Map<String, Object> params = new LinkedHashMap<String, Object>();
            params.put("grant_type", "client_credential");
            params.put("appid", wxiConfig.getAppId());
            params.put("secret", wxiConfig.getAppSecret());
            JSONObject result = JSONUtil.parseObj(HttpUtil.get(WXIConfig.WX_CGI_BIN_GET_TOKEN, params));
            if (result.containsKey("errcode") && result.getStr("errcode") != null) {
                result = JSONUtil.parseObj(HttpUtil.get(WXIConfig.WX_CGI_BIN_GET_TOKEN, params));
            }
            access_token = result.getStr("access_token");
            redisTemplate.opsForValue().set(RedisContent.WX_CGI_BIN_TOKEN + wxiConfig.getAppId(), access_token, 30, TimeUnit.MINUTES);
        }
        return access_token;
    }

   
   

   }
   
WXIConfig


import com.ljzforum.common.util.CustomizedPropertyConfigurer;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Data
@Component
public class WXIConfig {

    //微信支付分配的商戶號
    @Value("${mch_id}")
    public String mchId;
    //支付的私人祕鑰
    @Value("${app_pay_key}")
    public String appPayKey;
    //獲取APPID
    @Value("${app_id}")
    public String appId;
    //獲取微信APP_SECRET
    @Value("${app_secret}")
    public String appSecret;
    //獲取小程序APPID
    @Value("${mini_appid}")
    public String miniAppid;
    //獲取小程序SECRET
    @Value("${mini_secret}")
    public String miniSecret;

    //臨時的整型參數值
    public static String QR_SCENE = "QR_SCENE";
    //臨時的字符串參數值
    public static String QR_STR_SCENE = "QR_STR_SCENE";
    //永久的整型參數值
    public static String QR_LIMIT_SCENE = "QR_LIMIT_SCENE";
    //永久的字符串參數值
    public static String QR_LIMIT_STR_SCENE = "QR_LIMIT_STR_SCENE";

    //統一下單
    public static String WX_UNIFIED_ORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder";

    //獲取access token
    public static String WX_TOKEN = "https://api.weixin.qq.com/cgi-bin/token";
    //獲取微信服務器IP地址
    public static String WX_TOKEN_CALLBACK_IP = "https://api.weixin.qq.com/cgi-bin/getcallbackip";
    //用戶同意授權,獲取code
    public static String WX_GET_USERINFO_AUTHORIZE = "https://open.weixin.qq.com/connect/oauth2/authorize";
    //通過code換取網頁授權access_token
    public static String WX_GET_USERINFO_ACCESS_TOKEN = "https://api.weixin.qq.com/sns/oauth2/access_token";
    //刷新access_token(如果需要)
    public static String WX_GET_USERINFO_REFRESH_TOKEN = "https://api.weixin.qq.com/sns/oauth2/refresh_token";
    //刷新拉取用戶信息(需scope爲 snsapi_userinfo)
    public static String WX_GET_USERINFO = "https://api.weixin.qq.com/sns/userinfo";
    //通過access_token、openid獲取用戶信息
    public static String WX_GET_INFO = "https://api.weixin.qq.com/cgi-bin/user/info";
    //獲取微信用戶素材
    public static String WX_BATCH_GET_MATERIAL = "https://api.weixin.qq.com/cgi-bin/material/batchget_material?access_token=";
    //獲取微信用戶素材總數
    public static String WX_GET_MATERIAL_COUNT = "https://api.weixin.qq.com/cgi-bin/material/get_materialcount?access_token=";
    //獲取微信永久素材
    public static String WX_GET_MATERIAL = "https://api.weixin.qq.com/cgi-bin/material/get_material?access_token=";
    //創建微信菜單
    public static String WX_CREATE_MENU = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=";
    //查詢微信菜單
    public static String WX_GET_MENU = "https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info?access_token=";
    //刪除微信菜單
    public static String WX_DELETE_MENU = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=";
    //微信退款
    public static String WX_REFUND_URL = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=";
    //獲取access_toke
    public static String WX_CGI_BIN_GET_TOKEN = "https://api.weixin.qq.com/cgi-bin/token";
    //長鏈接轉短鏈接接口
    public static String WX_GET_SHORT_URL = "https://api.weixin.qq.com/cgi-bin/shorturl?access_token=";
    //新增其他類型永久素材
    public static String WX_MATERIAL_ADD_MATERIAL = "http://api.weixin.qq.com/cgi-bin/material/add_material?access_token=%1s&type=%2s";

    /**
     * 獲取JSSDK調研說需要的鑰匙
     */
    public static String WX_JS_SDK_GET_TICKET = "https://api.weixin.qq.com/cgi-bin/ticket/getticket";


    //微信開放平臺公衆號消息加解密Key
    public static String WX_PLATFORM_ENCODING_AES_KEY = (String) CustomizedPropertyConfigurer.getContextProperty("wx_platform_encoding_aes_key");
    //微信開放平臺公衆號消息校驗Token
    public static String WX_PLATFORM_TOKEN = (String) CustomizedPropertyConfigurer.getContextProperty("wx_platform_token");
    //微信開放平臺APPID
    public static String WX_PLATFORM_APP_ID = (String) CustomizedPropertyConfigurer.getContextProperty("wx_platform_app_id");
    //微信開放平臺AppSecret
    public static String WX_PLATFORM_APP_SECRET = (String) CustomizedPropertyConfigurer.getContextProperty("wx_platform_app_secret");
    //獲取第三方平臺component_access_token
    public static String WX_GET_COMPONENT_ACCESS_TOKEN = "https://api.weixin.qq.com/cgi-bin/component/api_component_token";
    //獲取預授權碼pre_auth_code
    public static String WX_GET_PRE_AUTH_CODE = "https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode?component_access_token=";
    //獲取公衆號的接口調用憑據和授權信息
    public static String WX_API_QUERY_AUTH = "https://api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token=";

    public static String WX_API_REFRESH_TOKEN = "https://api.weixin.qq.com/cgi-bin/component/api_authorizer_token?component_access_token=";
    public static String WX_GET_AUTHORIZER_INFO = "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_info?component_access_token=";


    /**
     * 微信通知信息推送模版
     */
    //模版id
    public static String WX_TEMPLATE_ID_SUCCESS_SIGNUP = "w64cYpbQWD65KR6cziKipXNnBowEOSheqc7uoKUTR70";
    public static String WX_TEMPLATE_ID_ACTIVITY_MESSAGE = "jVXOi_SxSpGA9t9dHCHA7Ss50a4Y8KPLFVnCas5EaIc";
    //發送模版信息
    public static String WX_SEND_TEMPLATE = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=";

    //微信短鏈生成接口
    public static String WX_SHORT_URL = "https://api.weixin.qq.com/cgi-bin/shorturl?access_token=";
    public static String USER_LIST_URL = "https://api.weixin.qq.com/cgi-bin/user/get?access_token=";


    //生成帶參數的二維碼
    public static String WX_QRCODE_URL = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=";
    //通過ticket換取二維碼
    public static String WX_CGI_BIN_QRCODE = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=";


    //客服接口-發消息
    public static String WX_SEND_MSG_CUSTOM = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=";


    //圖片
    public static String WX_MATERIAL_IMAGE = "image";
    //語音
    public static String WX_MATERIAL_VOICE = "voice";
    //視頻
    public static String WX_MATERIAL_VIDEO = "video";
    //縮略圖
    public static String WX_MATERIAL_THUMB = "thumb";
}
WxJSSDKSign

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class WxJSSDKSign {
	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 + "&timestamp=" + timestamp + "&url=" + url;
//		System.out.println(string1);

		try {
			//生成一個 MessageDigest 對象
			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("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 Long.toString(System.currentTimeMillis() / 1000);
	}
}

注意事項:

JAVA, Node, Python 部分代碼只實現了簽名算法,需要開發者傳入 jsapi_ticket 和 url ,其中 jsapi_ticket 需要通過 http://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=ACCESS_TOKEN 接口獲取 url 爲調用頁面的完整 url 。

PHP 部分代碼包括了獲取 access_token 和 jsapi_ticket 的操作,只需傳入 appid 和 appsecret 即可,但要注意如果已有其他業務需要使用 access_token 的話,應修改獲取 access_token 部分代碼從全局緩存中獲取,防止重複獲取 access_token ,超過調用頻率。

注意事項:
1. jsapi_ticket 的有效期爲 7200 秒,開發者必須全局緩存 jsapi_ticket ,防止超過調用頻率。
 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章