From 469558bddc1feebe298187eea8a3bdd138c10971 Mon Sep 17 00:00:00 2001 From: harrylee Date: Wed, 11 Sep 2019 15:06:50 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=20=E9=9B=86=E6=88=90=E4=BA=AC?= =?UTF-8?q?=E4=B8=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../zhyd/oauth/config/AuthDefaultSource.java | 2 +- .../me/zhyd/oauth/request/AuthJdRequest.java | 91 ++++++++++++++++++- .../me/zhyd/oauth/utils/GlobalAuthUtil.java | 20 ++++ .../java/me/zhyd/oauth/utils/UrlBuilder.java | 12 ++- 4 files changed, 122 insertions(+), 3 deletions(-) diff --git a/src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java b/src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java index f40384f..d30e627 100644 --- a/src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java +++ b/src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java @@ -690,7 +690,7 @@ public enum AuthDefaultSource implements AuthSource { @Override public String userInfo() { - return null; + return "https://api.jd.com/routerjson"; } @Override diff --git a/src/main/java/me/zhyd/oauth/request/AuthJdRequest.java b/src/main/java/me/zhyd/oauth/request/AuthJdRequest.java index f8bacec..04db60e 100644 --- a/src/main/java/me/zhyd/oauth/request/AuthJdRequest.java +++ b/src/main/java/me/zhyd/oauth/request/AuthJdRequest.java @@ -7,15 +7,24 @@ import me.zhyd.oauth.cache.AuthStateCache; import me.zhyd.oauth.config.AuthConfig; import me.zhyd.oauth.config.AuthDefaultSource; import me.zhyd.oauth.enums.AuthResponseStatus; +import me.zhyd.oauth.enums.AuthUserGender; import me.zhyd.oauth.exception.AuthException; import me.zhyd.oauth.model.AuthCallback; import me.zhyd.oauth.model.AuthResponse; import me.zhyd.oauth.model.AuthToken; import me.zhyd.oauth.model.AuthUser; +import me.zhyd.oauth.utils.GlobalAuthUtil; +import me.zhyd.oauth.utils.StringUtils; import me.zhyd.oauth.utils.UrlBuilder; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Map; +import java.util.TreeMap; + /** * 京东登录 + * link: http://open.jd.com/home/home#/doc/common?listId=717 * * @author harry.lee (harryleexyz@qq.com) * @since @@ -51,9 +60,86 @@ public class AuthJdRequest extends AuthDefaultRequest { .build(); } + /** + * link: http://jos.jd.com/api/showTools.htm?id=3051&groupId=106 + * postUrl: https://api.jd.com/routerjson?v=2.0&method=jingdong.user.getUserInfoByOpenId + * &app_key=x&access_token=x&360buy_param_json={"openId":"x"} + * ×tamp=2019-09-11 11:12:26&sign=DB5278CD12443BEA22C5E5EA05A30D2B + * @param authToken token信息 + * @return + */ @Override protected AuthUser getUserInfo(AuthToken authToken) { - return null; + UrlBuilder urlBuilder = UrlBuilder.fromBaseUrl(source.userInfo()) + .queryParam("access_token", authToken.getAccessToken()) + .queryParam("app_key", config.getClientId()) + .queryParam("method", "jingdong.user.getUserInfoByOpenId") + .queryParam("360buy_param_json", "{\"openId\":\"" + authToken.getOpenId() + "\"}") + .queryParam("timestamp", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))) + .queryParam("v", "2.0"); + urlBuilder.queryParam("sign", sign(urlBuilder.getReadParams())); + HttpResponse response = HttpRequest.post(urlBuilder.build(true)).execute(); + JSONObject object = JSONObject.parseObject(response.body()); + + this.checkResponse(object); + + JSONObject data = this.getUserDataJsonObject(object); + + return AuthUser.builder() + .uuid(authToken.getOpenId()) + .username(data.getString("nickname")) + .nickname(data.getString("nickname")) + .avatar(data.getString("imageUrl")) + .gender(AuthUserGender.getRealGender(data.getString("gendar"))) + .token(authToken) + .source(source.toString()) + .build(); + } + + /** + * 个人用户无法申请应用 + * 暂时只能参考官网给出的返回结果解析 + * link: http://open.jd.com/home/home#/doc/api?apiCateId=106&apiId=3051&apiName=jingdong.user.getUserInfoByOpenId + * + * @param object 请求返回结果 + * @return data JSONObject + */ + private JSONObject getUserDataJsonObject(JSONObject object) { + return object.getJSONObject("jingdong_user_getUserInfoByOpenId_response") + .getJSONObject("getuserinfobyappidandopenid_result") + .getJSONObject("data"); + } + + /** + * 宙斯签名规则过程如下: + * 将所有请求参数按照字母先后顺序排列,例如将access_token,app_key,method,timestamp,v 排序为access_token,app_key,method,timestamp,v + * 1.把所有参数名和参数值进行拼接,例如:access_tokenxxxapp_keyxxxmethodxxxxxxtimestampxxxxxxvx + * 2.把appSecret夹在字符串的两端,例如:appSecret+XXXX+appSecret + * 3.使用MD5进行加密,再转化成大写 + * link: http://open.jd.com/home/home#/doc/common?listId=890 + * link: https://github.com/pingjiang/jd-open-api-sdk-src/blob/master/src/main/java/com/jd/open/api/sdk/DefaultJdClient.java + * + * @param params + * @return + */ + private String sign(Map params) { + // 放入 TreeMap 排序 + Map treeMap = new TreeMap<>(params); + String appSecret = config.getClientSecret(); + StringBuilder signBuilder = new StringBuilder(appSecret); + for (Map.Entry entry : treeMap.entrySet()) { + String name = entry.getKey(); + String value = (String) entry.getValue(); + if (StringUtils.isNotEmpty(name) && StringUtils.isNotEmpty(value)) { + signBuilder.append(name).append(value); + } + } + signBuilder.append(appSecret); + try { + return GlobalAuthUtil.jdMd5(signBuilder.toString()); + } catch (Exception e) { + throw new AuthException("build sign to jdMd5 error"); + } } @Override @@ -84,6 +170,9 @@ public class AuthJdRequest extends AuthDefaultRequest { if (object.containsKey("msg")) { throw new AuthException(object.getString("msg")); } + if (object.containsKey("error_response")) { + throw new AuthException(object.getJSONObject("error_response").getString("zh_desc")); + } } @Override diff --git a/src/main/java/me/zhyd/oauth/utils/GlobalAuthUtil.java b/src/main/java/me/zhyd/oauth/utils/GlobalAuthUtil.java index 5c248f0..ea0f6b2 100644 --- a/src/main/java/me/zhyd/oauth/utils/GlobalAuthUtil.java +++ b/src/main/java/me/zhyd/oauth/utils/GlobalAuthUtil.java @@ -238,4 +238,24 @@ public class GlobalAuthUtil { return null == buffer ? "" : buffer.toString(); } + /** + * 京东md5加密 + * link: https://github.com/pingjiang/jd-open-api-sdk-src/blob/master/src/main/java/com/jd/open/api/sdk/internal/util/CodecUtil.java + * @param source + * @return + * @throws Exception + */ + public static String jdMd5(String source) throws Exception { + MessageDigest md = MessageDigest.getInstance("MD5"); + byte[] bytes = md.digest(source.getBytes(StandardCharsets.UTF_8)); + StringBuilder sign = new StringBuilder(); + for (int i = 0; i < bytes.length; i++) { + String hex = Integer.toHexString(bytes[i] & 0xff); + if (hex.length() == 1) { + sign.append("0"); + } + sign.append(hex.toUpperCase()); + } + return sign.toString(); + } } diff --git a/src/main/java/me/zhyd/oauth/utils/UrlBuilder.java b/src/main/java/me/zhyd/oauth/utils/UrlBuilder.java index c16ea0c..940c1af 100644 --- a/src/main/java/me/zhyd/oauth/utils/UrlBuilder.java +++ b/src/main/java/me/zhyd/oauth/utils/UrlBuilder.java @@ -2,7 +2,6 @@ package me.zhyd.oauth.utils; import cn.hutool.core.lang.Assert; import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.StrUtil; import lombok.Setter; import java.util.LinkedHashMap; @@ -26,6 +25,17 @@ public class UrlBuilder { } + /** + * 只读的 Map, clone 内部实现也是 putAll + * HashMap#putAll 可实现对 基本类型 和 String 类型的深度复制 + * + * @return Map + */ + @SuppressWarnings("unchecked") + public Map getReadParams() { + return (Map) ((LinkedHashMap) params).clone(); + } + /** * @param baseUrl 基础路径 * @return the new {@code UrlBuilder} -- GitLab