From 0e8c93b45f2e6a111fb3b5eb2ca618db92f3a54c Mon Sep 17 00:00:00 2001 From: zhizhi wu <2377881365@qq.com> Date: Tue, 30 Nov 2021 11:25:03 +0800 Subject: [PATCH 1/2] fiot --- .../signin/api/FiotExplorerController.java | 56 +++++++++ .../com/ccsens/signin/bean/dto/FiotDto.java | 31 +++++ .../com/ccsens/signin/bean/vo/FiotVo.java | 18 +++ .../ccsens/signin/service/UserService.java | 2 +- .../java/com/ccsens/util/WebConstant.java | 5 + .../java/com/ccsens/util/wx/WxXcxUtil.java | 2 +- .../bean/po/wxfiotexplorer/FiotSignin.java | 74 ++++++++++++ .../po/wxfiotexplorer/FiotSigninResponse.java | 17 +++ .../bean/po/wxfiotexplorer/ResponseInfo.java | 30 +++++ .../com/ccsens/wechatutil/util/TC3Util.java | 107 ++++++++++++++++++ .../FiotExplorerSigninUtil.java | 79 +++++++++++++ 11 files changed, 419 insertions(+), 2 deletions(-) create mode 100644 signin/src/main/java/com/ccsens/signin/api/FiotExplorerController.java create mode 100644 signin/src/main/java/com/ccsens/signin/bean/dto/FiotDto.java create mode 100644 signin/src/main/java/com/ccsens/signin/bean/vo/FiotVo.java create mode 100644 wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/wxfiotexplorer/FiotSignin.java create mode 100644 wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/wxfiotexplorer/FiotSigninResponse.java create mode 100644 wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/wxfiotexplorer/ResponseInfo.java create mode 100644 wechatutil/src/main/java/com/ccsens/wechatutil/util/TC3Util.java create mode 100644 wechatutil/src/main/java/com/ccsens/wechatutil/wxfiotexplorer/FiotExplorerSigninUtil.java diff --git a/signin/src/main/java/com/ccsens/signin/api/FiotExplorerController.java b/signin/src/main/java/com/ccsens/signin/api/FiotExplorerController.java new file mode 100644 index 00000000..2b1c0c67 --- /dev/null +++ b/signin/src/main/java/com/ccsens/signin/api/FiotExplorerController.java @@ -0,0 +1,56 @@ +package com.ccsens.signin.api; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.servlet.ServletUtil; +import com.ccsens.signin.bean.dto.FiotDto; +import com.ccsens.signin.bean.dto.UserDto; +import com.ccsens.signin.bean.vo.FiotVo; +import com.ccsens.signin.bean.vo.UserVo; +import com.ccsens.signin.exception.UserLoginException; +import com.ccsens.util.JsonResponse; +import com.ccsens.util.WebConstant; +import com.ccsens.util.wx.WxXcxUtil; +import com.ccsens.wechatutil.wxfiotexplorer.FiotExplorerSigninUtil; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import java.util.Map; + +/** + * @description: + * @author: whj + * @time: 2021/11/30 10:42 + */ +@Slf4j +@Api(tags = "物联网", description = "") +@RestController +@RequestMapping("/fiot") +public class FiotExplorerController { + + @ApiOperation(value = "物联网用户登录") + @ApiImplicitParams({ + }) + @RequestMapping(value = "/signin", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8"}) + public JsonResponse userSignin(@ApiParam @Validated @RequestBody(required = true)FiotDto.Signin signin) throws Exception { + log.info("物联网用户登录:{}", signin); + String type = "FIOT"; + WxXcxUtil.WechatUser wechatUser = WxXcxUtil.getUserInfo(signin.getCode(), type); + String token = FiotExplorerSigninUtil.getToken(wechatUser.openid, signin.getNickName(), signin.getAvatar(), WxXcxUtil.appId(type)); + FiotVo.Signin vo = new FiotVo.Signin(); + vo.setToken(token); + log.info("物联网用户登录结果:{}", vo); + return JsonResponse.newInstance().ok(vo); + + } +} diff --git a/signin/src/main/java/com/ccsens/signin/bean/dto/FiotDto.java b/signin/src/main/java/com/ccsens/signin/bean/dto/FiotDto.java new file mode 100644 index 00000000..04031e92 --- /dev/null +++ b/signin/src/main/java/com/ccsens/signin/bean/dto/FiotDto.java @@ -0,0 +1,31 @@ +package com.ccsens.signin.bean.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; + +/** + * @description: + * @author: whj + * @time: 2021/11/30 10:45 + */ +public class FiotDto { + + @Data + @ApiModel("登录-请求") + public static class Signin { + @NotEmpty + @ApiModelProperty("登录code") + private String code; + @NotEmpty + @ApiModelProperty("用户名") + private String nickName; + @NotEmpty + @ApiModelProperty("头像") + private String avatar; + } + + +} diff --git a/signin/src/main/java/com/ccsens/signin/bean/vo/FiotVo.java b/signin/src/main/java/com/ccsens/signin/bean/vo/FiotVo.java new file mode 100644 index 00000000..9cc85766 --- /dev/null +++ b/signin/src/main/java/com/ccsens/signin/bean/vo/FiotVo.java @@ -0,0 +1,18 @@ +package com.ccsens.signin.bean.vo; + +import io.swagger.annotations.ApiModel; +import lombok.Data; + +/** + * @description: + * @author: whj + * @time: 2021/11/30 10:52 + */ +public class FiotVo { + + @Data + @ApiModel("物联网登录-返回") + public static class Signin{ + private String token; + } +} diff --git a/signin/src/main/java/com/ccsens/signin/service/UserService.java b/signin/src/main/java/com/ccsens/signin/service/UserService.java index d4300c7e..b3d77577 100644 --- a/signin/src/main/java/com/ccsens/signin/service/UserService.java +++ b/signin/src/main/java/com/ccsens/signin/service/UserService.java @@ -358,7 +358,7 @@ public class UserService implements IUserService { if (sign == null) { return null; } - redisUtil.set(StrUtil.format(WebConstant.Wx.SESSION_KEY, sign.getUserId(), gameType), wechatUser.session_key); + redisUtil.set(StrUtil.format(WebConstant.Wx.SESSION_KEY, sign.getUserId(), gameType), wechatUser.session_key, WebConstant.Time.DAY); return sign; } diff --git a/util/src/main/java/com/ccsens/util/WebConstant.java b/util/src/main/java/com/ccsens/util/WebConstant.java index c3efbef0..3f01b317 100644 --- a/util/src/main/java/com/ccsens/util/WebConstant.java +++ b/util/src/main/java/com/ccsens/util/WebConstant.java @@ -25,6 +25,10 @@ public class WebConstant { /* 导入WBS ,规定标签长度 */ public static final Integer LABEL_LENGTH = 6; + public static class Time { + public static final long DAY = 24 * 3600 * 1000; + } + /**属性名*/ public static class Field{ public static final String CODE = "code"; @@ -47,6 +51,7 @@ public class WebConstant { } public static final String SESSION_KEY = "{}_{}_session_key"; + public static final String OPEN_KEY = "{}_{}_openid_key"; } diff --git a/util/src/main/java/com/ccsens/util/wx/WxXcxUtil.java b/util/src/main/java/com/ccsens/util/wx/WxXcxUtil.java index 26ac0908..1cecbe6f 100644 --- a/util/src/main/java/com/ccsens/util/wx/WxXcxUtil.java +++ b/util/src/main/java/com/ccsens/util/wx/WxXcxUtil.java @@ -190,7 +190,7 @@ public class WxXcxUtil { private static final String key = ""; - private static String appId(String gameType){ + public static String appId(String gameType){ switch (gameType){ case "SP": return appid_sp; diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/wxfiotexplorer/FiotSignin.java b/wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/wxfiotexplorer/FiotSignin.java new file mode 100644 index 00000000..fa209c7e --- /dev/null +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/wxfiotexplorer/FiotSignin.java @@ -0,0 +1,74 @@ +package com.ccsens.wechatutil.bean.po.wxfiotexplorer; + +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.HexUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.ccsens.wechatutil.util.TC3Util; +import lombok.Data; + +import java.util.Calendar; +import java.util.Random; +import java.util.TimeZone; +import java.util.UUID; + +/** + * @description: + * @author: whj + * @time: 2021/11/26 9:33 + */ +@Data +public class FiotSignin { + /** + * 公共参数,本接口取值:AppGetTokenByWeiXin + */ + private String Action = "AppGetTokenByWeiXin"; + /** + * 公共参数,唯一请求 ID,可自行生成,推荐使用 uuid。定位问题时,需提供该次请求的 RequestId + */ + private String RequestId = UUID.randomUUID().toString(); + /** + * 公共参数,应用 AppKey ,用于标识对应的小程序或 App + */ + private String AppKey = null; + /** + * 公共参数,请求签名,需用户自行生成,用于校验请求的合法性 + */ + private String Signature = null; + /** + * 公共参数,请求的 UINX 时间戳(秒级) + */ + private long Timestamp = System.currentTimeMillis() / 1000; + /** + * 公共参数,随机正整数,与 Timestamp 联合起来,防止重放攻击 + */ + private int Nonce = new Random().nextInt(10000); + /** + * 微信用户的 OpenID 或 UnionID + */ + private String WxOpenID; + /** + * 昵称 + */ + private String NickName; + /** + * 头像 + */ + private String Avatar; + + public FiotSignin() { + } + + public FiotSignin(String openId, String nickName, String avatar, String appKey) throws Exception { + this(); + this.WxOpenID = openId; + this.NickName = nickName; + this.Avatar = avatar; + this.AppKey = appKey; + this.Signature = TC3Util.generateSignature("iot", "iot.cloud.tencent.com", this.Timestamp, null); + + } + + +} diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/wxfiotexplorer/FiotSigninResponse.java b/wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/wxfiotexplorer/FiotSigninResponse.java new file mode 100644 index 00000000..896e4286 --- /dev/null +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/wxfiotexplorer/FiotSigninResponse.java @@ -0,0 +1,17 @@ +package com.ccsens.wechatutil.bean.po.wxfiotexplorer; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @description: + * @author: whj + * @time: 2021/11/30 10:25 + */ +@Data +public class FiotSigninResponse { + @ApiModelProperty("截止时间,UINX 秒级时间戳") + private Long ExpireAt; + @ApiModelProperty("开发平台返回的 AccessToken,通过该 Token 进行登录后的接口请求") + private String Token; +} diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/wxfiotexplorer/ResponseInfo.java b/wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/wxfiotexplorer/ResponseInfo.java new file mode 100644 index 00000000..4aa5486d --- /dev/null +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/wxfiotexplorer/ResponseInfo.java @@ -0,0 +1,30 @@ +package com.ccsens.wechatutil.bean.po.wxfiotexplorer; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @description: + * @author: whj + * @time: 2021/11/30 10:17 + */ +@Data +public class ResponseInfo { + private Info Response; + private String RequestId; + + + @Data + public static class Info{ + @ApiModelProperty("数据,异常时,data为空") + private T Data; + @ApiModelProperty("错误码,正确返回时,error为空") + private ErrorInfo Error; + } + + @Data + public static class ErrorInfo{ + private String Code; + private String Message; + } +} diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/util/TC3Util.java b/wechatutil/src/main/java/com/ccsens/wechatutil/util/TC3Util.java new file mode 100644 index 00000000..f5b3be29 --- /dev/null +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/util/TC3Util.java @@ -0,0 +1,107 @@ +package com.ccsens.wechatutil.util; + +import cn.hutool.core.util.StrUtil; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import javax.xml.bind.DatatypeConverter; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; +import java.util.TreeMap; + +/** + * @description: + * @author: whj + * @time: 2021/11/29 17:50 + */ +public class TC3Util { + private final static Charset UTF8 = StandardCharsets.UTF_8; + private final static String SECRET_ID = "AKIDxhBRRAdplRpwnMfnfGaeRxDBsJTN0NTI"; + private final static String SECRET_KEY = "Zrte9MPFo68tMZU8WcXDeqnVx95rYzA6"; + private final static String CT_JSON = "application/json; charset=utf-8"; + + public static byte[] hmac256(byte[] key, String msg) throws Exception { + Mac mac = Mac.getInstance("HmacSHA256"); + SecretKeySpec secretKeySpec = new SecretKeySpec(key, mac.getAlgorithm()); + mac.init(secretKeySpec); + return mac.doFinal(msg.getBytes(UTF8)); + } + + public static String sha256Hex(String s) throws Exception { + MessageDigest md = MessageDigest.getInstance("SHA-256"); + byte[] d = md.digest(s.getBytes(UTF8)); + return DatatypeConverter.printHexBinary(d).toLowerCase(); + } + + public static String generateSignature(String service, String host, long timestamp, String payload) throws Exception { + //String service = "cvm"; + //String host = "cvm.tencentcloudapi.com"; +// String region = "ap-guangzhou"; +// String action = "DescribeInstances"; +// String version = "2017-03-12"; + String algorithm = "TC3-HMAC-SHA256"; + //String timestamp = "1551113065"; + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + // 注意时区,否则容易出错 + sdf.setTimeZone(TimeZone.getTimeZone("UTC")); + String date = sdf.format(new Date(Long.valueOf(timestamp + "000"))); + + // ************* 步骤 1:拼接规范请求串 ************* + String httpRequestMethod = "POST"; + String canonicalUri = "/"; + String canonicalQueryString = ""; + String canonicalHeaders = "content-type:application/json; charset=utf-8\n" + "host:" + host + "\n"; + String signedHeaders = "content-type;host"; + +// String payload = "{\"Limit\": 1, \"Filters\": [{\"Values\": [\"\\u672a\\u547d\\u540d\"], \"Name\": \"instance-name\"}]}"; + String hashedRequestPayload = StrUtil.isEmpty(payload) ? "" : sha256Hex(payload); + String canonicalRequest = httpRequestMethod + "\n" + canonicalUri + "\n" + canonicalQueryString + "\n" + + canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestPayload; + System.out.println(canonicalRequest); + + // ************* 步骤 2:拼接待签名字符串 ************* + String credentialScope = date + "/" + service + "/" + "tc3_request"; + String hashedCanonicalRequest = sha256Hex(canonicalRequest); + String stringToSign = algorithm + "\n" + timestamp + "\n" + credentialScope + "\n" + hashedCanonicalRequest; + System.out.println(stringToSign); + + // ************* 步骤 3:计算签名 ************* + byte[] secretDate = hmac256(("TC3" + SECRET_KEY).getBytes(UTF8), date); + byte[] secretService = hmac256(secretDate, service); + byte[] secretSigning = hmac256(secretService, "tc3_request"); + String signature = DatatypeConverter.printHexBinary(hmac256(secretSigning, stringToSign)).toLowerCase(); + System.out.println(signature); + + // ************* 步骤 4:拼接 Authorization ************* + String authorization = algorithm + " " + "Credential=" + SECRET_ID + "/" + credentialScope + ", " + + "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature; + + TreeMap headers = new TreeMap(); + headers.put("Authorization", authorization); + headers.put("Content-Type", CT_JSON); + headers.put("Host", host); +// headers.put("X-TC-Action", action); +// headers.put("X-TC-Timestamp", timestamp); +// headers.put("X-TC-Version", version); +// headers.put("X-TC-Region", region); + + StringBuilder sb = new StringBuilder(); + sb.append("curl -X POST https://").append(host) + .append(" -H \"Authorization: ").append(authorization).append("\"") + .append(" -H \"Content-Type: application/json; charset=utf-8\"") + .append(" -H \"Host: ").append(host).append("\""); +// .append(" -H \"X-TC-Action: ").append(action).append("\"") +// .append(" -H \"X-TC-Timestamp: ").append(timestamp).append("\"") +// .append(" -H \"X-TC-Version: ").append(version).append("\"") +// .append(" -H \"X-TC-Region: ").append(region).append("\"") + if (StrUtil.isNotEmpty(payload)) { + sb.append(" -d '").append(payload).append("'"); + } + + return sb.toString(); + } +} diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/wxfiotexplorer/FiotExplorerSigninUtil.java b/wechatutil/src/main/java/com/ccsens/wechatutil/wxfiotexplorer/FiotExplorerSigninUtil.java new file mode 100644 index 00000000..65bc60e1 --- /dev/null +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/wxfiotexplorer/FiotExplorerSigninUtil.java @@ -0,0 +1,79 @@ +package com.ccsens.wechatutil.wxfiotexplorer; + +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSONObject; +import com.ccsens.util.CodeError; +import com.ccsens.util.RedisUtil; +import com.ccsens.util.RestTemplateUtil; +import com.ccsens.util.exception.BaseException; +import com.ccsens.wechatutil.bean.po.wxfiotexplorer.FiotSignin; +import com.ccsens.wechatutil.bean.po.wxfiotexplorer.FiotSigninResponse; +import com.ccsens.wechatutil.bean.po.wxfiotexplorer.ResponseInfo; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; + +/** + * @description: + * @author: whj + * @time: 2021/11/25 11:47 + */ +@Slf4j +@Component +public class FiotExplorerSigninUtil { + + private final static String SIGN_URL = "https://iot.cloud.tencent.com/api/exploreropen/appapi"; + private final static String TOKEN_KEY = "FIOT_{}"; + @Value("${fiot.appId:}") + private String appKey; + @Value("${fiot.secret:}") + private String appSecret; + @Resource + private RedisUtil redisUtil; + private static FiotExplorerSigninUtil util; + + @PostConstruct + public void init(){ + util = this; + util.redisUtil = this.redisUtil; + util.appKey = this.appKey; + util.appSecret = this.appSecret; + } + + + + + public static String getToken(String openId, String nickName, String avatar, String appKey) throws Exception { + log.info("物联网查询token:{},{},{}", openId, nickName, avatar); + String key = StrUtil.format(TOKEN_KEY, openId); + Object o = util.redisUtil.get(key); + log.info("{}缓存的token:{}", key, o); + if (o != null) { + return (String) o; + } + // 查询token + + FiotSignin fiotSignin = new FiotSignin(openId, nickName, avatar, appKey); + log.info("登录:{}", fiotSignin); + String s = RestTemplateUtil.postBody(SIGN_URL, fiotSignin); + log.info("登录结果:{}", s); + if (StrUtil.isEmpty(s)) { + throw new BaseException(CodeError.THIRD_ERROR); + } + ResponseInfo res = JSONObject.parseObject(s, ResponseInfo.class); + if (res.getResponse() == null) { + throw new BaseException(CodeError.THIRD_ERROR); + } else if (res.getResponse().getError() != null ) { + throw new BaseException(CodeError.THIRD_ERROR.getCode(), res.getResponse().getError().getMessage()); + } + FiotSigninResponse data = res.getResponse().getData(); + util.redisUtil.set(key, data.getToken(), data.getExpireAt() - System.currentTimeMillis()/1000); + return data.getToken(); + } + + + +} From 5a72f70da19302b4c408ada8618f2ff4e1df6a76 Mon Sep 17 00:00:00 2001 From: zhizhi wu <2377881365@qq.com> Date: Fri, 3 Dec 2021 14:50:32 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E8=85=BE=E8=AE=AF=E7=89=A9=E8=81=94?= =?UTF-8?q?=E7=BD=91=20=E5=BE=AE=E4=BF=A1=E7=94=A8=E6=88=B7=E6=B3=A8?= =?UTF-8?q?=E5=86=8C=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../signin/api/FiotExplorerController.java | 10 +- .../com/ccsens/signin/bean/vo/FiotVo.java | 1 + signin/src/main/resources/application.yml | 4 +- .../com/ccsens/util/KCPlayerSignature.java | 2 +- .../com/ccsens/util/RestTemplateUtil.java | 32 ++++ .../bean/po/wxfiotexplorer/FiotSignin.java | 13 +- .../po/wxfiotexplorer/FiotSigninResponse.java | 3 + .../bean/po/wxfiotexplorer/ResponseInfo.java | 4 +- .../com/ccsens/wechatutil/util/TC3Util.java | 137 ++++++++++++++---- .../FiotExplorerSigninUtil.java | 26 ++-- wechatutil/src/main/resources/application.yml | 4 +- 11 files changed, 183 insertions(+), 53 deletions(-) diff --git a/signin/src/main/java/com/ccsens/signin/api/FiotExplorerController.java b/signin/src/main/java/com/ccsens/signin/api/FiotExplorerController.java index 2b1c0c67..76effe8f 100644 --- a/signin/src/main/java/com/ccsens/signin/api/FiotExplorerController.java +++ b/signin/src/main/java/com/ccsens/signin/api/FiotExplorerController.java @@ -12,6 +12,7 @@ import com.ccsens.signin.exception.UserLoginException; import com.ccsens.util.JsonResponse; import com.ccsens.util.WebConstant; import com.ccsens.util.wx.WxXcxUtil; +import com.ccsens.wechatutil.bean.po.wxfiotexplorer.FiotSigninResponse; import com.ccsens.wechatutil.wxfiotexplorer.FiotExplorerSigninUtil; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParams; @@ -45,12 +46,13 @@ public class FiotExplorerController { public JsonResponse userSignin(@ApiParam @Validated @RequestBody(required = true)FiotDto.Signin signin) throws Exception { log.info("物联网用户登录:{}", signin); String type = "FIOT"; - WxXcxUtil.WechatUser wechatUser = WxXcxUtil.getUserInfo(signin.getCode(), type); - String token = FiotExplorerSigninUtil.getToken(wechatUser.openid, signin.getNickName(), signin.getAvatar(), WxXcxUtil.appId(type)); + WxXcxUtil.WechatUser wechatUser = WxXcxUtil.getUserInfo(signin.getCode(), "yanyuan"); + log.info("用户账号:{}", wechatUser); + FiotSigninResponse response = FiotExplorerSigninUtil.getToken(wechatUser.openid, signin.getNickName(), signin.getAvatar(), WxXcxUtil.appId(type)); FiotVo.Signin vo = new FiotVo.Signin(); - vo.setToken(token); + vo.setToken(response.getToken()); + vo.setExpireAt(response.getExpireAt()); log.info("物联网用户登录结果:{}", vo); return JsonResponse.newInstance().ok(vo); - } } diff --git a/signin/src/main/java/com/ccsens/signin/bean/vo/FiotVo.java b/signin/src/main/java/com/ccsens/signin/bean/vo/FiotVo.java index 9cc85766..6cc79652 100644 --- a/signin/src/main/java/com/ccsens/signin/bean/vo/FiotVo.java +++ b/signin/src/main/java/com/ccsens/signin/bean/vo/FiotVo.java @@ -14,5 +14,6 @@ public class FiotVo { @ApiModel("物联网登录-返回") public static class Signin{ private String token; + private long expireAt; } } diff --git a/signin/src/main/resources/application.yml b/signin/src/main/resources/application.yml index e75cc2c3..ebc544af 100644 --- a/signin/src/main/resources/application.yml +++ b/signin/src/main/resources/application.yml @@ -1,5 +1,5 @@ spring: profiles: - active: prod - include: util-prod,common + active: test + include: util-test,common diff --git a/util/src/main/java/com/ccsens/util/KCPlayerSignature.java b/util/src/main/java/com/ccsens/util/KCPlayerSignature.java index c0967ee8..f6bf029d 100644 --- a/util/src/main/java/com/ccsens/util/KCPlayerSignature.java +++ b/util/src/main/java/com/ccsens/util/KCPlayerSignature.java @@ -59,7 +59,7 @@ public class KCPlayerSignature{ return strSign; } - private String base64Encode(byte[] buffer) { + public static String base64Encode(byte[] buffer) { Base64.Encoder encoder = Base64.getEncoder(); return encoder.encodeToString(buffer); } diff --git a/util/src/main/java/com/ccsens/util/RestTemplateUtil.java b/util/src/main/java/com/ccsens/util/RestTemplateUtil.java index dd840609..c9766256 100644 --- a/util/src/main/java/com/ccsens/util/RestTemplateUtil.java +++ b/util/src/main/java/com/ccsens/util/RestTemplateUtil.java @@ -1,5 +1,6 @@ package com.ccsens.util; +import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; @@ -87,6 +88,37 @@ public class RestTemplateUtil { return response.getBody(); } + public static String postBodySpecialHeader(String url, Object params, Map headMap) { + log.info("路径:{}, 参数:{}", url, params); + HttpHeaders httpHeaders = new HttpHeaders(); + MediaType type=MediaType.parseMediaType("application/json;charset=UTF-8"); + httpHeaders.setContentType(type); + if (CollectionUtil.isNotEmpty(headMap)) { + for (Map.Entry entry: headMap.entrySet()) { + httpHeaders.set(entry.getKey(), entry.getValue()); + } + } + +// MultiValueMap map=new LinkedMultiValueMap<>(); + + JSONObject json = JSON.parseObject(JSON.toJSONString(params)); + HttpEntity> objectHttpEntity = new HttpEntity<>(json,httpHeaders); + URI uri = null; + try { + uri = new URI(url); + }catch (URISyntaxException e) { + log.error("转换路径异常:{}", e); + throw new BaseException(CodeEnum.URL_ERROR); + } + log.info("uri:{}",uri); + log.info("json:{}",json); + log.info("objectHttpEntity:{}",objectHttpEntity); + + ResponseEntity response = util.restTemplate.postForEntity(uri, objectHttpEntity, String.class); + log.info("返回:{}", response); + return response.getBody(); + } + public static String postBody1(String url, List params) { log.info("路径:{}, 参数:{}", url, params); HttpHeaders httpHeaders = new HttpHeaders(); diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/wxfiotexplorer/FiotSignin.java b/wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/wxfiotexplorer/FiotSignin.java index fa209c7e..d8d556c5 100644 --- a/wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/wxfiotexplorer/FiotSignin.java +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/wxfiotexplorer/FiotSignin.java @@ -5,6 +5,7 @@ import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.HexUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.annotation.JSONField; import com.ccsens.wechatutil.util.TC3Util; import lombok.Data; @@ -23,38 +24,47 @@ public class FiotSignin { /** * 公共参数,本接口取值:AppGetTokenByWeiXin */ + @JSONField(name="Action") private String Action = "AppGetTokenByWeiXin"; /** * 公共参数,唯一请求 ID,可自行生成,推荐使用 uuid。定位问题时,需提供该次请求的 RequestId */ + @JSONField(name="RequestId") private String RequestId = UUID.randomUUID().toString(); /** * 公共参数,应用 AppKey ,用于标识对应的小程序或 App */ + @JSONField(name="AppKey") private String AppKey = null; /** * 公共参数,请求签名,需用户自行生成,用于校验请求的合法性 */ + @JSONField(name="Signature") private String Signature = null; /** * 公共参数,请求的 UINX 时间戳(秒级) */ + @JSONField(name="Timestamp") private long Timestamp = System.currentTimeMillis() / 1000; /** * 公共参数,随机正整数,与 Timestamp 联合起来,防止重放攻击 */ + @JSONField(name="Nonce") private int Nonce = new Random().nextInt(10000); /** * 微信用户的 OpenID 或 UnionID */ + @JSONField(name="WxOpenID") private String WxOpenID; /** * 昵称 */ + @JSONField(name="NickName") private String NickName; /** * 头像 */ + @JSONField(name="Avatar") private String Avatar; public FiotSignin() { @@ -66,7 +76,8 @@ public class FiotSignin { this.NickName = nickName; this.Avatar = avatar; this.AppKey = appKey; - this.Signature = TC3Util.generateSignature("iot", "iot.cloud.tencent.com", this.Timestamp, null); + JSONObject json = JSONObject.parseObject(JSONObject.toJSONString(this)); + this.Signature = TC3Util.generateSignature(json); } diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/wxfiotexplorer/FiotSigninResponse.java b/wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/wxfiotexplorer/FiotSigninResponse.java index 896e4286..98452ccf 100644 --- a/wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/wxfiotexplorer/FiotSigninResponse.java +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/wxfiotexplorer/FiotSigninResponse.java @@ -1,5 +1,6 @@ package com.ccsens.wechatutil.bean.po.wxfiotexplorer; +import com.alibaba.fastjson.annotation.JSONField; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @@ -11,7 +12,9 @@ import lombok.Data; @Data public class FiotSigninResponse { @ApiModelProperty("截止时间,UINX 秒级时间戳") + @JSONField(name = "ExpireAt") private Long ExpireAt; @ApiModelProperty("开发平台返回的 AccessToken,通过该 Token 进行登录后的接口请求") + @JSONField(name = "Token") private String Token; } diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/wxfiotexplorer/ResponseInfo.java b/wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/wxfiotexplorer/ResponseInfo.java index 4aa5486d..b4ff8c63 100644 --- a/wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/wxfiotexplorer/ResponseInfo.java +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/wxfiotexplorer/ResponseInfo.java @@ -10,7 +10,9 @@ import lombok.Data; */ @Data public class ResponseInfo { - private Info Response; + private int code; + private String msg; + private Info data; private String RequestId; diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/util/TC3Util.java b/wechatutil/src/main/java/com/ccsens/wechatutil/util/TC3Util.java index f5b3be29..cd52b272 100644 --- a/wechatutil/src/main/java/com/ccsens/wechatutil/util/TC3Util.java +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/util/TC3Util.java @@ -1,6 +1,9 @@ package com.ccsens.wechatutil.util; +import cn.hutool.core.codec.Base64Encoder; import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSONObject; +import com.ccsens.util.KCPlayerSignature; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; @@ -9,9 +12,7 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.TimeZone; -import java.util.TreeMap; +import java.util.*; /** * @description: @@ -22,7 +23,12 @@ public class TC3Util { private final static Charset UTF8 = StandardCharsets.UTF_8; private final static String SECRET_ID = "AKIDxhBRRAdplRpwnMfnfGaeRxDBsJTN0NTI"; private final static String SECRET_KEY = "Zrte9MPFo68tMZU8WcXDeqnVx95rYzA6"; + private final static String APP_KEY = "mEulfDBBOFeOjGavy"; + private final static String APP_SECRET = "FaFKWiwVUWbJmfxVnSdd"; private final static String CT_JSON = "application/json; charset=utf-8"; + //签名算法 + private static final String HMAC_ALGORITHM = "HmacSHA1"; + private static final String CONTENT_CHARSET = "UTF-8"; public static byte[] hmac256(byte[] key, String msg) throws Exception { Mac mac = Mac.getInstance("HmacSHA256"); @@ -37,6 +43,72 @@ public class TC3Util { return DatatypeConverter.printHexBinary(d).toLowerCase(); } + public static Map getHeaders(String service, long timestamp, String signature){ + Map headMap = new HashMap<>(); + String algorithm = "TC3-HMAC-SHA256"; + String signedHeaders = "content-type;host"; + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + // 注意时区,否则容易出错 + sdf.setTimeZone(TimeZone.getTimeZone("UTC")); + String date = sdf.format(new Date(Long.valueOf(timestamp + "000"))); + String credentialScope = date + "/" + service + "/" + "tc3_request"; + String authorization = algorithm + " " + "Credential=" + SECRET_ID + "/" + credentialScope + ", " + + "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature; + headMap.put("Authorization", authorization); + return headMap; + } + + public static void main(String[] args) throws Exception { + JSONObject json = new JSONObject(); + json.put("Action","AppGetTokenByWeiXin"); + json.put("RequestId","4c582f20-608a-41eb-8a04-b19cd341a328"); + json.put("AppKey","mEulfDBBOFeOjGavy"); + json.put("Timestamp","1638345578"); + json.put("Nonce","3493"); + json.put("WxOpenId","oOBSe5HAi885IlNeAAb3FQYxwExI"); + json.put("NickName","wu"); + json.put("Avatar","https://thirdwx.qlogo.cn/mmopen/vi_32/4KIkPtTLnuGdQPo1YqiaqRAgjyIPz4NyDzZVwwKJiatibWhszIH02XmWyqnl7LW1zYmRMbF2UYQG5o4N0cgyPH0qw/132"); + System.out.println(json); + String s = generateSignature(json); + System.out.println("s:" + s); +// String data = "Action=AppCreateCellphoneUser&AppKey=ahPxdK****TGrejd&CountryCode=86&Nonce=71087795&Password=My!P@ssword&PhoneNumber=13900000000&RequestId=8b8d499bbba1ac28b6da21b4&Timestamp=1546315200&VerificationCode=123456"; +// Mac mac = Mac.getInstance(HMAC_ALGORITHM); +// SecretKeySpec secretKey = new SecretKeySpec("NcbHqk****TCGbKnQH".getBytes(), HMAC_ALGORITHM); +// mac.init(secretKey); +// +// byte[] hash = mac.doFinal(data.getBytes()); +// String strSign = Base64Encoder.encode(hash); +// System.out.println("strSign:" + strSign); + } + + public static String generateSignature(Map param) throws Exception { + StringBuilder builder = new StringBuilder(); + Set keys = param.keySet(); + Object[] keyArr = keys.toArray(); + + Arrays.sort(keyArr); + for (Object keyObj: keyArr) { + String key = (String) keyObj; + if (param.get(key) == null || "".equals(param.get(key))) { + continue; + } + builder.append(key.replaceAll("_",".")).append("=").append(param.get(key)).append("&"); + } + System.out.println("builder:" + builder.toString()); + String plaintext = builder.substring(0, builder.length() - 1); + + + + Mac mac = Mac.getInstance(HMAC_ALGORITHM); + SecretKeySpec secretKey = new SecretKeySpec(APP_SECRET.getBytes(), HMAC_ALGORITHM); + mac.init(secretKey); + + byte[] hash = mac.doFinal(plaintext.getBytes()); +// String strSign = KCPlayerSignature.base64Encode(hash); + String strSign = Base64Encoder.encode(hash); +// strSign = strSign.replace(" ", "").replace("\n", "").replace("\r", ""); + return strSign; + } public static String generateSignature(String service, String host, long timestamp, String payload) throws Exception { //String service = "cvm"; //String host = "cvm.tencentcloudapi.com"; @@ -74,34 +146,35 @@ public class TC3Util { byte[] secretService = hmac256(secretDate, service); byte[] secretSigning = hmac256(secretService, "tc3_request"); String signature = DatatypeConverter.printHexBinary(hmac256(secretSigning, stringToSign)).toLowerCase(); - System.out.println(signature); - - // ************* 步骤 4:拼接 Authorization ************* - String authorization = algorithm + " " + "Credential=" + SECRET_ID + "/" + credentialScope + ", " - + "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature; - - TreeMap headers = new TreeMap(); - headers.put("Authorization", authorization); - headers.put("Content-Type", CT_JSON); - headers.put("Host", host); -// headers.put("X-TC-Action", action); -// headers.put("X-TC-Timestamp", timestamp); -// headers.put("X-TC-Version", version); -// headers.put("X-TC-Region", region); - - StringBuilder sb = new StringBuilder(); - sb.append("curl -X POST https://").append(host) - .append(" -H \"Authorization: ").append(authorization).append("\"") - .append(" -H \"Content-Type: application/json; charset=utf-8\"") - .append(" -H \"Host: ").append(host).append("\""); -// .append(" -H \"X-TC-Action: ").append(action).append("\"") -// .append(" -H \"X-TC-Timestamp: ").append(timestamp).append("\"") -// .append(" -H \"X-TC-Version: ").append(version).append("\"") -// .append(" -H \"X-TC-Region: ").append(region).append("\"") - if (StrUtil.isNotEmpty(payload)) { - sb.append(" -d '").append(payload).append("'"); - } - - return sb.toString(); + return signature; +// System.out.println(signature); +// +// // ************* 步骤 4:拼接 Authorization ************* +// String authorization = algorithm + " " + "Credential=" + SECRET_ID + "/" + credentialScope + ", " +// + "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature; +// +// TreeMap headers = new TreeMap(); +// headers.put("Authorization", authorization); +// headers.put("Content-Type", CT_JSON); +// headers.put("Host", host); +//// headers.put("X-TC-Action", action); +//// headers.put("X-TC-Timestamp", timestamp); +//// headers.put("X-TC-Version", version); +//// headers.put("X-TC-Region", region); +// +// StringBuilder sb = new StringBuilder(); +// sb.append("curl -X POST https://").append(host) +// .append(" -H \"Authorization: ").append(authorization).append("\"") +// .append(" -H \"Content-Type: application/json; charset=utf-8\"") +// .append(" -H \"Host: ").append(host).append("\""); +//// .append(" -H \"X-TC-Action: ").append(action).append("\"") +//// .append(" -H \"X-TC-Timestamp: ").append(timestamp).append("\"") +//// .append(" -H \"X-TC-Version: ").append(version).append("\"") +//// .append(" -H \"X-TC-Region: ").append(region).append("\"") +// if (StrUtil.isNotEmpty(payload)) { +// sb.append(" -d '").append(payload).append("'"); +// } +// +// return sb.toString(); } } diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/wxfiotexplorer/FiotExplorerSigninUtil.java b/wechatutil/src/main/java/com/ccsens/wechatutil/wxfiotexplorer/FiotExplorerSigninUtil.java index 65bc60e1..fb437233 100644 --- a/wechatutil/src/main/java/com/ccsens/wechatutil/wxfiotexplorer/FiotExplorerSigninUtil.java +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/wxfiotexplorer/FiotExplorerSigninUtil.java @@ -9,6 +9,7 @@ import com.ccsens.util.exception.BaseException; import com.ccsens.wechatutil.bean.po.wxfiotexplorer.FiotSignin; import com.ccsens.wechatutil.bean.po.wxfiotexplorer.FiotSigninResponse; import com.ccsens.wechatutil.bean.po.wxfiotexplorer.ResponseInfo; +import com.ccsens.wechatutil.util.TC3Util; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @@ -46,13 +47,13 @@ public class FiotExplorerSigninUtil { - public static String getToken(String openId, String nickName, String avatar, String appKey) throws Exception { + public static FiotSigninResponse getToken(String openId, String nickName, String avatar, String appKey) throws Exception { log.info("物联网查询token:{},{},{}", openId, nickName, avatar); String key = StrUtil.format(TOKEN_KEY, openId); Object o = util.redisUtil.get(key); log.info("{}缓存的token:{}", key, o); if (o != null) { - return (String) o; + return (FiotSigninResponse) o; } // 查询token @@ -61,17 +62,22 @@ public class FiotExplorerSigninUtil { String s = RestTemplateUtil.postBody(SIGN_URL, fiotSignin); log.info("登录结果:{}", s); if (StrUtil.isEmpty(s)) { + log.info("登录结果为空"); throw new BaseException(CodeError.THIRD_ERROR); } - ResponseInfo res = JSONObject.parseObject(s, ResponseInfo.class); - if (res.getResponse() == null) { - throw new BaseException(CodeError.THIRD_ERROR); - } else if (res.getResponse().getError() != null ) { - throw new BaseException(CodeError.THIRD_ERROR.getCode(), res.getResponse().getError().getMessage()); + JSONObject json = JSONObject.parseObject(s); + log.info("json:{}", json); + String dataKey = "data"; + String realDataKey = "Data"; + if (json.get(dataKey) == null || json.getJSONObject(dataKey).get(realDataKey) == null) { + log.info("数据异常"); + throw new BaseException(CodeError.THIRD_ERROR.getCode(), json.getString("msg")); } - FiotSigninResponse data = res.getResponse().getData(); - util.redisUtil.set(key, data.getToken(), data.getExpireAt() - System.currentTimeMillis()/1000); - return data.getToken(); + + FiotSigninResponse data = JSONObject.parseObject(json.getJSONObject(dataKey).getString(realDataKey), FiotSigninResponse.class); + util.redisUtil.set(key, data, data.getExpireAt() - System.currentTimeMillis()/1000); + log.info("登录返回:{}", data); + return data; } diff --git a/wechatutil/src/main/resources/application.yml b/wechatutil/src/main/resources/application.yml index b5408a3a..ebc544af 100644 --- a/wechatutil/src/main/resources/application.yml +++ b/wechatutil/src/main/resources/application.yml @@ -1,5 +1,5 @@ spring: profiles: - active: dev - include: util-dev,common + active: test + include: util-test,common