@@ -0,0 +1,40 @@ | |||||
package me.chanjar.weixin.common.util; | |||||
import lombok.extern.slf4j.Slf4j; | |||||
import org.apache.commons.codec.binary.Hex; | |||||
import javax.crypto.Mac; | |||||
import javax.crypto.spec.SecretKeySpec; | |||||
import java.security.InvalidKeyException; | |||||
import java.security.NoSuchAlgorithmException; | |||||
/** | |||||
* <pre> | |||||
* 签名工具类 | |||||
* Created by BinaryWang on 2018/7/11. | |||||
* </pre> | |||||
* | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
*/ | |||||
@Slf4j | |||||
public class SignUtils { | |||||
/** | |||||
* HmacSHA256 签名算法 | |||||
* | |||||
* @param message 签名数据 | |||||
* @param key 签名密钥 | |||||
*/ | |||||
public static String createHmacSha256Sign(String message, String key) { | |||||
try { | |||||
Mac sha256 = Mac.getInstance("HmacSHA256"); | |||||
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "HmacSHA256"); | |||||
sha256.init(secretKeySpec); | |||||
byte[] bytes = sha256.doFinal(message.getBytes()); | |||||
return Hex.encodeHexString(bytes).toUpperCase(); | |||||
} catch (NoSuchAlgorithmException | InvalidKeyException e) { | |||||
SignUtils.log.error(e.getMessage(), e); | |||||
} | |||||
return null; | |||||
} | |||||
} |
@@ -5,6 +5,8 @@ import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; | |||||
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo; | import cn.binarywang.wx.miniapp.bean.WxMaUserInfo; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import java.util.Map; | |||||
/** | /** | ||||
* 用户信息相关操作接口. | * 用户信息相关操作接口. | ||||
* | * | ||||
@@ -28,6 +30,16 @@ public interface WxMaUserService { | |||||
*/ | */ | ||||
WxMaUserInfo getUserInfo(String sessionKey, String encryptedData, String ivStr); | WxMaUserInfo getUserInfo(String sessionKey, String encryptedData, String ivStr); | ||||
/** | |||||
* 上报用户数据后台接口. | |||||
* <p>小游戏可以通过本接口上报key-value数据到用户的CloudStorage。</p> | |||||
* 文档参考https://developers.weixin.qq.com/minigame/dev/document/open-api/data/setUserStorage.html | |||||
* @param kvMap 要上报的数据 | |||||
* @param sessionKey 通过wx.login 获得的登录态 | |||||
* @param openid | |||||
*/ | |||||
void setUserStorage(Map<String, String> kvMap, String sessionKey, String openid) throws WxErrorException; | |||||
/** | /** | ||||
* 解密用户手机号信息. | * 解密用户手机号信息. | ||||
* | * | ||||
@@ -1,14 +1,25 @@ | |||||
package cn.binarywang.wx.miniapp.api.impl; | package cn.binarywang.wx.miniapp.api.impl; | ||||
import org.apache.commons.codec.digest.DigestUtils; | |||||
import cn.binarywang.wx.miniapp.api.WxMaService; | import cn.binarywang.wx.miniapp.api.WxMaService; | ||||
import cn.binarywang.wx.miniapp.api.WxMaUserService; | import cn.binarywang.wx.miniapp.api.WxMaUserService; | ||||
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; | import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; | ||||
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; | import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; | ||||
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo; | import cn.binarywang.wx.miniapp.bean.WxMaUserInfo; | ||||
import cn.binarywang.wx.miniapp.config.WxMaConfig; | |||||
import cn.binarywang.wx.miniapp.util.crypt.WxMaCryptUtils; | import cn.binarywang.wx.miniapp.util.crypt.WxMaCryptUtils; | ||||
import com.google.gson.JsonArray; | |||||
import com.google.gson.JsonObject; | |||||
import me.chanjar.weixin.common.error.WxError; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.common.util.SignUtils; | |||||
import org.apache.commons.codec.binary.Hex; | |||||
import org.apache.commons.codec.digest.DigestUtils; | |||||
import javax.crypto.Mac; | |||||
import javax.crypto.spec.SecretKeySpec; | |||||
import java.security.InvalidKeyException; | |||||
import java.security.NoSuchAlgorithmException; | |||||
import java.util.Map; | |||||
/** | /** | ||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | * @author <a href="https://github.com/binarywang">Binary Wang</a> | ||||
@@ -30,6 +41,30 @@ public class WxMaUserServiceImpl implements WxMaUserService { | |||||
return WxMaUserInfo.fromJson(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr)); | return WxMaUserInfo.fromJson(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr)); | ||||
} | } | ||||
@Override | |||||
public void setUserStorage(Map<String, String> kvMap, String sessionKey, String openid) throws WxErrorException { | |||||
final WxMaConfig config = this.service.getWxMaConfig(); | |||||
JsonObject param = new JsonObject(); | |||||
JsonArray array = new JsonArray(); | |||||
for (Map.Entry<String, String> e : kvMap.entrySet()) { | |||||
JsonObject jsonObject = new JsonObject(); | |||||
jsonObject.addProperty("key", e.getKey()); | |||||
jsonObject.addProperty("value", e.getValue()); | |||||
array.add(jsonObject); | |||||
} | |||||
param.add("kv_list", array); | |||||
String params = param.toString(); | |||||
String signature = SignUtils.createHmacSha256Sign(params, sessionKey); | |||||
String url = String.format("https://api.weixin.qq.com/wxa/set_user_storage" + | |||||
"?appid=%s&signature=%s&openid=%s&sig_method=%s", | |||||
config.getAppid(), signature, openid, "hmac_sha256"); | |||||
String result = this.service.post(url, params); | |||||
WxError error = WxError.fromJson(result); | |||||
if (error.getErrorCode() != 0) { | |||||
throw new WxErrorException(error); | |||||
} | |||||
} | |||||
@Override | @Override | ||||
public WxMaPhoneNumberInfo getPhoneNoInfo(String sessionKey, String encryptedData, String ivStr) { | public WxMaPhoneNumberInfo getPhoneNoInfo(String sessionKey, String encryptedData, String ivStr) { | ||||
return WxMaPhoneNumberInfo.fromJson(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr)); | return WxMaPhoneNumberInfo.fromJson(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr)); | ||||
@@ -1,5 +1,9 @@ | |||||
package cn.binarywang.wx.miniapp.api.impl; | package cn.binarywang.wx.miniapp.api.impl; | ||||
import cn.binarywang.wx.miniapp.test.TestConfig; | |||||
import com.google.common.collect.ImmutableMap; | |||||
import jdk.nashorn.internal.ir.annotations.Immutable; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | |||||
import org.testng.annotations.*; | import org.testng.annotations.*; | ||||
import cn.binarywang.wx.miniapp.api.WxMaService; | import cn.binarywang.wx.miniapp.api.WxMaService; | ||||
@@ -8,6 +12,8 @@ import cn.binarywang.wx.miniapp.bean.WxMaUserInfo; | |||||
import cn.binarywang.wx.miniapp.test.ApiTestModule; | import cn.binarywang.wx.miniapp.test.ApiTestModule; | ||||
import com.google.inject.Inject; | import com.google.inject.Inject; | ||||
import javax.management.ImmutableDescriptor; | |||||
import static org.testng.Assert.*; | import static org.testng.Assert.*; | ||||
/** | /** | ||||
@@ -54,4 +60,17 @@ public class WxMaUserServiceImplTest { | |||||
assertNotNull(phoneNoInfo); | assertNotNull(phoneNoInfo); | ||||
System.out.println(phoneNoInfo.toString()); | System.out.println(phoneNoInfo.toString()); | ||||
} | } | ||||
@Test | |||||
public void testGetSessionInfo() { | |||||
} | |||||
/** | |||||
* TODO 测试数据有问题,需要替换为正确的数据 | |||||
*/ | |||||
@Test | |||||
public void testSetUserStorage() throws WxErrorException { | |||||
this.wxService.getUserService().setUserStorage(ImmutableMap.of("1","2"), | |||||
"r7BXXKkLb8qrSNn05n0qiA",((TestConfig)this.wxService.getWxMaConfig()).getOpenid()); | |||||
} | |||||
} | } |
@@ -6,19 +6,12 @@ import com.github.binarywang.wxpay.constant.WxPayConstants.SignType; | |||||
import com.google.common.collect.Lists; | import com.google.common.collect.Lists; | ||||
import com.google.common.collect.Maps; | import com.google.common.collect.Maps; | ||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
import me.chanjar.weixin.common.util.BeanUtils; | |||||
import org.apache.commons.codec.binary.Hex; | |||||
import lombok.extern.slf4j.Slf4j; | |||||
import org.apache.commons.codec.digest.DigestUtils; | import org.apache.commons.codec.digest.DigestUtils; | ||||
import org.apache.commons.lang3.StringUtils; | import org.apache.commons.lang3.StringUtils; | ||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import javax.crypto.Mac; | |||||
import javax.crypto.spec.SecretKeySpec; | |||||
import java.lang.reflect.Field; | import java.lang.reflect.Field; | ||||
import java.lang.reflect.Modifier; | import java.lang.reflect.Modifier; | ||||
import java.security.InvalidKeyException; | |||||
import java.security.NoSuchAlgorithmException; | |||||
import java.util.*; | import java.util.*; | ||||
/** | /** | ||||
@@ -29,9 +22,8 @@ import java.util.*; | |||||
* | * | ||||
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | * @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | ||||
*/ | */ | ||||
@Slf4j | |||||
public class SignUtils { | public class SignUtils { | ||||
private static final Logger log = LoggerFactory.getLogger(SignUtils.class); | |||||
/** | /** | ||||
* 请参考并使用 {@link #createSign(Object, String, String, boolean)}. | * 请参考并使用 {@link #createSign(Object, String, String, boolean)}. | ||||
*/ | */ | ||||
@@ -91,26 +83,12 @@ public class SignUtils { | |||||
toSign.append("key=").append(signKey); | toSign.append("key=").append(signKey); | ||||
if (SignType.HMAC_SHA256.equals(signType)) { | if (SignType.HMAC_SHA256.equals(signType)) { | ||||
return createHmacSha256Sign(toSign.toString(), signKey); | |||||
return me.chanjar.weixin.common.util.SignUtils.createHmacSha256Sign(toSign.toString(), signKey); | |||||
} else { | } else { | ||||
return DigestUtils.md5Hex(toSign.toString()).toUpperCase(); | return DigestUtils.md5Hex(toSign.toString()).toUpperCase(); | ||||
} | } | ||||
} | } | ||||
private static String createHmacSha256Sign(String message, String key) { | |||||
try { | |||||
Mac sha256 = Mac.getInstance("HmacSHA256"); | |||||
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "HmacSHA256"); | |||||
sha256.init(secretKeySpec); | |||||
byte[] bytes = sha256.doFinal(message.getBytes()); | |||||
return Hex.encodeHexString(bytes).toUpperCase(); | |||||
} catch (NoSuchAlgorithmException | InvalidKeyException e) { | |||||
log.error(e.getMessage(), e); | |||||
} | |||||
return null; | |||||
} | |||||
/** | /** | ||||
* 校验签名是否正确. | * 校验签名是否正确. | ||||
* | * | ||||
@@ -146,11 +124,11 @@ public class SignUtils { | |||||
Map<String, String> result = Maps.newHashMap(); | Map<String, String> result = Maps.newHashMap(); | ||||
List<Field> fields = new ArrayList<>(Arrays.asList(bean.getClass().getDeclaredFields())); | List<Field> fields = new ArrayList<>(Arrays.asList(bean.getClass().getDeclaredFields())); | ||||
fields.addAll(Arrays.asList(bean.getClass().getSuperclass().getDeclaredFields())); | fields.addAll(Arrays.asList(bean.getClass().getSuperclass().getDeclaredFields())); | ||||
if(bean.getClass().getSuperclass().getSuperclass() == BaseWxPayRequest.class){ | |||||
if (bean.getClass().getSuperclass().getSuperclass() == BaseWxPayRequest.class) { | |||||
fields.addAll(Arrays.asList(BaseWxPayRequest.class.getDeclaredFields())); | fields.addAll(Arrays.asList(BaseWxPayRequest.class.getDeclaredFields())); | ||||
} | } | ||||
if(bean.getClass().getSuperclass().getSuperclass() == BaseWxPayResult.class){ | |||||
if (bean.getClass().getSuperclass().getSuperclass() == BaseWxPayResult.class) { | |||||
fields.addAll(Arrays.asList(BaseWxPayResult.class.getDeclaredFields())); | fields.addAll(Arrays.asList(BaseWxPayResult.class.getDeclaredFields())); | ||||
} | } | ||||