All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.xnx3.weixin.WeiXinAppletUtil Maven / Gradle / Ivy

The newest version!
package com.xnx3.weixin;

import net.sf.json.JSONObject;
import java.awt.image.BufferedImage;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.AlgorithmParameters;
import java.security.Security;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import com.xnx3.BaseVO;
import com.xnx3.DateUtil;
import com.xnx3.media.ImageUtil;
import com.xnx3.net.HttpResponse;
import com.xnx3.net.HttpsUtil;
import com.xnx3.weixin.bean.AccessToken;
import com.xnx3.weixin.vo.Jscode2sessionResultVO;
import com.xnx3.weixin.vo.PhoneVO;

/**
 * 微信小程序
 * @author 管雷鸣
 */
public class WeiXinAppletUtil implements java.io.Serializable{
	static HttpsUtil https;
	
	//获取普通access_token的url
	public static String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";	
	//access_token获取后使用的时长,单位为秒,官方给出的access_token获取后最大有效时间是7200秒,一个access_token的有效期最大只能是7200秒之内有效,超出后就要重新获取。这里设定获取到access_token后最大持续5000秒,超过后便再次获取新的access_token
	public static int ACCESS_TOKEN_DELAY_TIME = 5000;	
	
	private String appId; 	//appId 小程序的 app_id
	private String appSecret;	//appSecret 小程序的 app_secret
	
	private AccessToken accessToken;	//持久化access_token数据
	
	public boolean debug = true;	//调试日志是否打印
	
	static{
		https = new HttpsUtil();
	}
	
	/**
	 * 初始化
	 * @param appId 小程序的 app_id
	 * @param appSecret 小程序的 app_secret
	 */
	public WeiXinAppletUtil(String appId, String appSecret) {
		this.appId = appId;
		this.appSecret = appSecret;
	}
	
	/**
	 * https://api.weixin.qq.com/sns/jscode2session 根据code ,获取 openid、session_key 、 unionid
	 * @param code 微信小程序端,通过js获取到的code,也就是 wx.login 获取到的 code 
	 * @return {@link Jscode2sessionResultVO} 微信jscode2session接口所返回的结果。
	 * 	
    *
  • result : {@link Jscode2sessionResultVO}.FAILURE 调用失败,通过 .getInfo() 返回失败结果
  • *
  • result : {@link Jscode2sessionResultVO}.SUCCESS 调用成功。即可正常获得 openid、session_key 等
  • *
* @deprecated 请使用 {@link #loginByCode(String)} */ public Jscode2sessionResultVO jscode2session(String code){ Jscode2sessionResultVO vo = new Jscode2sessionResultVO(); HttpsUtil https = new HttpsUtil(); HttpResponse hr = https.get("https://api.weixin.qq.com/sns/jscode2session?appid="+appId+"&secret="+appSecret+"&js_code="+code+"&grant_type=authorization_code"); if(hr.getCode() - 200 == 0 && hr.getContent() != null && hr.getContent().indexOf("session_key") > -1){ JSONObject json = JSONObject.fromObject(hr.getContent()); vo.setOpenid(json.get("openid") == null? "":json.getString("openid")); vo.setUnionid(json.get("unionid") == null ? "":json.getString("unionid")); vo.setSessionKey(json.get("session_key") == null? "":json.getString("session_key")); }else{ vo.setBaseVO(BaseVO.FAILURE, hr.getContent()); } return vo; } /** * https://api.weixin.qq.com/sns/jscode2session 根据code ,获取 openid、session_key 、 unionid *
这个跟 {@link #jscode2session(String)} 一样,只不过这个loginByCode 更接近其意思 * @param code 微信小程序端,通过js获取到的code,也就是 wx.login 获取到的 code * @return {@link Jscode2sessionResultVO} 微信jscode2session接口所返回的结果。 *
    *
  • result : {@link Jscode2sessionResultVO}.FAILURE 调用失败,通过 .getInfo() 返回失败结果
  • *
  • result : {@link Jscode2sessionResultVO}.SUCCESS 调用成功。即可正常获得 openid、session_key 等
  • *
*/ public Jscode2sessionResultVO loginByCode(String code){ return jscode2session(code); } /** * 获取用户手机号 * @param sessionKey 用户通过小程序code登录成功后获得的sessionKey * @param encryptedData 小程序通过 open-type="getPhoneNumber" 按钮获取的手机号加密信息 * @param iv 小程序通过 open-type="getPhoneNumber" 按钮获取的 * @return 手机号。如果 {@link PhoneVO}.getResult 为 PhoneVO.SUCCESS ,那么便是获取成功,可以通过 {@link PhoneVO#getPhone()}获取手机号 */ public PhoneVO getPhone(String sessionKey, String encryptedData, String iv){ PhoneVO vo = new PhoneVO(); //被加密的数据 byte[] dataByte = Base64.getDecoder().decode(encryptedData); //加密秘钥 byte[] keyByte = Base64.getDecoder().decode(sessionKey); // 偏移量 byte[] ivByte = Base64.getDecoder().decode(iv); try { //如果密钥不足16位,那么就补足. 这个if 中的内容很重要 int base = 16; if (keyByte.length % base != 0) { int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0); byte[] temp = new byte[groups * base]; Arrays.fill(temp, (byte) 0); System.arraycopy(keyByte, 0, temp, 0, keyByte.length); keyByte = temp; } // 初始化 if (Security.getProvider(org.bouncycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME) == null){ Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); } Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); SecretKeySpec spec = new SecretKeySpec(keyByte, "AES"); AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES"); parameters.init(new IvParameterSpec(ivByte)); cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化 byte[] resultByte = cipher.doFinal(dataByte); if (null != resultByte && resultByte.length > 0) { String result = new String(resultByte, "UTF-8"); JSONObject json = JSONObject.fromObject(result); if(json.get("phoneNumber") != null){ vo.setPhone(json.getString("phoneNumber")); } if(json.get("countryCode") != null){ vo.setCountryCode(json.getString("countryCode")); } vo.setResult(PhoneVO.SUCCESS); return vo; }else{ vo.setBaseVO(PhoneVO.FAILURE, "weixin server response is null"); return vo; } } catch (Exception e) { e.printStackTrace(); vo.setBaseVO(PhoneVO.FAILURE, e.getMessage()); return vo; } } /** * 获取用户手机号 * @param sessionKey 用户通过小程序code登录成功后获得的sessionKey * @param requestPayloadString request payload 请求发过来的JSON格式字符串 * @return 手机号。如果 {@link PhoneVO}.getResult 为 PhoneVO.SUCCESS ,那么便是获取成功,可以通过 {@link PhoneVO#getPhone()}获取手机号 */ public PhoneVO getPhone(String sessionKey, String requestPayloadString){ PhoneVO vo = new PhoneVO(); if(requestPayloadString == null || requestPayloadString.length() == 0){ vo.setBaseVO(PhoneVO.FAILURE, "requestPayloadString 无数据"); return vo; } JSONObject json = JSONObject.fromObject(requestPayloadString); if(json.get("encryptedData") != null){ return getPhone(sessionKey, json.getString("encryptedData"), json.getString("iv")); }else{ vo.setBaseVO(PhoneVO.FAILURE, "encryptedData 不存在"); return vo; } } /** * 获取最新的普通access_token * @return AccessToken 若返回null,则获取access_token失败 */ public AccessToken getAccessToken(){ boolean refreshToken = false; //需重新刷新获取token,默认是不需要 if(accessToken == null){ accessToken = new AccessToken(); refreshToken = true; } //是否过时,需要重新获取token if(DateUtil.timeForUnix10()>accessToken.getGainTime()+ACCESS_TOKEN_DELAY_TIME){ refreshToken = true; } //避免一次可能网络中断,连续获取三次,减小误差 boolean success = !refreshToken; int i = 0; for (; i < 3 && !success ; i++) { success = refreshAccessToken(); } if(!success){ debug("连续获取"+i+"次access_token,均失败!" ); return null; }else{ return accessToken; } } /** * 刷新重新获取access_token * @return 获取成功|失败 */ private boolean refreshAccessToken(){ com.xnx3.net.HttpUtil httpUtil = new com.xnx3.net.HttpUtil(); HttpResponse httpResponse = httpUtil.get(ACCESS_TOKEN_URL.replace("APPID", this.appId).replace("APPSECRET", this.appSecret)); JSONObject json = JSONObject.fromObject(httpResponse.getContent()); if(json.get("errcode") == null){ //没有出错,获取access_token成功 accessToken.setAccess_token(json.getString("access_token")); accessToken.setExpires_in(json.getInt("expires_in")); accessToken.setGainTime(DateUtil.timeForUnix10()); return true; }else{ debug("获取access_token失败!返回值:"+httpResponse.getContent()); return false; } } /** * 调试日志打印 * @param message 日志内容 */ private void debug(String message){ if(debug){ System.out.println("WeiXinUtil:"+message); } } /** * 向小程序使用者发送一条小程序模版的订阅消息 *

必须小程序端弹出过是否允许订阅xxx消息,用户点击允许,才能发给用户

*

要是必须小程序端弹出的是否允许订阅xxx消息,只是允许一次,那只能成功给这个用户发送一次,第二次发送将报错。

* @param template_id 模板消息id,传入如 Mg3rJhtFtV4cd2PKDCwSBNOVKbXsX1Mhdh4mBrMQ111 * @param openid 接受用户的微信 openid * @param dataMap 模板消息的信息。如 *
	 * 		Map<String, String> dataMap = new HashMap<String, String>();
	 *		dataMap.put("first", "测试的");
	 * 		dataMap.put("tradeDateTime", "我是时间");
	 * 	
* 其中key便是 {{first.DATA}} 中的 first * @return */ public BaseVO sendSubscribeTemplateMessage(String template_id, String openid, Map dataMap){ JSONObject json = new JSONObject(); json.put("touser", openid); json.put("template_id", template_id); JSONObject dataJson = new JSONObject(); for(Map.Entry entry : dataMap.entrySet()){ JSONObject item = new JSONObject(); dataJson.put(entry.getKey(), getMessageValue(entry.getValue())); } json.put("data", dataJson); HttpResponse hr; try { hr = https.send("https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token="+getAccessToken().getAccess_token(), json.toString(), new HashMap()); if(hr.getCode() - 200 == 0){ //响应200,拿到数据了,将数据格式化 JSONObject resultJson = JSONObject.fromObject(hr.getContent()); Object errcode = resultJson.get("errcode"); if(errcode != null && errcode.toString().equals("0")){ Object msgid = resultJson.get("msgid"); if(msgid != null){ //成功 return BaseVO.success(msgid.toString()); } } return BaseVO.failure(hr.getContent()); }else{ return BaseVO.failure("weixin server response http code:"+hr.getCode()+","+hr.getContent()); } } catch (Exception e) { e.printStackTrace(); return BaseVO.failure(e.getMessage()); } } private static JSONObject getMessageValue(String value) { JSONObject json = new JSONObject(); json.put("value", value); return json; } /** * 生成微信小程序的二维码,通过该接口生成的小程序码,永久有效 *

获取小程序码,适用于需要的码数量较少的业务场景。通过该接口生成的小程序码,永久有效,有数量限制

*

使用示例:

*
	 * WeiXinAppletUtil util = new WeiXinAppletUtil("wx451fb5710f14exxx", "a4537821cb3828fb9ae98c6dbxxxxxx");
	 * BufferedImage bufferImage = util.getWXCode("path/index/index?id=1"); //生成二维码
	 * ImageUtil.saveToLocalhost(bufferImage, "jpg", "/images/12.jpg"); //保存到本地
	 * 
*

注意,与 wxacode.createQRCode 总共生成的码数量限制为 100,000,请谨慎调用

* @param path 生成二维码的路径,传入如 path/index/index ,也可以带参数,如 path/index/index?id=1 * @return 生成的二维码的 {@link BufferedImage} jpg格式的二维码,比如可以使用 ImageUtil.saveToLocalhost(bufferImage, "jpg", "/images/12.jpg"); 将其保存到磁盘。 *

如果返回 null ,则是生成二维码失败

*/ public BufferedImage getWXCode(String path) { JSONObject json = new JSONObject(); json.put("path", path); try { HttpURLConnection conn = (HttpURLConnection) new URL("https://api.weixin.qq.com/wxa/getwxacode?access_token="+getAccessToken().getAccess_token()).openConnection(); // 设置连接超时时间 conn.setConnectTimeout(30000); // 设置读取超时时间 conn.setReadTimeout(30000); conn.setRequestMethod("POST"); conn.setRequestProperty("Origin", "https://sirius.searates.com");// 主要参数 conn.setRequestProperty("Referer", "https://sirius.searates.com/cn/port?A=ChIJP1j2OhRahjURNsllbOuKc3Y&D=567&G=16959&shipment=1&container=20st&weight=1&product=0&request=&weightcargo=1&"); conn.setRequestProperty("X-Requested-With", "XMLHttpRequest");// 主要参数 // 需要输出 conn.setDoInput(true); // 需要输入 conn.setDoOutput(true); // 设置是否使用缓存 conn.setUseCaches(false); // 设置请求属性 conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); //application/x-www-form-urlencoded conn.setRequestProperty("Connection", "Keep-Alive");// 维持长连接 conn.setRequestProperty("Charset", "UTF-8"); conn.setRequestProperty("Accept-Charset", "UTF-8"); DataOutputStream dos=new DataOutputStream(conn.getOutputStream()); dos.writeBytes(json.toString()); dos.flush(); dos.close(); // 输出返回结果 InputStream input = conn.getInputStream(); return ImageUtil.inputStreamToBufferedImage(input); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy