From f448c9655f26c53252712cefcdfc3c97ad5da215 Mon Sep 17 00:00:00 2001 From: ShylockGou Date: Sun, 16 Oct 2016 23:32:33 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E5=85=AC=E4=BC=97=E5=8F=B7=E7=AC=AC?= =?UTF-8?q?=E4=B8=89=E6=96=B9=E5=B9=B3=E5=8F=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jfinal/weixin/sdk/api/AccessToken.java | 23 ++- .../jfinal/weixin/sdk/api/AccessTokenApi.java | 97 +++++----- .../jfinal/weixin/sdk/api/ApiConfigKit.java | 123 +++--------- .../sdk/api/component/AuthorizedApi.java | 11 ++ .../api/component/ComponentAccessToken.java | 97 ++++++++++ .../component/ComponentAccessTokenApi.java | 98 ++++++++++ .../sdk/api/component/ComponentAuthApi.java | 175 ++++++++++++++++++ .../ComponentAuthorizerAccessToken.java | 123 ++++++++++++ .../component/ComponentAuthorizerToken.java | 98 ++++++++++ .../ComponentAuthorizerTokenApi.java | 100 ++++++++++ .../api/component/ComponentPreAuthCode.java | 97 ++++++++++ .../component/ComponentPreAuthCodeApi.java | 107 +++++++++++ .../api/component/ComponentVerifyTicket.java | 59 ++++++ .../api/component/DefaultAuthorizedApi.java | 16 ++ .../authorizer/AuthorizationInfo.java | 105 +++++++++++ .../component/authorizer/AuthorizedInfo.java | 54 ++++++ .../component/authorizer/AuthorizerInfo.java | 94 ++++++++++ .../authorizer/type/BusinessType.java | 39 ++++ .../authorizer/type/FunctionType.java | 69 +++++++ .../authorizer/type/ServiceType.java | 36 ++++ .../component/authorizer/type/VerifyType.java | 47 +++++ .../authorizer/type/api/result/AuthInfo.java | 88 +++++++++ .../type/api/result/BusinessInfo.java | 75 ++++++++ .../authorizer/type/api/result/Category.java | 13 ++ .../type/api/result/FunctionInfo.java | 62 +++++++ .../type/api/result/FunctionScope.java | 13 ++ .../authorizer/type/api/result/TypeInfo.java | 13 ++ .../sdk/cache/DefaultAccessTokenCache.java | 62 ++++++- .../weixin/sdk/cache/IAccessTokenCache.java | 37 +++- .../sdk/cache/RedisAccessTokenCache.java | 64 ++++++- 30 files changed, 1931 insertions(+), 164 deletions(-) create mode 100644 src/main/java/com/jfinal/weixin/sdk/api/component/AuthorizedApi.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/api/component/ComponentAccessToken.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/api/component/ComponentAccessTokenApi.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/api/component/ComponentAuthApi.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/api/component/ComponentAuthorizerAccessToken.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/api/component/ComponentAuthorizerToken.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/api/component/ComponentAuthorizerTokenApi.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/api/component/ComponentPreAuthCode.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/api/component/ComponentPreAuthCodeApi.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/api/component/ComponentVerifyTicket.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/api/component/DefaultAuthorizedApi.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/AuthorizationInfo.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/AuthorizedInfo.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/AuthorizerInfo.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/BusinessType.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/FunctionType.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/ServiceType.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/VerifyType.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/api/result/AuthInfo.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/api/result/BusinessInfo.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/api/result/Category.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/api/result/FunctionInfo.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/api/result/FunctionScope.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/api/result/TypeInfo.java diff --git a/src/main/java/com/jfinal/weixin/sdk/api/AccessToken.java b/src/main/java/com/jfinal/weixin/sdk/api/AccessToken.java index 9e88c45..12d9188 100644 --- a/src/main/java/com/jfinal/weixin/sdk/api/AccessToken.java +++ b/src/main/java/com/jfinal/weixin/sdk/api/AccessToken.java @@ -1,17 +1,17 @@ /** * Copyright (c) 2011-2014, James Zhan 詹波 (jfinal@126.com). - * + *

* Licensed under the Apache License, Version 2.0 (the "License"); */ package com.jfinal.weixin.sdk.api; -import java.io.Serializable; -import java.util.Map; - import com.jfinal.weixin.sdk.utils.JsonUtils; import com.jfinal.weixin.sdk.utils.RetryUtils.ResultCheck; +import java.io.Serializable; +import java.util.Map; + /** * 封装 access_token */ @@ -19,12 +19,12 @@ public class AccessToken implements ResultCheck, Serializable { private static final long serialVersionUID = -822464425433824314L; - private String access_token; // 正确获取到 access_token 时有值 + private String access_token; // 正确获取到 access_token 时有值 private Integer expires_in; // 正确获取到 access_token 时有值 private Integer errcode; // 出错时有值 - private String errmsg; // 出错时有值 + private String errmsg; // 出错时有值 - private Long expiredTime; // 正确获取到 access_token 时有值,存放过期时间 + private Long expiredTime; // 正确获取到 access_token 时有值,存放过期时间 private String json; @SuppressWarnings("unchecked") @@ -39,13 +39,20 @@ public class AccessToken implements ResultCheck, Serializable { errmsg = (String) temp.get("errmsg"); if (expires_in != null) - expiredTime = System.currentTimeMillis() + ((expires_in -5) * 1000); + expiredTime = System.currentTimeMillis() + ((expires_in - 5) * 1000); } catch (Exception e) { throw new RuntimeException(e); } } + public AccessToken(String access_token, Integer expires_in) { + this.access_token = access_token; + this.expires_in = expires_in; + if (expires_in != null) + expiredTime = System.currentTimeMillis() + ((expires_in - 5) * 1000); + } + public String getJson() { return json; } diff --git a/src/main/java/com/jfinal/weixin/sdk/api/AccessTokenApi.java b/src/main/java/com/jfinal/weixin/sdk/api/AccessTokenApi.java index 5ca846c..14e391f 100644 --- a/src/main/java/com/jfinal/weixin/sdk/api/AccessTokenApi.java +++ b/src/main/java/com/jfinal/weixin/sdk/api/AccessTokenApi.java @@ -1,65 +1,68 @@ /** * Copyright (c) 2011-2014, James Zhan 詹波 (jfinal@126.com). - * + *

* Licensed under the Apache License, Version 2.0 (the "License"); */ package com.jfinal.weixin.sdk.api; import com.jfinal.kit.StrKit; +import com.jfinal.log.Log; +import com.jfinal.weixin.sdk.api.component.AuthorizedApi; +import com.jfinal.weixin.sdk.api.component.ComponentAuthApi; +import com.jfinal.weixin.sdk.api.component.ComponentAuthorizerAccessToken; import com.jfinal.weixin.sdk.cache.IAccessTokenCache; import com.jfinal.weixin.sdk.kit.ParaMap; import com.jfinal.weixin.sdk.utils.HttpUtils; import com.jfinal.weixin.sdk.utils.RetryUtils; + import java.util.Map; +import java.util.ServiceLoader; import java.util.concurrent.Callable; /** * 认证并获取 access_token API * http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96access_token - * + *

* AccessToken默认存储于内存中,可设置存储于redis或者实现IAccessTokenCache到数据库中实现分布式可用 - * + *

* 具体配置: *

  * ApiConfigKit.setAccessTokenCache(new RedisAccessTokenCache());
  * 
*/ public class AccessTokenApi { - + public static final Log log = Log.getLog(AccessTokenApi.class); + // 利用 appId 与 accessToken 建立关联,支持多账户 + static IAccessTokenCache accessTokenCache = ApiConfigKit.getAccessTokenCache(); + static ServiceLoader apiLoader = ServiceLoader.load( + AuthorizedApi.class); + static final AuthorizedApi authorizedApi = apiLoader.iterator().next(); // "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET"; - private static String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential"; + private static String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential"; - // 利用 appId 与 accessToken 建立关联,支持多账户 - static IAccessTokenCache accessTokenCache = ApiConfigKit.getAccessTokenCache(); + protected static boolean isAuthorized(String appId) { + return authorizedApi.isAuthorized(appId); + } /** * 从缓存中获取 access token,如果未取到或者 access token 不可用则先更新再获取 + * * @return AccessToken accessToken */ public static AccessToken getAccessToken() { - ApiConfig ac = ApiConfigKit.getApiConfig(); - AccessToken result = getAvailableAccessToken(ac); - if (result != null) { + String appId = ApiConfigKit.getApiConfig().getAppId(); + AccessToken result = accessTokenCache.get(appId); + if (result != null && result.isAvailable()) return result; - } - - return refreshAccessTokenIfNecessary(ac); - } - private static AccessToken getAvailableAccessToken(ApiConfig ac) { - String accessTokenJson = accessTokenCache.get(ac.getAppId()); - if (StrKit.notBlank(accessTokenJson)) { - AccessToken result = new AccessToken(accessTokenJson); - if (result != null && result.isAvailable()) { - return result; - } - } - return null; + refreshAccessToken(); + return accessTokenCache.get(appId); } /** * 直接获取 accessToken 字符串,方便使用 + * * @return String accessToken */ public static String getAccessTokenStr() { @@ -67,31 +70,24 @@ public class AccessTokenApi { } /** - * synchronized 配合再次获取 token 并检测可用性,防止多线程重复刷新 token 值 + * 强制更新 access token 值 */ - private static synchronized AccessToken refreshAccessTokenIfNecessary(ApiConfig ac) { - AccessToken result = getAvailableAccessToken(ac); - if (result != null) { - return result; + public static synchronized void refreshAccessToken() { + ApiConfig ac = ApiConfigKit.getApiConfig(); + String appId = ac.getAppId(); + String appSecret = ac.getAppSecret(); + if (isAuthorized(appId)) { + log.info("authorized appid:" + appId); + refreshAccessToken(appId); + } else { + log.info("unauthorized appid:" + appId); + refreshAccessToken(appId, appSecret); } - return refreshAccessToken(ac); - } - - /** - * 无条件强制更新 access token 值,不再对 cache 中的 token 进行判断 - */ - public static AccessToken refreshAccessToken() { - return refreshAccessToken(ApiConfigKit.getApiConfig()); } - /** - * 无条件强制更新 access token 值,不再对 cache 中的 token 进行判断 - */ - public static AccessToken refreshAccessToken(ApiConfig ac) { - String appId = ac.getAppId(); - String appSecret = ac.getAppSecret(); - final Map queryParas = ParaMap.create("appid", appId).put("secret", appSecret).getData(); - + private static void refreshAccessToken(String appId, String appSecret) { + final Map queryParas = ParaMap.create("appid", appId).put("secret", + appSecret).getData(); // 最多三次请求 AccessToken result = RetryUtils.retryOnException(3, new Callable() { @@ -103,10 +99,17 @@ public class AccessTokenApi { }); // 三次请求如果仍然返回了不可用的 access token 仍然 put 进去,便于上层通过 AccessToken 中的属性判断底层的情况 - if (null != result) { - accessTokenCache.set(ac.getAppId(), result.getJson()); + accessTokenCache.set(appId, result); + } + + public static synchronized void refreshAccessToken(String appId) { + final ComponentAuthorizerAccessToken token = ComponentAuthApi.getComponentAuthorizerAccessToken( + appId); + if (token != null && StrKit.notBlank(token.getAuthorizerAccessToken())) { + accessTokenCache.set(appId, + new AccessToken(token.getAuthorizerAccessToken(), + token.getExpiresIn())); } - return result; } } diff --git a/src/main/java/com/jfinal/weixin/sdk/api/ApiConfigKit.java b/src/main/java/com/jfinal/weixin/sdk/api/ApiConfigKit.java index ca3bf08..c9949f8 100644 --- a/src/main/java/com/jfinal/weixin/sdk/api/ApiConfigKit.java +++ b/src/main/java/com/jfinal/weixin/sdk/api/ApiConfigKit.java @@ -1,134 +1,69 @@ package com.jfinal.weixin.sdk.api; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import com.jfinal.kit.StrKit; -import com.jfinal.log.Log; import com.jfinal.weixin.sdk.cache.DefaultAccessTokenCache; import com.jfinal.weixin.sdk.cache.IAccessTokenCache; -import com.jfinal.weixin.sdk.session.DefaultWxSessionManager; -import com.jfinal.weixin.sdk.session.WxSessionManager; /** * 将 ApiConfig 绑定到 ThreadLocal 的工具类,以方便在当前线程的各个地方获取 ApiConfig 对象: * 1:如果控制器继承自 MsgController 该过程是自动的,详细可查看 MsgInterceptor 与之的配合 * 2:如果控制器继承自 ApiController 该过程是自动的,详细可查看 ApiInterceptor 与之的配合 * 3:如果控制器没有继承自 MsgController、ApiController,则需要先手动调用 - * ApiConfigKit.setThreadLocalAppId(appId) 来绑定 appId 到线程之上 + * ApiConfigKit.setThreadLocalApiConfig(ApiConfig) 来绑定 apiConfig 到线程之上 */ public class ApiConfigKit { - private static final Log log = Log.getLog(ApiConfigKit.class); - - private static final ThreadLocal TL = new ThreadLocal(); - - private static final Map CFG_MAP = new ConcurrentHashMap(); - private static final String DEFAULT_CFG_KEY = "_default_cfg_key_"; + private static final ThreadLocal API_CONFIG_THREAD_LOCAL = new ThreadLocal(); + private static final ThreadLocal COMPONENT_API_CONFIG_THREAD_LOCAL = new ThreadLocal(); + static IAccessTokenCache accessTokenCache = new DefaultAccessTokenCache(); // 开发模式将输出消息交互 xml 到控制台 - private static boolean devMode = false; - - public static void setDevMode(boolean devMode) { - ApiConfigKit.devMode = devMode; - } + private static boolean devMode = false; public static boolean isDevMode() { return devMode; } - /** - * 添加公众号配置,每个appId只需添加一次,相同appId将被覆盖。 - * 第一个添加的将作为默认公众号配置 - * @param apiConfig 公众号配置 - * @return ApiConfig 公众号配置 - */ - public static ApiConfig putApiConfig(ApiConfig apiConfig) { - if (CFG_MAP.size() == 0) { - CFG_MAP.put(DEFAULT_CFG_KEY, apiConfig); - } - return CFG_MAP.put(apiConfig.getAppId(), apiConfig); - } - - public static ApiConfig removeApiConfig(ApiConfig apiConfig) { - return removeApiConfig(apiConfig.getAppId()); + public static void setDevMode(boolean devMode) { + ApiConfigKit.devMode = devMode; } - public static ApiConfig removeApiConfig(String appId) { - return CFG_MAP.remove(appId); + public static void setThreadLocalApiConfig(ApiConfig apiConfig) { + API_CONFIG_THREAD_LOCAL.set(apiConfig); } - public static void setThreadLocalAppId(String appId) { - if (StrKit.isBlank(appId)) { - appId = CFG_MAP.get(DEFAULT_CFG_KEY).getAppId(); - } - TL.set(appId); + public static void setThreadLocalComponentApiConfig(ApiConfig apiConfig) { + COMPONENT_API_CONFIG_THREAD_LOCAL.set(apiConfig); } - public static void removeThreadLocalAppId() { - TL.remove(); + public static void removeThreadLocalApiConfig() { + API_CONFIG_THREAD_LOCAL.remove(); } - public static String getAppId() { - String appId = TL.get(); - if (StrKit.isBlank(appId)) { - appId = CFG_MAP.get(DEFAULT_CFG_KEY).getAppId(); - } - return appId; + public static void removeThreadLocalComponentApiConfig() { + COMPONENT_API_CONFIG_THREAD_LOCAL.remove(); } public static ApiConfig getApiConfig() { - String appId = getAppId(); - return getApiConfig(appId); + ApiConfig result = API_CONFIG_THREAD_LOCAL.get(); + if (result == null) + throw new IllegalStateException( + "需要事先使用 ApiConfigKit.setThreadLocalApiConfig(apiConfig) 将 ApiConfig对象存入,才可以调用 ApiConfigKit.getComponentApiConfig() 方法"); + return result; } - public static ApiConfig getApiConfig(String appId) { - log.debug("appId: " + appId); - ApiConfig cfg = CFG_MAP.get(appId); - if (cfg == null) - throw new IllegalStateException("需事先调用 ApiConfigKit.putApiConfig(apiConfig) 将 appId对应的 ApiConfig 对象存入," + - "如JFinalConfig.afterJFinalStart()中调用, 才可以使用 ApiConfigKit.getApiConfig() 系列方法"); - return cfg; - } - - static IAccessTokenCache accessTokenCache = new DefaultAccessTokenCache(); - - public static void setAccessTokenCache(IAccessTokenCache accessTokenCache) { - ApiConfigKit.accessTokenCache = accessTokenCache; + public static ApiConfig getComponentApiConfig() { + ApiConfig result = COMPONENT_API_CONFIG_THREAD_LOCAL.get(); + if (result == null) + throw new IllegalStateException( + "需要事先使用 ApiConfigKit.setThreadLocalApiConfig(apiConfig) 将 ApiConfig对象存入,才可以调用 ApiConfigKit.getComponentApiConfig() 方法"); + return result; } public static IAccessTokenCache getAccessTokenCache() { return ApiConfigKit.accessTokenCache; } - /** - * 是否启用session,默认不启用 - */ - private static WxSessionManager sessionManager = null; - - /** - * 启用默认的session管理器 - */ - public static void enableDefaultWxSessionManager() { - setWxSessionManager(new DefaultWxSessionManager()); - } - - /** - * 设置微信session管理器 - * @param sessionManager session管理器 - */ - public static void setWxSessionManager(WxSessionManager sessionManager) { - ApiConfigKit.sessionManager = sessionManager; - } - - /** - * 获取微信session管理器 - * @return sessionManager session管理器 - */ - public static WxSessionManager getWxSessionManager() { - WxSessionManager sessionManager = ApiConfigKit.sessionManager; - if (null == sessionManager) { - throw new NullPointerException("WxSessionManager is null, Please setWxSessionManager first!"); - } - return sessionManager; + public static void setAccessTokenCache(IAccessTokenCache accessTokenCache) { + ApiConfigKit.accessTokenCache = accessTokenCache; } + } diff --git a/src/main/java/com/jfinal/weixin/sdk/api/component/AuthorizedApi.java b/src/main/java/com/jfinal/weixin/sdk/api/component/AuthorizedApi.java new file mode 100644 index 0000000..9f5bf77 --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/api/component/AuthorizedApi.java @@ -0,0 +1,11 @@ +package com.jfinal.weixin.sdk.api.component; + +/** + *

+ * Created by Shylock on 25/09/2016. + */ +public interface AuthorizedApi { + boolean isAuthorized(String appId); + + ComponentAuthorizerAccessToken of(String appId); +} diff --git a/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentAccessToken.java b/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentAccessToken.java new file mode 100644 index 0000000..ffe4abc --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentAccessToken.java @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2011-2014, James Zhan 詹波 (jfinal@126.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +package com.jfinal.weixin.sdk.api.component; + +import com.jfinal.weixin.sdk.api.ReturnCode; +import com.jfinal.weixin.sdk.utils.JsonUtils; +import com.jfinal.weixin.sdk.utils.RetryUtils.ResultCheck; + +import java.io.Serializable; +import java.util.Map; + +/** + * 封装 access_token + */ +public class ComponentAccessToken implements ResultCheck, Serializable { + + private static final long serialVersionUID = -822464425433824314L; + + private String component_access_token; // 正确获取到 access_token 时有值 + private Integer expires_in; // 正确获取到 access_token 时有值 + private Integer errcode; // 出错时有值 + private String errmsg; // 出错时有值 + private Long expiredTime; // 正确获取到 access_token 时有值,存放过期时间 + private String json; + + @SuppressWarnings("unchecked") + public ComponentAccessToken(String jsonStr) { + this.json = jsonStr; + + try { + Map temp = JsonUtils.parse(jsonStr, Map.class); + component_access_token = (String) temp.get("component_access_token"); + expires_in = getInt(temp, "expires_in"); + errcode = getInt(temp, "errcode"); + errmsg = (String) temp.get("errmsg"); + + if (expires_in != null) + expiredTime = System.currentTimeMillis() + ((expires_in - 5) * 1000); + + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public String getJson() { + return json; + } + + public boolean isAvailable() { + if (expiredTime == null) + return false; + if (errcode != null) + return false; + if (expiredTime < System.currentTimeMillis()) + return false; + return component_access_token != null; + } + + private Integer getInt(Map temp, String key) { + Number number = (Number) temp.get(key); + return number == null ? null : number.intValue(); + } + + public String getComponetAccessToken() { + return component_access_token; + } + + public Integer getExpiresIn() { + return expires_in; + } + + public Long getExpiredTime() { + return expiredTime; + } + + public Integer getErrorCode() { + return errcode; + } + + public String getErrorMsg() { + if (errcode != null) { + String result = ReturnCode.get(errcode); + if (result != null) + return result; + } + return errmsg; + } + + @Override + public boolean matching() { + return isAvailable(); + } +} diff --git a/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentAccessTokenApi.java b/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentAccessTokenApi.java new file mode 100644 index 0000000..47161f5 --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentAccessTokenApi.java @@ -0,0 +1,98 @@ +/** + * Copyright (c) 2011-2014, James Zhan 詹波 (jfinal@126.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +package com.jfinal.weixin.sdk.api.component; + +import com.jfinal.weixin.sdk.api.ApiConfig; +import com.jfinal.weixin.sdk.api.ApiConfigKit; +import com.jfinal.weixin.sdk.cache.IAccessTokenCache; +import com.jfinal.weixin.sdk.kit.ParaMap; +import com.jfinal.weixin.sdk.utils.HttpUtils; +import com.jfinal.weixin.sdk.utils.JsonUtils; +import com.jfinal.weixin.sdk.utils.RetryUtils; + +import java.util.Map; +import java.util.concurrent.Callable; + +/** + * 认证并获取 access_token API + * http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96access_token + *

+ * AccessToken默认存储于内存中,可设置存储于redis或者实现IAccessTokenCache到数据库中实现分布式可用 + *

+ * 具体配置: + *

+ * ApiConfigKit.setAccessTokenCache(new RedisAccessTokenCache());
+ * 
+ */ +public class ComponentAccessTokenApi { + + + // 利用 appId 与 accessToken 建立关联,支持多账户 + static IAccessTokenCache tokenCache = ApiConfigKit.getAccessTokenCache(); + /** + * https://api.weixin.qq.com/cgi-bin/component/api_component_token + * { + * "component_appid":"appid_value" , + * "component_appsecret": "appsecret_value", + * "component_verify_ticket": "ticket_value" + * } + */ + private static String url = "https://api.weixin.qq.com/cgi-bin/component/api_component_token"; + + /** + * 从缓存中获取 access token,如果未取到或者 access token 不可用则先更新再获取 + * + * @return AccessToken accessToken + */ + public static ComponentAccessToken getComponentAccessToken() { + String appId = ApiConfigKit.getComponentApiConfig().getAppId(); + ComponentAccessToken result = tokenCache.getComponentAccessToken(appId); + if (result != null && result.isAvailable()) + return result; + + refreshComponentAccessToken(); + return tokenCache.getComponentAccessToken(appId); + } + + /** + * 直接获取 accessToken 字符串,方便使用 + * + * @return String accessToken + */ + public static String getComponentAccessTokenStr() { + return getComponentAccessToken().getComponetAccessToken(); + } + + /** + * 强制更新 access token 值 + */ + public static synchronized void refreshComponentAccessToken() { + ApiConfig ac = ApiConfigKit.getComponentApiConfig(); + String appId = ac.getAppId(); + String appSecret = ac.getAppSecret(); + String ticket = tokenCache.getComponentVerifyTicket(appId).getComponentVerifyTicket(); + final Map queryParas = ParaMap + .create("component_appid", appId) + .put("component_appsecret", appSecret) + .put("component_verify_ticket", ticket) + .getData(); + + // 最多三次请求 + ComponentAccessToken result = RetryUtils + .retryOnException(3, new Callable() { + @Override + public ComponentAccessToken call() throws Exception { + String json = HttpUtils.post(url, JsonUtils.toJson(queryParas)); + return new ComponentAccessToken(json); + } + }); + + // 三次请求如果仍然返回了不可用的 access token 仍然 put 进去,便于上层通过 AccessToken 中的属性判断底层的情况 + tokenCache.setComponentAccessToken(ac.getAppId(), result); + } + +} diff --git a/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentAuthApi.java b/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentAuthApi.java new file mode 100644 index 0000000..634de7c --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentAuthApi.java @@ -0,0 +1,175 @@ +/** + * Copyright (c) 2011-2014, James Zhan 詹波 (jfinal@126.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +package com.jfinal.weixin.sdk.api.component; + +import com.jfinal.weixin.sdk.api.ApiConfig; +import com.jfinal.weixin.sdk.api.ApiConfigKit; +import com.jfinal.weixin.sdk.api.ApiResult; +import com.jfinal.weixin.sdk.api.component.authorizer.AuthorizedInfo; +import com.jfinal.weixin.sdk.cache.IAccessTokenCache; +import com.jfinal.weixin.sdk.kit.ParaMap; +import com.jfinal.weixin.sdk.utils.HttpUtils; +import com.jfinal.weixin.sdk.utils.JsonUtils; +import com.jfinal.weixin.sdk.utils.RetryUtils; + +import java.text.MessageFormat; +import java.util.Map; +import java.util.ServiceLoader; +import java.util.concurrent.Callable; + +/** + * 认证并获取 access_token API + * http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96access_token + *

+ * AccessToken默认存储于内存中,可设置存储于redis或者实现IAccessTokenCache到数据库中实现分布式可用 + *

+ * 具体配置: + *

+ * ApiConfigKit.setAccessTokenCache(new RedisAccessTokenCache());
+ * 
+ */ +public class ComponentAuthApi { + + + // 利用 appId 与 accessToken 建立关联,支持多账户 + + /** + * https://api.weixin.qq.com/cgi-bin/component/api_component_token + * { + * "component_appid":"appid_value" , + * "component_appsecret": "appsecret_value", + * "component_verify_ticket": "ticket_value" + * } + */ + + public static final String redirect = "https://mp.weixin.qq.com/cgi-bin/componentloginpage?component_appid={0}&pre_auth_code={1}&redirect_uri={2}"; + static String auth = "https://api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token={0}"; + static String refresh = "https://api.weixin.qq.com/cgi-bin/component/api_authorizer_token?component_access_token={0}"; + static String info = "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_info?component_access_token={0}"; + static IAccessTokenCache tokenCache = ApiConfigKit.getAccessTokenCache(); + static ServiceLoader apiLoader = ServiceLoader.load( + AuthorizedApi.class); + static AuthorizedApi authorizedApi = apiLoader.iterator().next(); + + /** + * 生成Authorize链接 + * + * @param appId 应用id + * @param redirect_uri 回跳地址 + * @return url + */ + public static String getAuthorizeURL(String appId, String redirect_uri) { + String pre_auth_code = ComponentPreAuthCodeApi.getComponentPreAuthCodeStr(); + String redirectURL = MessageFormat.format(redirect, appId, pre_auth_code, redirect_uri); + return redirectURL; + } + + + public static synchronized ApiResult auth(final String authorization_code) { + ApiConfig ac = ApiConfigKit.getComponentApiConfig(); + String appId = ac.getAppId(); + final Map queryParas = ParaMap.create("component_appid", appId) + .put("authorization_code", authorization_code) + .getData(); + + final String paramUrl = MessageFormat.format(auth, + ComponentAccessTokenApi.getComponentAccessTokenStr()); + String json = HttpUtils.post(paramUrl, JsonUtils.toJson(queryParas)); + final ApiResult apiResult = new ApiResult(json); + if (apiResult != null && apiResult.isSucceed()) { + final String authorization_info = JsonUtils.toJson(apiResult.get( + "authorization_info")); + ComponentAuthorizerAccessToken token = new ComponentAuthorizerAccessToken( + authorization_info); + if (token.isAvailable()) { + tokenCache.setComponentAuthorizerAccessToken(appId, + token.getAuthorizerAppId(), + token); + } + } + return apiResult; + } + + /** + * 从缓存中获取 access token,如果未取到或者 access token 不可用则先更新再获取 + * + * @return AccessToken accessToken + */ + public static ComponentAuthorizerAccessToken getComponentAuthorizerAccessToken(String authorizer_appid) { + String appId = ApiConfigKit.getComponentApiConfig().getAppId(); + ComponentAuthorizerAccessToken result = tokenCache.getComponentAuthorizerAccessToken(appId, + authorizer_appid); + if (result != null && result.isAvailable()) + return result; + refreshAccessToken(authorizer_appid); + return tokenCache.getComponentAuthorizerAccessToken(appId, authorizer_appid); + } + + + public static AuthorizedInfo getAuthorizer(String authorizer_appid) { + ApiConfig ac = ApiConfigKit.getComponentApiConfig(); + String appId = ac.getAppId(); + final Map queryParas = ParaMap.create("component_appid", appId) + .put("authorizer_appid", authorizer_appid) + .getData(); + + final String paramUrl = MessageFormat.format(info, + ComponentAccessTokenApi.getComponentAccessTokenStr()); + String json = HttpUtils.post(paramUrl, JsonUtils.toJson(queryParas)); + return new AuthorizedInfo(json); + } + + /** + * 直接获取 accessToken 字符串,方便使用 + * + * @return String accessToken + */ + public static String getComponentAuthorizerAccessTokenStr(String authorizer_appid) { + return getComponentAuthorizerAccessToken(authorizer_appid).getAuthorizerAccessToken(); + } + + /** + * 强制更新 access token 值 + */ + public static synchronized void refreshAccessToken(String authorizer_appid) { + ApiConfig ac = ApiConfigKit.getComponentApiConfig(); + String appId = ac.getAppId(); + String appSecret = ac.getAppSecret(); + ComponentAuthorizerAccessToken accessToken = tokenCache.getComponentAuthorizerAccessToken( + appId, + authorizer_appid); + if (accessToken == null || !accessToken.isAvailable()) { + accessToken = authorizedApi.of(authorizer_appid); + } + final Map queryParas = ParaMap.create("component_appid", appId) + .put("authorizer_appid", + accessToken.getAuthorizerAppId()) + .put("authorizer_refresh_token", + accessToken.getAuthorizerRefreshToken()).getData(); + // 最多三次请求 + ComponentAuthorizerAccessToken result = RetryUtils.retryOnException(3, + new Callable() { + + @Override + public ComponentAuthorizerAccessToken call() throws Exception { + final String paramUrl = MessageFormat.format( + refresh, + ComponentAccessTokenApi.getComponentAccessTokenStr()); + String json = HttpUtils.post( + paramUrl, + JsonUtils.toJson( + queryParas)); + return new ComponentAuthorizerAccessToken( + json); + } + }); + + // 三次请求如果仍然返回了不可用的 access token 仍然 put 进去,便于上层通过 AccessToken 中的属性判断底层的情况 + tokenCache.setComponentAuthorizerAccessToken(appId, authorizer_appid, result); + } + +} diff --git a/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentAuthorizerAccessToken.java b/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentAuthorizerAccessToken.java new file mode 100644 index 0000000..5feeba8 --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentAuthorizerAccessToken.java @@ -0,0 +1,123 @@ +/** + * Copyright (c) 2011-2014, James Zhan 詹波 (jfinal@126.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +package com.jfinal.weixin.sdk.api.component; + +import com.jfinal.weixin.sdk.api.ReturnCode; +import com.jfinal.weixin.sdk.utils.JsonUtils; +import com.jfinal.weixin.sdk.utils.RetryUtils.ResultCheck; + +import java.io.Serializable; +import java.util.Map; + +/** + * 封装 access_token + */ +public class ComponentAuthorizerAccessToken implements ResultCheck, Serializable { + + private static final long serialVersionUID = -822464425433824314L; + /** + * "authorizer_appid": "wxf8b4f85f3a794e77", + * "authorizer_access_token": "QXjUqNqfYVH0yBE1iI_7vuN_9gQbpjfK7hYwJ3P7xOa88a89-Aga5x1NMYJyB8G2yKt1KCl0nPC3W9GJzw0Zzq_dBxc8pxIGUNi_bFes0qM", + * "expires_in": 7200, + * "authorizer_refresh_token": "dTo-YCXPL4llX-u1W1pPpnp8Hgm4wpJtlR6iV0doKdY", + */ + private String authorizer_appid; // 正确获取到 access_token 时有值 + private String authorizer_access_token; // 正确获取到 access_token 时有值 + private String authorizer_refresh_token; // 正确获取到 access_token 时有值 + private Integer expires_in; // 正确获取到 access_token 时有值 + private Integer errcode; // 出错时有值 + private String errmsg; // 出错时有值 + private Long expiredTime; // 正确获取到 access_token 时有值,存放过期时间 + private String json; + + @SuppressWarnings("unchecked") + public ComponentAuthorizerAccessToken(String jsonStr) { + this.json = jsonStr; + + try { + Map temp = JsonUtils.parse(jsonStr, Map.class); + authorizer_appid = (String) temp.get("authorizer_appid"); + authorizer_refresh_token = (String) temp.get("authorizer_refresh_token"); + authorizer_access_token = (String) temp.get("authorizer_access_token"); + expires_in = getInt(temp, "expires_in"); + errcode = getInt(temp, "errcode"); + errmsg = (String) temp.get("errmsg"); + + if (expires_in != null) + expiredTime = System.currentTimeMillis() + ((expires_in - 5) * 1000); + + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public ComponentAuthorizerAccessToken(String appId, + String authorizerAccessToken, + String authorizerRefreshToken) { + this.authorizer_appid = appId; + this.authorizer_access_token = authorizerAccessToken; + this.authorizer_refresh_token = authorizerRefreshToken; + } + + public String getJson() { + return json; + } + + public boolean isAvailable() { + if (expiredTime == null) + return false; + if (errcode != null) + return false; + if (expiredTime < System.currentTimeMillis()) + return false; + return authorizer_access_token != null; + } + + private Integer getInt(Map temp, String key) { + Number number = (Number) temp.get(key); + return number == null ? null : number.intValue(); + } + + public String getAuthorizerAppId() { + return authorizer_appid; + } + + public String getAuthorizerAccessToken() { + return authorizer_access_token; + } + + public String getAuthorizerRefreshToken() { + return authorizer_refresh_token; + } + + public Integer getExpiresIn() { + return expires_in; + } + + public Long getExpiredTime() { + return expiredTime; + } + + public Integer getErrorCode() { + return errcode; + } + + public String getErrorMsg() { + if (errcode != null) { + String result = ReturnCode.get(errcode); + if (result != null) + return result; + } + return errmsg; + } + + @Override + public boolean matching() { + return isAvailable(); + } + +} diff --git a/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentAuthorizerToken.java b/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentAuthorizerToken.java new file mode 100644 index 0000000..30fb1a6 --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentAuthorizerToken.java @@ -0,0 +1,98 @@ +/** + * Copyright (c) 2011-2014, James Zhan 詹波 (jfinal@126.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +package com.jfinal.weixin.sdk.api.component; + +import com.jfinal.weixin.sdk.api.ReturnCode; +import com.jfinal.weixin.sdk.utils.JsonUtils; +import com.jfinal.weixin.sdk.utils.RetryUtils.ResultCheck; + +import java.io.Serializable; +import java.util.Map; + +/** + * 封装 access_token + */ +public class ComponentAuthorizerToken implements ResultCheck, Serializable { + + private static final long serialVersionUID = -822464425433824314L; + + private String component_access_token; // 正确获取到 access_token 时有值 + private Integer expires_in; // 正确获取到 access_token 时有值 + private Integer errcode; // 出错时有值 + private String errmsg; // 出错时有值 + private Long expiredTime; // 正确获取到 access_token 时有值,存放过期时间 + private String json; + + @SuppressWarnings("unchecked") + public ComponentAuthorizerToken(String jsonStr) { + this.json = jsonStr; + + try { + Map temp = JsonUtils.parse(jsonStr, Map.class); + component_access_token = (String) temp.get("component_access_token"); + expires_in = getInt(temp, "expires_in"); + errcode = getInt(temp, "errcode"); + errmsg = (String) temp.get("errmsg"); + + if (expires_in != null) + expiredTime = System.currentTimeMillis() + ((expires_in - 5) * 1000); + + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + + public String getJson() { + return json; + } + + public boolean isAvailable() { + if (expiredTime == null) + return false; + if (errcode != null) + return false; + if (expiredTime < System.currentTimeMillis()) + return false; + return component_access_token != null; + } + + private Integer getInt(Map temp, String key) { + Number number = (Number) temp.get(key); + return number == null ? null : number.intValue(); + } + + public String getComponetAccessToken() { + return component_access_token; + } + + public Integer getExpiresIn() { + return expires_in; + } + + public Long getExpiredTime() { + return expiredTime; + } + + public Integer getErrorCode() { + return errcode; + } + + public String getErrorMsg() { + if (errcode != null) { + String result = ReturnCode.get(errcode); + if (result != null) + return result; + } + return errmsg; + } + + @Override + public boolean matching() { + return isAvailable(); + } +} diff --git a/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentAuthorizerTokenApi.java b/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentAuthorizerTokenApi.java new file mode 100644 index 0000000..a85ac29 --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentAuthorizerTokenApi.java @@ -0,0 +1,100 @@ +/** + * Copyright (c) 2011-2014, James Zhan 詹波 (jfinal@126.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +package com.jfinal.weixin.sdk.api.component; + +import com.jfinal.weixin.sdk.api.ApiConfig; +import com.jfinal.weixin.sdk.api.ApiConfigKit; +import com.jfinal.weixin.sdk.cache.IAccessTokenCache; +import com.jfinal.weixin.sdk.kit.ParaMap; +import com.jfinal.weixin.sdk.utils.HttpUtils; +import com.jfinal.weixin.sdk.utils.RetryUtils; + +import java.util.Map; +import java.util.concurrent.Callable; + +/** + * 认证并获取 access_token API + * http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96access_token + *

+ * AccessToken默认存储于内存中,可设置存储于redis或者实现IAccessTokenCache到数据库中实现分布式可用 + *

+ * 具体配置: + *

+ * ApiConfigKit.setAccessTokenCache(new RedisAccessTokenCache());
+ * 
+ */ +public class ComponentAuthorizerTokenApi { + + + /** + * https://api.weixin.qq.com/cgi-bin/component/api_component_token + * { + * "component_appid":"appid_value" , + * "component_appsecret": "appsecret_value", + * "component_verify_ticket": "ticket_value" + * } + */ + private static String url = "https://api.weixin.qq.com/cgi-bin/component/api_component_token"; + + // 利用 appId 与 accessToken 建立关联,支持多账户 + static IAccessTokenCache componentAccessTokenCache = ApiConfigKit.getAccessTokenCache(); + + /** + * 从缓存中获取 access token,如果未取到或者 access token 不可用则先更新再获取 + * + * @return AccessToken accessToken + */ + public static ComponentAccessToken getComponentAccessToken() { + String appId = ApiConfigKit.getComponentApiConfig().getAppId(); + ComponentAccessToken result = componentAccessTokenCache.get(appId); + if (result != null && result.isAvailable()) + return result; + + refreshAccessToken(); + return componentAccessTokenCache.get(appId); + } + + /** + * 直接获取 accessToken 字符串,方便使用 + * + * @return String accessToken + */ + public static String getComponetAccessTokenStr() { + return getComponentAccessToken().getComponetAccessToken(); + } + + /** + * 强制更新 access token 值 + */ + public static synchronized void refreshAccessToken() { + ApiConfig ac = ApiConfigKit.getComponentApiConfig(); + String appId = ac.getAppId(); + String appSecret = ac.getAppSecret(); + String ticket = componentAccessTokenCache.get(appId); + final Map queryParas = ParaMap.create("component_appid", appId).put( + "component_appsecret", + appSecret).put("component_verify_ticket", ticket).getData(); + + // 最多三次请求 + ComponentAccessToken result = RetryUtils.retryOnException(3, + new Callable() { + + @Override + public ComponentAccessToken call() throws Exception { + String json = HttpUtils.get( + url, + queryParas); + return new ComponentAccessToken( + json); + } + }); + + // 三次请求如果仍然返回了不可用的 access token 仍然 put 进去,便于上层通过 AccessToken 中的属性判断底层的情况 + componentAccessTokenCache.set(ac.getAppId(), result); + } + +} diff --git a/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentPreAuthCode.java b/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentPreAuthCode.java new file mode 100644 index 0000000..2bedd5b --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentPreAuthCode.java @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2011-2014, James Zhan 詹波 (jfinal@126.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +package com.jfinal.weixin.sdk.api.component; + +import com.jfinal.weixin.sdk.api.ReturnCode; +import com.jfinal.weixin.sdk.utils.JsonUtils; +import com.jfinal.weixin.sdk.utils.RetryUtils.ResultCheck; + +import java.io.Serializable; +import java.util.Map; + +/** + * 封装 access_token + */ +public class ComponentPreAuthCode implements ResultCheck, Serializable { + + private static final long serialVersionUID = -822464425433824314L; + + private String pre_auth_code; // 正确获取到 access_token 时有值 + private Integer expires_in; // 正确获取到 access_token 时有值 + private Integer errcode; // 出错时有值 + private String errmsg; // 出错时有值 + private Long expiredTime; // 正确获取到 access_token 时有值,存放过期时间 + private String json; + + @SuppressWarnings("unchecked") + public ComponentPreAuthCode(String jsonStr) { + this.json = jsonStr; + + try { + Map temp = JsonUtils.parse(jsonStr, Map.class); + pre_auth_code = (String) temp.get("pre_auth_code"); + expires_in = getInt(temp, "expires_in"); + errcode = getInt(temp, "errcode"); + errmsg = (String) temp.get("errmsg"); + + if (expires_in != null) + expiredTime = System.currentTimeMillis() + ((expires_in - 5) * 1000); + + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public String getJson() { + return json; + } + + public boolean isAvailable() { + if (expiredTime == null) + return false; + if (errcode != null) + return false; + if (expiredTime < System.currentTimeMillis()) + return false; + return pre_auth_code != null; + } + + private Integer getInt(Map temp, String key) { + Number number = (Number) temp.get(key); + return number == null ? null : number.intValue(); + } + + public String getPreAuthCode() { + return pre_auth_code; + } + + public Integer getExpiresIn() { + return expires_in; + } + + public Long getExpiredTime() { + return expiredTime; + } + + public Integer getErrorCode() { + return errcode; + } + + public String getErrorMsg() { + if (errcode != null) { + String result = ReturnCode.get(errcode); + if (result != null) + return result; + } + return errmsg; + } + + @Override + public boolean matching() { + return isAvailable(); + } +} diff --git a/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentPreAuthCodeApi.java b/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentPreAuthCodeApi.java new file mode 100644 index 0000000..dc0de06 --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentPreAuthCodeApi.java @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2011-2014, James Zhan 詹波 (jfinal@126.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +package com.jfinal.weixin.sdk.api.component; + +import com.jfinal.weixin.sdk.api.ApiConfig; +import com.jfinal.weixin.sdk.api.ApiConfigKit; +import com.jfinal.weixin.sdk.cache.IAccessTokenCache; +import com.jfinal.weixin.sdk.kit.ParaMap; +import com.jfinal.weixin.sdk.utils.HttpUtils; +import com.jfinal.weixin.sdk.utils.JsonUtils; +import com.jfinal.weixin.sdk.utils.RetryUtils; + +import java.text.MessageFormat; +import java.util.Map; +import java.util.concurrent.Callable; + +/** + * 认证并获取 access_token API + * http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96access_token + *

+ * AccessToken默认存储于内存中,可设置存储于redis或者实现IAccessTokenCache到数据库中实现分布式可用 + *

+ * 具体配置: + *

+ * ApiConfigKit.setAccessTokenCache(new RedisAccessTokenCache());
+ * 
+ */ +public class ComponentPreAuthCodeApi { + + + // 利用 appId 与 accessToken 建立关联,支持多账户 + static IAccessTokenCache tokenCache = ApiConfigKit.getAccessTokenCache(); + /** + * https://api.weixin.qq.com/cgi-bin/component/api_component_token + * { + * "component_appid":"appid_value" , + * "component_appsecret": "appsecret_value", + * "component_verify_ticket": "ticket_value" + * } + */ + private static String url = "https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode?component_access_token={0}"; + + /** + * 从缓存中获取 access token,如果未取到或者 access token 不可用则先更新再获取 + * + * @return AccessToken accessToken + */ + public static ComponentPreAuthCode getComponentPreAuthCode() { +// String appId = ApiConfigKit.getComponentApiConfig().getAppId(); +// ComponentPreAuthCode result = tokenCache.getComponentPreAuthCode(appId); +// if (result != null && result.isAvailable()) +// return result; +// +// getPreAuthCode(); +// return tokenCache.getComponentPreAuthCode(appId); + return getPreAuthCode(); + } + + /** + * 直接获取 accessToken 字符串,方便使用 + * + * @return String accessToken + */ + public static String getComponentPreAuthCodeStr() { + final ComponentPreAuthCode preAuthCode = getPreAuthCode(); + if (preAuthCode != null && preAuthCode.isAvailable()) { + return preAuthCode.getPreAuthCode(); + } + { + return ""; + } + } + + /** + * 强制更新 access token 值 + */ + private static ComponentPreAuthCode getPreAuthCode() { + ApiConfig ac = ApiConfigKit.getComponentApiConfig(); + String appId = ac.getAppId(); + final Map queryParas = ParaMap.create("component_appid", appId).getData(); + + // 最多三次请求 + ComponentPreAuthCode result = RetryUtils.retryOnException(3, + new Callable() { + @Override + public ComponentPreAuthCode call() throws Exception { + final String paramUrl = MessageFormat.format( + url, + ComponentAccessTokenApi.getComponentAccessTokenStr()); + String json = HttpUtils.post( + paramUrl, + JsonUtils.toJson( + queryParas)); + return new ComponentPreAuthCode( + json); + } + }); + + // 三次请求如果仍然返回了不可用的 access token 仍然 put 进去,便于上层通过 AccessToken 中的属性判断底层的情况 + return result; + } + +} diff --git a/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentVerifyTicket.java b/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentVerifyTicket.java new file mode 100644 index 0000000..82d295a --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentVerifyTicket.java @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2011-2014, James Zhan 詹波 (jfinal@126.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +package com.jfinal.weixin.sdk.api.component; + +import java.io.Serializable; + +/** + *

+ * 接收文本消息
+ * 
+ *  
+ * 1413192605 
+ *  
+ *  
+ * 
+ * 
+ */ +public class ComponentVerifyTicket implements Serializable { + private static final long serialVersionUID = 6458326639149049855L; + /** + * AppId 第三方平台appid + * CreateTime 时间戳 + * InfoType component_verify_ticket + * ComponentVerifyTicket Ticket内容 + */ + // 开发者微信号 + private String appId; + // 消息创建时间 (整型) + private Integer createTime; + private String componentVerifyTicket; + + public ComponentVerifyTicket(String appId, + Integer createTime, + String componentVerifyTicket) { + this.appId = appId; + this.createTime = createTime; + this.componentVerifyTicket = componentVerifyTicket; + } + + public String getAppId() { + return appId; + } + + public Integer getCreateTime() { + return createTime; + } + + public String getComponentVerifyTicket() { + return componentVerifyTicket; + } +} + + + + diff --git a/src/main/java/com/jfinal/weixin/sdk/api/component/DefaultAuthorizedApi.java b/src/main/java/com/jfinal/weixin/sdk/api/component/DefaultAuthorizedApi.java new file mode 100644 index 0000000..d01684a --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/api/component/DefaultAuthorizedApi.java @@ -0,0 +1,16 @@ +package com.jfinal.weixin.sdk.api.component; + +/** + *

+ * Created by Shylock on 25/09/2016. + */ +public class DefaultAuthorizedApi implements AuthorizedApi { + public boolean isAuthorized(String appId) { + return false; + } + + @Override + public ComponentAuthorizerAccessToken of(String appId) { + return null; + } +} diff --git a/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/AuthorizationInfo.java b/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/AuthorizationInfo.java new file mode 100644 index 0000000..51cbb54 --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/AuthorizationInfo.java @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2011-2014, James Zhan 詹波 (jfinal@126.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +package com.jfinal.weixin.sdk.api.component.authorizer; + +import com.jfinal.kit.StrKit; +import com.jfinal.weixin.sdk.api.component.authorizer.type.FunctionType; +import com.jfinal.weixin.sdk.api.component.authorizer.type.api.result.FunctionInfo; +import com.jfinal.weixin.sdk.utils.JsonUtils; + +import java.io.Serializable; +import java.util.List; + +/** + * 封装 access_token + */ +public class AuthorizationInfo implements Serializable { + + private static final long serialVersionUID = -1974768736356874602L; + + private String appid; + private List authorizedFunctions; + private String authorizer_appid; // 正确获取到 access_token 时有值 + private String authorizer_access_token; // 正确获取到 access_token 时有值 + private String authorizer_refresh_token; // 正确获取到 access_token 时有值 + private String json; + + + public AuthorizationInfo(String jsonStr) { + this.json = jsonStr; + + try { + FunctionInfo temp = JsonUtils.parse(jsonStr, FunctionInfo.class); + this.authorizedFunctions = temp.to(); + final String appid = temp.getAppid(); + if (StrKit.isBlank(appid)) { + this.appid = temp.getAuthorizer_appid(); + this.authorizer_appid = temp.getAuthorizer_appid(); + } else { + this.authorizer_appid = appid; + this.appid = appid; + } + this.authorizer_access_token = temp.getAuthorizer_access_token(); + this.authorizer_refresh_token = temp.getAuthorizer_refresh_token(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public AuthorizationInfo(String appid, List functionTypes) { + this.appid = appid; + this.authorizedFunctions = functionTypes; + } + + public String getAppid() { + return appid; + } + + public void setAppid(String appid) { + this.appid = appid; + } + + public List getAuthorizedFunctions() { + return authorizedFunctions; + } + + public void setAuthorizedFunctions(List authorizedFunctions) { + this.authorizedFunctions = authorizedFunctions; + } + + public String getAuthorizer_appid() { + return authorizer_appid; + } + + public void setAuthorizer_appid(String authorizer_appid) { + this.authorizer_appid = authorizer_appid; + } + + public String getAuthorizer_access_token() { + return authorizer_access_token; + } + + public void setAuthorizer_access_token(String authorizer_access_token) { + this.authorizer_access_token = authorizer_access_token; + } + + public String getAuthorizer_refresh_token() { + return authorizer_refresh_token; + } + + public void setAuthorizer_refresh_token(String authorizer_refresh_token) { + this.authorizer_refresh_token = authorizer_refresh_token; + } + + public String getJson() { + return json; + } + + public void setJson(String json) { + this.json = json; + } +} diff --git a/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/AuthorizedInfo.java b/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/AuthorizedInfo.java new file mode 100644 index 0000000..d22860d --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/AuthorizedInfo.java @@ -0,0 +1,54 @@ +package com.jfinal.weixin.sdk.api.component.authorizer; + +import com.jfinal.weixin.sdk.utils.JsonUtils; + +import java.io.Serializable; +import java.util.Map; + +/** + *

+ * Created by Shylock on 16/9/25. + */ +public class AuthorizedInfo implements Serializable { + + private static final long serialVersionUID = -1633011062366786962L; + + + private AuthorizerInfo authorizerInfo; + + private AuthorizationInfo authorizationInfo; + private String json; + + public AuthorizedInfo() { + } + + public AuthorizedInfo(String jsonStr) { + this.json = jsonStr; + try { + Map temp = JsonUtils.parse(jsonStr, Map.class); + final Object authorizer_info = temp.get("authorizer_info"); + if (authorizer_info != null) { + authorizerInfo = new AuthorizerInfo(JsonUtils.toJson(authorizer_info)); + } + final Object authorization_info = temp.get("authorization_info"); + if (authorization_info != null) { + authorizationInfo = new AuthorizationInfo(JsonUtils.toJson(authorization_info)); + } + + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public String getJson() { + return json; + } + + public AuthorizerInfo getAuthorizerInfo() { + return authorizerInfo; + } + + public AuthorizationInfo getAuthorizationInfo() { + return authorizationInfo; + } +} diff --git a/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/AuthorizerInfo.java b/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/AuthorizerInfo.java new file mode 100644 index 0000000..87e51b4 --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/AuthorizerInfo.java @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2011-2014, James Zhan 詹波 (jfinal@126.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +package com.jfinal.weixin.sdk.api.component.authorizer; + +import com.jfinal.weixin.sdk.api.component.authorizer.type.BusinessType; +import com.jfinal.weixin.sdk.api.component.authorizer.type.ServiceType; +import com.jfinal.weixin.sdk.api.component.authorizer.type.VerifyType; +import com.jfinal.weixin.sdk.api.component.authorizer.type.api.result.AuthInfo; +import com.jfinal.weixin.sdk.utils.JsonUtils; + +import java.io.Serializable; +import java.util.List; + +/** + * 封装 access_token + */ +public class AuthorizerInfo implements Serializable { + + + private static final long serialVersionUID = -877395267037045760L; + private String nickName; + private String headImg; + private ServiceType serviceType; + private VerifyType verifyType; + private String userName; + /** + * 用以了解以下功能的开通状况(0代表未开通,1代表已开通): + * open_store:是否开通微信门店功能 + * open_scan:是否开通微信扫商品功能 + * open_pay:是否开通微信支付功能 + * open_card:是否开通微信卡券功能 + * open_shake:是否开通微信摇一摇功能 + */ + private List openedBusiness; + private String alias; + private String qrCodeUrl; + + private String json; + + public AuthorizerInfo(String jsonStr) { + this.json = jsonStr; + try { + AuthInfo temp = JsonUtils.parse(jsonStr, AuthInfo.class); + this.alias = temp.getAlias(); + this.nickName = temp.getNick_name(); + this.headImg = temp.getHead_img(); + this.serviceType = temp.toServiceType(); + this.verifyType = temp.toVerifyType(); + this.userName = temp.getUser_name(); + this.openedBusiness = temp.getBusiness_info().to(); + this.qrCodeUrl = temp.getQrcode_url(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public String getNickName() { + return nickName; + } + + public String getHeadImg() { + return headImg; + } + + public ServiceType getServiceType() { + return serviceType; + } + + public VerifyType getVerifyType() { + return verifyType; + } + + public String getUserName() { + return userName; + } + + public List getOpenedBusiness() { + return openedBusiness; + } + + public String getAlias() { + return alias; + } + + public String getQrCodeUrl() { + return qrCodeUrl; + } + + +} diff --git a/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/BusinessType.java b/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/BusinessType.java new file mode 100644 index 0000000..dc8266e --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/BusinessType.java @@ -0,0 +1,39 @@ +package com.jfinal.weixin.sdk.api.component.authorizer.type; + +/** + * open_store:是否开通微信门店功能 + * open_scan:是否开通微信扫商品功能 + * open_pay:是否开通微信支付功能 + * open_card:是否开通微信卡券功能 + * open_shake:是否开通微信摇一摇功能 + *

+ * Created by Shylock on 16/9/25. + */ +public enum BusinessType { + Store(0, "微信门店"), Scan(1, "微信扫商品"), Pay(2, "微信支付"), Card(3, "微信卡券"), Shake(4, "微信摇一摇"); + + private int code; + private String memo; + + BusinessType(int code, String memo) { + this.code = code; + this.memo = memo; + } + + public int getCode() { + return code; + } + + public String getMemo() { + return memo; + } + + public static BusinessType of(int code) { + for (BusinessType type : values()) { + if (type.code == code) { + return type; + } + } + return null; + } +} diff --git a/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/FunctionType.java b/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/FunctionType.java new file mode 100644 index 0000000..e851a32 --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/FunctionType.java @@ -0,0 +1,69 @@ +package com.jfinal.weixin.sdk.api.component.authorizer.type; + +/** + *

+ * Created by Shylock on 16/9/25. + */ + +/** + * 公众号授权给开发者的权限集列表,ID为1到15时分别代表: + * 消息管理权限 + * 用户管理权限 + * 帐号服务权限 + * 网页服务权限 + * 微信小店权限 + * 微信多客服权限 + * 群发与通知权限 + * 微信卡券权限 + * 微信扫一扫权限 + * 微信连WIFI权限 + * 素材管理权限 + * 微信摇周边权限 + * 微信门店权限 + * 微信支付权限 + * 自定义菜单权限 + *

+ * 请注意: + * 1)该字段的返回不会考虑公众号是否具备该权限集的权限(因为可能部分具备),请根据公众号的帐号类型和认证情况,来判断公众号的接口权限。 + */ +public enum FunctionType { + Message(1, "消息管理权限"), + User(2, "用户管理权限"), + AccountService(3, "帐号服务权限"), + WebService(4, "网页服务权限"), + WechatMall(5, "微信小店权限"), + CustomService(6, "微信多客服权限"), + TemplateMessage(7, "群发与通知权限"), + WechatCard(8, "微信卡券权限"), + WechatScan(9, "微信扫一扫权限"), + WechatWiFi(10, "微信连WIFI权限"), + Media(11, "素材管理权限"), + WechatShake(12, "微信摇周边权限"), + WechatStore(13, "微信门店权限"), + WechatPay(14, "微信支付权限"), + WechatMenu(15, "自定义菜单权限"); + private int code; + private String memo; + + FunctionType(int code, String memo) { + this.code = code; + this.memo = memo; + } + + public int getCode() { + return code; + } + + public String getMemo() { + return memo; + } + + public static FunctionType of(int code) { + for (FunctionType type : values()) { + if (type.code == code) { + return type; + } + } + return null; + } +} diff --git a/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/ServiceType.java b/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/ServiceType.java new file mode 100644 index 0000000..2fecac9 --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/ServiceType.java @@ -0,0 +1,36 @@ +package com.jfinal.weixin.sdk.api.component.authorizer.type; + +/** + * 授权方公众号类型,0代表订阅号,1代表由历史老帐号升级后的订阅号,2代表服务号 + *

+ * Created by Shylock on 16/9/25. + */ + +public enum ServiceType { + Subscription(0, "订阅号"), OldToSubscription(1, "订阅号"), Service(2, "服务号"); + + private int code; + private String memo; + + ServiceType(int code, String memo) { + this.code = code; + this.memo = memo; + } + + public int getCode() { + return code; + } + + public String getMemo() { + return memo; + } + + public static ServiceType of(int code) { + for (ServiceType type : values()) { + if (type.code == code) { + return type; + } + } + return null; + } +} diff --git a/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/VerifyType.java b/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/VerifyType.java new file mode 100644 index 0000000..dd5e068 --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/VerifyType.java @@ -0,0 +1,47 @@ +package com.jfinal.weixin.sdk.api.component.authorizer.type; + +/** + * 授权方认证类型,-1代表未认证, + * 0代表微信认证, + * 1代表新浪微博认证, + * 2代表腾讯微博认证, + * 3代表已资质认证通过但还未通过名称认证, + * 4代表已资质认证通过、还未通过名称认证,但通过了新浪微博认证, + * 5代表已资质认证通过、还未通过名称认证,但通过了腾讯微博认证 + *

+ * Created by Shylock on 16/9/25. + */ +public enum VerifyType { + NONE(-1, "未认证"), + WECHAT(0, "微信"), + SINA_WEIBO(1, "新浪微博"), + TENCENT_WEIBO(2, "腾讯微博"), + QC_NO_NAME(3, "资质认证通过但还未通过名称认证"), + QC_SIAN_WEIBO_NO_NAME(4, "资质认证通过、还未通过名称认证,但通过了新浪微博认证"), + QC_TENCENT_WEIBO_NO_NAME(5, "资质认证通过、还未通过名称认证,但通过了腾讯微博认证"); + + private int code; + private String memo; + + VerifyType(int code, String memo) { + this.code = code; + this.memo = memo; + } + + public int getCode() { + return code; + } + + public String getMemo() { + return memo; + } + + public static VerifyType of(int code) { + for (VerifyType type : values()) { + if (type.code == code) { + return type; + } + } + return null; + } +} diff --git a/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/api/result/AuthInfo.java b/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/api/result/AuthInfo.java new file mode 100644 index 0000000..82754bd --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/api/result/AuthInfo.java @@ -0,0 +1,88 @@ +package com.jfinal.weixin.sdk.api.component.authorizer.type.api.result; + +import com.jfinal.weixin.sdk.api.component.authorizer.type.ServiceType; +import com.jfinal.weixin.sdk.api.component.authorizer.type.VerifyType; + +public class AuthInfo { + + private String nick_name; + private String head_img; + private TypeInfo service_type_info; + private TypeInfo verify_type_info; + private String user_name; + private String alias; + private BusinessInfo business_info; + private String qrcode_url; + + public ServiceType toServiceType() { + return ServiceType.of(service_type_info.getId()); + } + + public VerifyType toVerifyType() { + return VerifyType.of(verify_type_info.getId()); + } + + public String getNick_name() { + return nick_name; + } + + public void setNick_name(String nick_name) { + this.nick_name = nick_name; + } + + public String getHead_img() { + return head_img; + } + + public void setHead_img(String head_img) { + this.head_img = head_img; + } + + public TypeInfo getService_type_info() { + return service_type_info; + } + + public void setService_type_info(TypeInfo service_type_info) { + this.service_type_info = service_type_info; + } + + public TypeInfo getVerify_type_info() { + return verify_type_info; + } + + public void setVerify_type_info(TypeInfo verify_type_info) { + this.verify_type_info = verify_type_info; + } + + public String getUser_name() { + return user_name; + } + + public void setUser_name(String user_name) { + this.user_name = user_name; + } + + public String getAlias() { + return alias; + } + + public void setAlias(String alias) { + this.alias = alias; + } + + public BusinessInfo getBusiness_info() { + return business_info; + } + + public void setBusiness_info(BusinessInfo business_info) { + this.business_info = business_info; + } + + public String getQrcode_url() { + return qrcode_url; + } + + public void setQrcode_url(String qrcode_url) { + this.qrcode_url = qrcode_url; + } +} \ No newline at end of file diff --git a/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/api/result/BusinessInfo.java b/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/api/result/BusinessInfo.java new file mode 100644 index 0000000..e80bb9a --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/api/result/BusinessInfo.java @@ -0,0 +1,75 @@ +package com.jfinal.weixin.sdk.api.component.authorizer.type.api.result; + +import com.jfinal.weixin.sdk.api.component.authorizer.type.BusinessType; + +import java.util.ArrayList; +import java.util.List; + +public class BusinessInfo { + private static final int OPENED = 1; + private Integer open_store; + private Integer open_scan; + private Integer open_pay; + private Integer open_card; + private Integer open_shake; + + public Integer getOpen_store() { + return open_store; + } + + public void setOpen_store(Integer open_store) { + this.open_store = open_store; + } + + public Integer getOpen_scan() { + return open_scan; + } + + public void setOpen_scan(Integer open_scan) { + this.open_scan = open_scan; + } + + public Integer getOpen_pay() { + return open_pay; + } + + public void setOpen_pay(Integer open_pay) { + this.open_pay = open_pay; + } + + public Integer getOpen_card() { + return open_card; + } + + public void setOpen_card(Integer open_card) { + this.open_card = open_card; + } + + public Integer getOpen_shake() { + return open_shake; + } + + public void setOpen_shake(Integer open_shake) { + this.open_shake = open_shake; + } + + public List to() { + List types = new ArrayList(); + if (open_store == OPENED) { + types.add(BusinessType.Store); + } + if (open_scan == OPENED) { + types.add(BusinessType.Scan); + } + if (open_pay == OPENED) { + types.add(BusinessType.Pay); + } + if (open_card == OPENED) { + types.add(BusinessType.Card); + } + if (open_shake == OPENED) { + types.add(BusinessType.Shake); + } + return types; + } +} \ No newline at end of file diff --git a/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/api/result/Category.java b/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/api/result/Category.java new file mode 100644 index 0000000..665ee24 --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/api/result/Category.java @@ -0,0 +1,13 @@ +package com.jfinal.weixin.sdk.api.component.authorizer.type.api.result; + +public class Category { + private int id; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } +} diff --git a/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/api/result/FunctionInfo.java b/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/api/result/FunctionInfo.java new file mode 100644 index 0000000..23976c7 --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/api/result/FunctionInfo.java @@ -0,0 +1,62 @@ +package com.jfinal.weixin.sdk.api.component.authorizer.type.api.result; + +import com.jfinal.weixin.sdk.api.component.authorizer.type.FunctionType; + +import java.util.ArrayList; +import java.util.List; + +public class FunctionInfo { + private String appid; + private List func_info; + private String authorizer_appid; // 正确获取到 access_token 时有值 + private String authorizer_access_token; // 正确获取到 access_token 时有值 + private String authorizer_refresh_token; // 正确获取到 access_token 时有值 + + public String getAppid() { + return appid; + } + + public void setAppid(String appid) { + this.appid = appid; + } + + public List getFunc_info() { + return func_info; + } + + public void setFunc_info(List func_info) { + this.func_info = func_info; + } + + public List to() { + List functionTypes = new ArrayList(); + for (FunctionScope scope : func_info) { + functionTypes.add(FunctionType.of(scope.getFuncscope_category().getId())); + } + return functionTypes; + } + + public String getAuthorizer_appid() { + return authorizer_appid; + } + + public void setAuthorizer_appid(String authorizer_appid) { + this.authorizer_appid = authorizer_appid; + } + + public String getAuthorizer_access_token() { + return authorizer_access_token; + } + + public void setAuthorizer_access_token(String authorizer_access_token) { + this.authorizer_access_token = authorizer_access_token; + } + + public String getAuthorizer_refresh_token() { + return authorizer_refresh_token; + } + + public void setAuthorizer_refresh_token(String authorizer_refresh_token) { + this.authorizer_refresh_token = authorizer_refresh_token; + } +} \ No newline at end of file diff --git a/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/api/result/FunctionScope.java b/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/api/result/FunctionScope.java new file mode 100644 index 0000000..eb73e4a --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/api/result/FunctionScope.java @@ -0,0 +1,13 @@ +package com.jfinal.weixin.sdk.api.component.authorizer.type.api.result; + +public class FunctionScope { + private Category funcscope_category; + + public Category getFuncscope_category() { + return funcscope_category; + } + + public void setFuncscope_category(Category funcscope_category) { + this.funcscope_category = funcscope_category; + } +} diff --git a/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/api/result/TypeInfo.java b/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/api/result/TypeInfo.java new file mode 100644 index 0000000..7eff26d --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/api/component/authorizer/type/api/result/TypeInfo.java @@ -0,0 +1,13 @@ +package com.jfinal.weixin.sdk.api.component.authorizer.type.api.result; + +public class TypeInfo { + private Integer id; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } +} \ No newline at end of file diff --git a/src/main/java/com/jfinal/weixin/sdk/cache/DefaultAccessTokenCache.java b/src/main/java/com/jfinal/weixin/sdk/cache/DefaultAccessTokenCache.java index 286c5dd..ef9878d 100644 --- a/src/main/java/com/jfinal/weixin/sdk/cache/DefaultAccessTokenCache.java +++ b/src/main/java/com/jfinal/weixin/sdk/cache/DefaultAccessTokenCache.java @@ -1,5 +1,10 @@ package com.jfinal.weixin.sdk.cache; +import com.jfinal.weixin.sdk.api.component.ComponentAccessToken; +import com.jfinal.weixin.sdk.api.component.ComponentAuthorizerAccessToken; +import com.jfinal.weixin.sdk.api.component.ComponentPreAuthCode; +import com.jfinal.weixin.sdk.api.component.ComponentVerifyTicket; + import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -8,21 +13,66 @@ import java.util.concurrent.ConcurrentHashMap; */ public class DefaultAccessTokenCache implements IAccessTokenCache { - private Map map = new ConcurrentHashMap(); + private Map map = new ConcurrentHashMap(); + + @SuppressWarnings("unchecked") + @Override + public T get(String key) { + return (T) map.get(ACCESS_TOKEN_PREFIX + key); + } + + @Override + public void set(String key, Object value) { + map.put(ACCESS_TOKEN_PREFIX + key, value); + } + + @Override + public void setComponentVerifyTicket(String key, ComponentVerifyTicket value) { + map.put(COMPONENT_VERIFY_TICKET_PREFIX + key, value); + } + + @Override + public ComponentVerifyTicket getComponentVerifyTicket(String key) { + return (ComponentVerifyTicket) map.get(COMPONENT_VERIFY_TICKET_PREFIX + key); + } + + @Override + public void setComponentAccessToken(String key, ComponentAccessToken value) { + map.put(COMPONENT_ACCESS_TOKEN_PREFIX + key, value); + } @Override - public String get(String key) { - return map.get(key); + public ComponentAccessToken getComponentAccessToken(String key) { + return (ComponentAccessToken) map.get(COMPONENT_ACCESS_TOKEN_PREFIX + key); + } + + @Override + public void setComponentPreAuthCode(String key, ComponentPreAuthCode value) { + map.put(COMPONENT_PRE_AUTH_CODE_PREFIX + key, value); + } + + @Override + public ComponentPreAuthCode getComponentPreAuthCode(String key) { + return (ComponentPreAuthCode) map.get(COMPONENT_PRE_AUTH_CODE_PREFIX + key); + } + + @Override + public void setComponentAuthorizerAccessToken(String key1, + String key2, + ComponentAuthorizerAccessToken value) { + map.put(COMPONENT_AUTHORIZER_ACCESS_TOKEN_PREFIX + key1 + "_" + key2, value); + } @Override - public void set(String key, String jsonValue) { - map.put(key, jsonValue); + public ComponentAuthorizerAccessToken getComponentAuthorizerAccessToken(String key1, + String key2) { + return (ComponentAuthorizerAccessToken) map.get(COMPONENT_AUTHORIZER_ACCESS_TOKEN_PREFIX + key1 + "_" + key2); } @Override public void remove(String key) { - map.remove(key); + map.remove(ACCESS_TOKEN_PREFIX + key); } } diff --git a/src/main/java/com/jfinal/weixin/sdk/cache/IAccessTokenCache.java b/src/main/java/com/jfinal/weixin/sdk/cache/IAccessTokenCache.java index dd8ee16..7d35d0c 100644 --- a/src/main/java/com/jfinal/weixin/sdk/cache/IAccessTokenCache.java +++ b/src/main/java/com/jfinal/weixin/sdk/cache/IAccessTokenCache.java @@ -1,13 +1,44 @@ package com.jfinal.weixin.sdk.cache; +import com.jfinal.weixin.sdk.api.component.ComponentAccessToken; +import com.jfinal.weixin.sdk.api.component.ComponentAuthorizerAccessToken; +import com.jfinal.weixin.sdk.api.component.ComponentPreAuthCode; +import com.jfinal.weixin.sdk.api.component.ComponentVerifyTicket; + public interface IAccessTokenCache { // 默认超时时间7200秒 5秒用于程序执行误差 - int DEFAULT_TIME_OUT = 7200 - 5; + int TIME_OUT_OFFSET = 5; + int DEFAULT_TIME_OUT = 7200 - TIME_OUT_OFFSET; + String ACCESS_TOKEN_PREFIX = "ac_"; + String COMPONENT_VERIFY_TICKET_PREFIX = "cvt_"; + String COMPONENT_ACCESS_TOKEN_PREFIX = "cat_"; + String COMPONENT_PRE_AUTH_CODE_PREFIX = "cpac_"; + String COMPONENT_AUTHORIZER_ACCESS_TOKEN_PREFIX = "caat_"; + + T get(String key); + + void set(String key, Object value); + + void setComponentVerifyTicket(String key, ComponentVerifyTicket value); + + ComponentVerifyTicket getComponentVerifyTicket(String key); + + void setComponentAccessToken(String key, ComponentAccessToken value); + + ComponentAccessToken getComponentAccessToken(String key); + + void setComponentPreAuthCode(String key, ComponentPreAuthCode value); + + ComponentPreAuthCode getComponentPreAuthCode(String key); + + + void setComponentAuthorizerAccessToken(String key1, + String key2, + ComponentAuthorizerAccessToken value); - String get(String key); - void set(String key, String jsonValue); + ComponentAuthorizerAccessToken getComponentAuthorizerAccessToken(String key1, String key2); void remove(String key); diff --git a/src/main/java/com/jfinal/weixin/sdk/cache/RedisAccessTokenCache.java b/src/main/java/com/jfinal/weixin/sdk/cache/RedisAccessTokenCache.java index 4276e80..d3e979e 100644 --- a/src/main/java/com/jfinal/weixin/sdk/cache/RedisAccessTokenCache.java +++ b/src/main/java/com/jfinal/weixin/sdk/cache/RedisAccessTokenCache.java @@ -2,9 +2,13 @@ package com.jfinal.weixin.sdk.cache; import com.jfinal.plugin.redis.Cache; import com.jfinal.plugin.redis.Redis; +import com.jfinal.weixin.sdk.api.component.ComponentAccessToken; +import com.jfinal.weixin.sdk.api.component.ComponentAuthorizerAccessToken; +import com.jfinal.weixin.sdk.api.component.ComponentPreAuthCode; +import com.jfinal.weixin.sdk.api.component.ComponentVerifyTicket; public class RedisAccessTokenCache implements IAccessTokenCache { - private final String ACCESS_TOKEN_PREFIX = "jfinal-weixin:token:"; + private final Cache cache; @@ -21,18 +25,66 @@ public class RedisAccessTokenCache implements IAccessTokenCache { } @Override - public String get(String key) { - return cache.get(ACCESS_TOKEN_PREFIX.concat(key)); + public T get(String key) { + return cache.get(ACCESS_TOKEN_PREFIX + key); + } + + @Override + public void set(String key, Object object) { + cache.setex(ACCESS_TOKEN_PREFIX + key, DEFAULT_TIME_OUT, object); + } + + @Override + public void setComponentVerifyTicket(String key, ComponentVerifyTicket value) { + cache.set(COMPONENT_VERIFY_TICKET_PREFIX + key, value); + } + + @Override + public ComponentVerifyTicket getComponentVerifyTicket(String key) { + return cache.get(COMPONENT_VERIFY_TICKET_PREFIX + key); + } + + @Override + public void setComponentAccessToken(String key, ComponentAccessToken value) { + cache.setex(COMPONENT_ACCESS_TOKEN_PREFIX + key, + value.getExpiresIn() - TIME_OUT_OFFSET, + value); } @Override - public void set(String key, String jsonValue) { - cache.setex(ACCESS_TOKEN_PREFIX.concat(key), DEFAULT_TIME_OUT, jsonValue); + public ComponentAccessToken getComponentAccessToken(String key) { + return cache.get(COMPONENT_ACCESS_TOKEN_PREFIX + key); + } + + @Override + public void setComponentPreAuthCode(String key, ComponentPreAuthCode value) { + cache.setex(COMPONENT_PRE_AUTH_CODE_PREFIX + key, + value.getExpiresIn() - TIME_OUT_OFFSET, + value); + } + + @Override + public ComponentPreAuthCode getComponentPreAuthCode(String key) { + return cache.get(COMPONENT_PRE_AUTH_CODE_PREFIX + key); + } + + @Override + public void setComponentAuthorizerAccessToken(String key1, + String key2, + ComponentAuthorizerAccessToken value) { + cache.set(COMPONENT_AUTHORIZER_ACCESS_TOKEN_PREFIX + key1 + "_" + key2, value); + } + + + @Override + public ComponentAuthorizerAccessToken getComponentAuthorizerAccessToken(String key1, + String key2) { + return cache.get(COMPONENT_AUTHORIZER_ACCESS_TOKEN_PREFIX + key1 + "_" + key2); } @Override public void remove(String key) { - cache.del(ACCESS_TOKEN_PREFIX.concat(key)); + cache.del(ACCESS_TOKEN_PREFIX + key); } } -- Gitee From ed4ebcfb605398f8b122c334f1f9586e60f7ab77 Mon Sep 17 00:00:00 2001 From: ShylockGou Date: Mon, 17 Oct 2016 00:11:41 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E5=85=AC=E4=BC=97=E5=8F=B7=E7=AC=AC?= =?UTF-8?q?=E4=B8=89=E6=96=B9=E5=B9=B3=E5=8F=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../weixin/demo/WeixinMsgController.java | 70 +++++ .../jfinal/weixin/sdk/api/ApiConfigKit.java | 126 ++++++-- .../weixin/sdk/cache/LocalTestTokenCache.java | 69 ++++- .../weixin/sdk/jfinal/MsgController.java | 291 ++++-------------- .../component/AuthMessageController.java | 96 ++++++ .../AuthMessageControllerAdapter.java | 52 ++++ .../component/AuthMessageInterceptor.java | 84 +++++ .../component/ComponentAPIController.java | 13 + .../component/ComponentAPIInterceptor.java | 27 ++ .../jfinal/weixin/sdk/kit/MsgEncryptKit.java | 80 ++++- .../weixin/sdk/msg/ComponentMsgParser.java | 140 +++++++++ .../jfinal/weixin/sdk/msg/InMsgParser.java | 210 ++++++++++--- .../weixin/sdk/msg/component/AuthMsg.java | 76 +++++ .../sdk/msg/component/AuthorizedMsg.java | 64 ++++ .../component/ComponentVerifyTicketMsg.java | 53 ++++ .../msg/component/NotDefinedComponentMsg.java | 11 + .../sdk/msg/component/UnAuthorizedMsg.java | 55 ++++ .../msg/component/UpdateAuthorizedMsg.java | 70 +++++ .../in/event/InShakearoundUserShakeEvent.java | 86 +++--- .../weixin/sdk/utils/ComponentWeixinUtil.java | 61 ++++ .../jfinal/weixin/sdk/utils/WeixinUtil.java | 60 ++++ 21 files changed, 1430 insertions(+), 364 deletions(-) create mode 100644 src/main/java/com/jfinal/weixin/sdk/jfinal/component/AuthMessageController.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/jfinal/component/AuthMessageControllerAdapter.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/jfinal/component/AuthMessageInterceptor.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/jfinal/component/ComponentAPIController.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/jfinal/component/ComponentAPIInterceptor.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/msg/ComponentMsgParser.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/msg/component/AuthMsg.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/msg/component/AuthorizedMsg.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/msg/component/ComponentVerifyTicketMsg.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/msg/component/NotDefinedComponentMsg.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/msg/component/UnAuthorizedMsg.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/msg/component/UpdateAuthorizedMsg.java create mode 100755 src/main/java/com/jfinal/weixin/sdk/utils/ComponentWeixinUtil.java create mode 100755 src/main/java/com/jfinal/weixin/sdk/utils/WeixinUtil.java diff --git a/src/main/java/com/jfinal/weixin/demo/WeixinMsgController.java b/src/main/java/com/jfinal/weixin/demo/WeixinMsgController.java index 4a98924..34b61a0 100644 --- a/src/main/java/com/jfinal/weixin/demo/WeixinMsgController.java +++ b/src/main/java/com/jfinal/weixin/demo/WeixinMsgController.java @@ -6,13 +6,21 @@ package com.jfinal.weixin.demo; +import com.jfinal.kit.JsonKit; import com.jfinal.log.Log; +import com.jfinal.weixin.sdk.api.ApiConfig; +import com.jfinal.weixin.sdk.api.ApiConfigKit; +import com.jfinal.weixin.sdk.api.ApiResult; +import com.jfinal.weixin.sdk.api.CustomServiceApi; +import com.jfinal.weixin.sdk.api.component.ComponentAuthApi; +import com.jfinal.weixin.sdk.api.component.ComponentAuthorizerAccessToken; import com.jfinal.weixin.sdk.jfinal.MsgControllerAdapter; import com.jfinal.weixin.sdk.msg.in.*; import com.jfinal.weixin.sdk.msg.in.event.*; import com.jfinal.weixin.sdk.msg.in.speech_recognition.InSpeechRecognitionResults; import com.jfinal.weixin.sdk.msg.out.OutCustomMsg; import com.jfinal.weixin.sdk.msg.out.OutTextMsg; +import com.jfinal.weixin.sdk.utils.ComponentWeixinUtil; /** * 将此 DemoController 在YourJFinalConfig 中注册路由, @@ -27,6 +35,11 @@ public class WeixinMsgController extends MsgControllerAdapter { protected void processInTextMsg(InTextMsg inTextMsg) { + final String msgContent = inTextMsg.getContent(); + if (msgContent.equals("TESTCOMPONENT_MSG_TYPE_TEXT") || msgContent.startsWith( + "QUERY_AUTH_CODE")) { + processTestTextMsg(inTextMsg); + } //转发给多客服PC客户端 OutCustomMsg outCustomMsg = new OutCustomMsg(inTextMsg); render(outCustomMsg); @@ -164,6 +177,63 @@ public class WeixinMsgController extends MsgControllerAdapter { renderNull(); } + + @Override + public ApiConfig getApiConfig() { + return null; + } + + @Override + public void processEvent(EventInMsg msg) { + final String toUserName = msg.getToUserName(); + if (toUserName.equals("gh_3c884a361561")) { + processTestEvent(msg); + } else { + super.processEvent(msg); + } + } + + + private void processTestEvent(EventInMsg msg) { + OutTextMsg outMsg = new OutTextMsg(msg); + outMsg.setContent(msg.getEvent() + "from_callback"); + render(outMsg); + } + + protected void processTestTextMsg(InTextMsg inTextMsg) { + String msgContent = inTextMsg.getContent().trim(); + if (msgContent.equals("TESTCOMPONENT_MSG_TYPE_TEXT")) { + final String toUserName = inTextMsg.getToUserName(); + if (toUserName.equals("gh_3c884a361561")) { + OutTextMsg outMsg = new OutTextMsg(inTextMsg); + outMsg.setContent(msgContent + "_callback"); + render(outMsg); + } + + } else if (msgContent.startsWith("QUERY_AUTH_CODE")) { + final String toUserName = inTextMsg.getToUserName(); + if (toUserName.equals("gh_3c884a361561")) { + OutTextMsg outMsg = new OutTextMsg(inTextMsg); + outMsg.setContent("success"); + render(outMsg); + String query_auth_code = msgContent.split(":")[1]; + ApiConfigKit.setThreadLocalComponentApiConfig(ComponentWeixinUtil.getApiConfig()); + final ApiResult auth = ComponentAuthApi.auth(query_auth_code); + if (auth != null && auth.isSucceed()) { + final String authorization_info = JsonKit.toJson(auth.get( + "authorization_info")); + ComponentAuthorizerAccessToken token = new ComponentAuthorizerAccessToken( + authorization_info); + + ApiConfigKit.putApiConfig(new ApiConfig("", + token.getAuthorizerAppId(), + "")); + final ApiResult apiResult = CustomServiceApi.sendText(inTextMsg.getFromUserName(), + query_auth_code + "_from_api"); + } + } + } + } // /** // * 实现父类抽方法,处理文本消息 // * 本例子中根据消息中的不同文本内容分别做出了不同的响应,同时也是为了测试 jfinal weixin sdk的基本功能: diff --git a/src/main/java/com/jfinal/weixin/sdk/api/ApiConfigKit.java b/src/main/java/com/jfinal/weixin/sdk/api/ApiConfigKit.java index c9949f8..6c263a3 100644 --- a/src/main/java/com/jfinal/weixin/sdk/api/ApiConfigKit.java +++ b/src/main/java/com/jfinal/weixin/sdk/api/ApiConfigKit.java @@ -1,22 +1,37 @@ package com.jfinal.weixin.sdk.api; +import com.jfinal.kit.StrKit; +import com.jfinal.log.Log; import com.jfinal.weixin.sdk.cache.DefaultAccessTokenCache; import com.jfinal.weixin.sdk.cache.IAccessTokenCache; +import com.jfinal.weixin.sdk.session.DefaultWxSessionManager; +import com.jfinal.weixin.sdk.session.WxSessionManager; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * 将 ApiConfig 绑定到 ThreadLocal 的工具类,以方便在当前线程的各个地方获取 ApiConfig 对象: * 1:如果控制器继承自 MsgController 该过程是自动的,详细可查看 MsgInterceptor 与之的配合 * 2:如果控制器继承自 ApiController 该过程是自动的,详细可查看 ApiInterceptor 与之的配合 * 3:如果控制器没有继承自 MsgController、ApiController,则需要先手动调用 - * ApiConfigKit.setThreadLocalApiConfig(ApiConfig) 来绑定 apiConfig 到线程之上 + * ApiConfigKit.setThreadLocalAppId(appId) 来绑定 appId 到线程之上 */ public class ApiConfigKit { - + private static final Log log = Log.getLog( + ApiConfigKit.class); private static final ThreadLocal API_CONFIG_THREAD_LOCAL = new ThreadLocal(); private static final ThreadLocal COMPONENT_API_CONFIG_THREAD_LOCAL = new ThreadLocal(); - static IAccessTokenCache accessTokenCache = new DefaultAccessTokenCache(); + private static final ThreadLocal TL = new ThreadLocal(); + private static final Map CFG_MAP = new ConcurrentHashMap(); + private static final String DEFAULT_CFG_KEY = "_default_cfg_key_"; + private static IAccessTokenCache accessTokenCache = new DefaultAccessTokenCache(); // 开发模式将输出消息交互 xml 到控制台 private static boolean devMode = false; + /** + * 是否启用session,默认不启用 + */ + private static WxSessionManager sessionManager = null; public static boolean isDevMode() { return devMode; @@ -26,36 +41,60 @@ public class ApiConfigKit { ApiConfigKit.devMode = devMode; } - public static void setThreadLocalApiConfig(ApiConfig apiConfig) { - API_CONFIG_THREAD_LOCAL.set(apiConfig); + /** + * 添加公众号配置,每个appId只需添加一次,相同appId将被覆盖。 + * 第一个添加的将作为默认公众号配置 + * + * @param apiConfig 公众号配置 + * @return ApiConfig 公众号配置 + */ + public static ApiConfig putApiConfig(ApiConfig apiConfig) { + if (CFG_MAP.size() == 0) { + CFG_MAP.put(DEFAULT_CFG_KEY, apiConfig); + } + return CFG_MAP.put(apiConfig.getAppId(), apiConfig); } - public static void setThreadLocalComponentApiConfig(ApiConfig apiConfig) { - COMPONENT_API_CONFIG_THREAD_LOCAL.set(apiConfig); + public static ApiConfig removeApiConfig(ApiConfig apiConfig) { + return removeApiConfig(apiConfig.getAppId()); } - public static void removeThreadLocalApiConfig() { - API_CONFIG_THREAD_LOCAL.remove(); + public static ApiConfig removeApiConfig(String appId) { + return CFG_MAP.remove(appId); } - public static void removeThreadLocalComponentApiConfig() { - COMPONENT_API_CONFIG_THREAD_LOCAL.remove(); + public static void setThreadLocalAppId(String appId) { + if (StrKit.isBlank(appId)) { + appId = CFG_MAP.get(DEFAULT_CFG_KEY).getAppId(); + } + TL.set(appId); + } + + public static void removeThreadLocalAppId() { + TL.remove(); + } + + public static String getAppId() { + String appId = TL.get(); + if (StrKit.isBlank(appId)) { + appId = CFG_MAP.get(DEFAULT_CFG_KEY).getAppId(); + } + return appId; } public static ApiConfig getApiConfig() { - ApiConfig result = API_CONFIG_THREAD_LOCAL.get(); - if (result == null) - throw new IllegalStateException( - "需要事先使用 ApiConfigKit.setThreadLocalApiConfig(apiConfig) 将 ApiConfig对象存入,才可以调用 ApiConfigKit.getComponentApiConfig() 方法"); - return result; + String appId = getAppId(); + return getApiConfig(appId); } - public static ApiConfig getComponentApiConfig() { - ApiConfig result = COMPONENT_API_CONFIG_THREAD_LOCAL.get(); - if (result == null) + public static ApiConfig getApiConfig(String appId) { + log.debug("appId: " + appId); + ApiConfig cfg = CFG_MAP.get(appId); + if (cfg == null) throw new IllegalStateException( - "需要事先使用 ApiConfigKit.setThreadLocalApiConfig(apiConfig) 将 ApiConfig对象存入,才可以调用 ApiConfigKit.getComponentApiConfig() 方法"); - return result; + "需事先调用 ApiConfigKit.putApiConfig(apiConfig) 将 appId对应的 ApiConfig 对象存入," + + "如JFinalConfig.afterJFinalStart()中调用, 才可以使用 ApiConfigKit.getApiConfig() 系列方法"); + return cfg; } public static IAccessTokenCache getAccessTokenCache() { @@ -66,4 +105,49 @@ public class ApiConfigKit { ApiConfigKit.accessTokenCache = accessTokenCache; } + /** + * 启用默认的session管理器 + */ + public static void enableDefaultWxSessionManager() { + setWxSessionManager(new DefaultWxSessionManager()); + } + + /** + * 获取微信session管理器 + * + * @return sessionManager session管理器 + */ + public static WxSessionManager getWxSessionManager() { + WxSessionManager sessionManager = ApiConfigKit.sessionManager; + if (null == sessionManager) { + throw new NullPointerException( + "WxSessionManager is null, Please setWxSessionManager first!"); + } + return sessionManager; + } + + /** + * 设置微信session管理器 + * + * @param sessionManager session管理器 + */ + public static void setWxSessionManager(WxSessionManager sessionManager) { + ApiConfigKit.sessionManager = sessionManager; + } + + public static void setThreadLocalComponentApiConfig(ApiConfig apiConfig) { + COMPONENT_API_CONFIG_THREAD_LOCAL.set(apiConfig); + } + + public static void removeThreadLocalComponentApiConfig() { + COMPONENT_API_CONFIG_THREAD_LOCAL.remove(); + } + + public static ApiConfig getComponentApiConfig() { + ApiConfig result = COMPONENT_API_CONFIG_THREAD_LOCAL.get(); + if (result == null) + throw new IllegalStateException( + "需要事先使用 ApiConfigKit.setThreadLocalApiConfig(apiConfig) 将 ApiConfig对象存入,才可以调用 ApiConfigKit.getComponentApiConfig() 方法"); + return result; + } } diff --git a/src/main/java/com/jfinal/weixin/sdk/cache/LocalTestTokenCache.java b/src/main/java/com/jfinal/weixin/sdk/cache/LocalTestTokenCache.java index 7ee5be9..d5c49ef 100644 --- a/src/main/java/com/jfinal/weixin/sdk/cache/LocalTestTokenCache.java +++ b/src/main/java/com/jfinal/weixin/sdk/cache/LocalTestTokenCache.java @@ -1,13 +1,17 @@ package com.jfinal.weixin.sdk.cache; +import com.jfinal.core.JFinal; +import com.jfinal.kit.HttpKit; +import com.jfinal.weixin.sdk.api.component.ComponentAccessToken; +import com.jfinal.weixin.sdk.api.component.ComponentAuthorizerAccessToken; +import com.jfinal.weixin.sdk.api.component.ComponentPreAuthCode; +import com.jfinal.weixin.sdk.api.component.ComponentVerifyTicket; + import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import com.jfinal.core.JFinal; -import com.jfinal.kit.HttpKit; - /** * 本地测试和线上同时使用同一个或者一批公众号测试时使用 * @author L.cm @@ -15,33 +19,80 @@ import com.jfinal.kit.HttpKit; */ public class LocalTestTokenCache implements IAccessTokenCache { private Map map = new ConcurrentHashMap(); - + /** * 线上AccessToken请求地址 * 需要开发一个获取AccessToken字符串的地址 */ private final String onLineTokenUrl; - + public LocalTestTokenCache(String onLineTokenUrl) { this.onLineTokenUrl = onLineTokenUrl; } - + @Override public String get(String key) { String url = onLineTokenUrl + "?key=" + enCodeUrl(key); return HttpKit.get(url); } - - @Override + public void set(String key, String jsonValue) { map.put(key, jsonValue); } + @Override + public void set(String key, Object value) { + + } + + @Override + public void setComponentVerifyTicket(String key, ComponentVerifyTicket value) { + + } + + @Override + public ComponentVerifyTicket getComponentVerifyTicket(String key) { + return null; + } + + @Override + public void setComponentAccessToken(String key, ComponentAccessToken value) { + + } + + @Override + public ComponentAccessToken getComponentAccessToken(String key) { + return null; + } + + @Override + public void setComponentPreAuthCode(String key, ComponentPreAuthCode value) { + + } + + @Override + public ComponentPreAuthCode getComponentPreAuthCode(String key) { + return null; + } + + @Override + public void setComponentAuthorizerAccessToken(String key1, + String key2, + ComponentAuthorizerAccessToken value) { + + } + + @Override + public ComponentAuthorizerAccessToken getComponentAuthorizerAccessToken(String key1, + String key2) { + return null; + } + @Override public void remove(String key) { map.remove(key); } - + private static String enCodeUrl(String key) { try { return URLEncoder.encode(key, JFinal.me().getConstants().getEncoding()); diff --git a/src/main/java/com/jfinal/weixin/sdk/jfinal/MsgController.java b/src/main/java/com/jfinal/weixin/sdk/jfinal/MsgController.java index a39dd87..f4a9480 100644 --- a/src/main/java/com/jfinal/weixin/sdk/jfinal/MsgController.java +++ b/src/main/java/com/jfinal/weixin/sdk/jfinal/MsgController.java @@ -12,50 +12,26 @@ import com.jfinal.ext.interceptor.NotAction; import com.jfinal.kit.HttpKit; import com.jfinal.kit.StrKit; import com.jfinal.log.Log; +import com.jfinal.weixin.sdk.api.ApiConfig; import com.jfinal.weixin.sdk.api.ApiConfigKit; import com.jfinal.weixin.sdk.kit.MsgEncryptKit; import com.jfinal.weixin.sdk.msg.InMsgParser; -import com.jfinal.weixin.sdk.msg.in.InImageMsg; -import com.jfinal.weixin.sdk.msg.in.InLinkMsg; -import com.jfinal.weixin.sdk.msg.in.InLocationMsg; -import com.jfinal.weixin.sdk.msg.in.InMsg; -import com.jfinal.weixin.sdk.msg.in.InNotDefinedMsg; -import com.jfinal.weixin.sdk.msg.in.InShortVideoMsg; -import com.jfinal.weixin.sdk.msg.in.InTextMsg; -import com.jfinal.weixin.sdk.msg.in.InVideoMsg; -import com.jfinal.weixin.sdk.msg.in.InVoiceMsg; -import com.jfinal.weixin.sdk.msg.in.event.InCustomEvent; -import com.jfinal.weixin.sdk.msg.in.event.InFollowEvent; -import com.jfinal.weixin.sdk.msg.in.event.InLocationEvent; -import com.jfinal.weixin.sdk.msg.in.event.InMassEvent; -import com.jfinal.weixin.sdk.msg.in.event.InMenuEvent; -import com.jfinal.weixin.sdk.msg.in.event.InMerChantOrderEvent; -import com.jfinal.weixin.sdk.msg.in.event.InNotDefinedEvent; -import com.jfinal.weixin.sdk.msg.in.event.InPoiCheckNotifyEvent; -import com.jfinal.weixin.sdk.msg.in.event.InQrCodeEvent; -import com.jfinal.weixin.sdk.msg.in.event.InShakearoundUserShakeEvent; -import com.jfinal.weixin.sdk.msg.in.event.InSubmitMemberCardEvent; -import com.jfinal.weixin.sdk.msg.in.event.InTemplateMsgEvent; -import com.jfinal.weixin.sdk.msg.in.event.InUpdateMemberCardEvent; -import com.jfinal.weixin.sdk.msg.in.event.InUserPayFromCardEvent; -import com.jfinal.weixin.sdk.msg.in.event.InUserViewCardEvent; -import com.jfinal.weixin.sdk.msg.in.event.InVerifyFailEvent; -import com.jfinal.weixin.sdk.msg.in.event.InVerifySuccessEvent; -import com.jfinal.weixin.sdk.msg.in.event.InWifiEvent; +import com.jfinal.weixin.sdk.msg.in.*; +import com.jfinal.weixin.sdk.msg.in.event.*; import com.jfinal.weixin.sdk.msg.in.speech_recognition.InSpeechRecognitionResults; import com.jfinal.weixin.sdk.msg.out.OutMsg; import com.jfinal.weixin.sdk.msg.out.OutTextMsg; -import com.jfinal.weixin.sdk.session.WxSession; -import com.jfinal.weixin.sdk.session.WxSessionManager; /** * 接收微信服务器消息,自动解析成 InMsg 并分发到相应的处理方法 */ public abstract class MsgController extends Controller { - private static final Log log = Log.getLog(MsgController.class); - private String inMsgXml = null; // 本次请求 xml数据 - private InMsg inMsg = null; // 本次请求 xml 解析后的 InMsg 对象 + private static final Log log = Log.getLog(MsgController.class); + private String inMsgXml = null; // 本次请求 xml数据 + private InMsg inMsg = null; // 本次请求 xml 解析后的 InMsg 对象 + + public abstract ApiConfig getApiConfig(); /** * weixin 公众号服务器调用唯一入口,即在开发者中心输入的 URL 必须要指向此 action @@ -70,7 +46,6 @@ public abstract class MsgController extends Controller { // 解析消息并根据消息类型分发到相应的处理方法 InMsg msg = getInMsg(); - if (msg instanceof InTextMsg) processInTextMsg((InTextMsg) msg); else if (msg instanceof InImageMsg) @@ -87,7 +62,17 @@ public abstract class MsgController extends Controller { processInLocationMsg((InLocationMsg) msg); else if (msg instanceof InLinkMsg) processInLinkMsg((InLinkMsg) msg); - else if (msg instanceof InCustomEvent) + else if (msg instanceof EventInMsg) + processEvent((EventInMsg) msg); + else if (msg instanceof InNotDefinedMsg) { + log.error("未能识别的消息类型。 消息 xml 内容为:\n" + getInMsgXml()); + processIsNotDefinedMsg((InNotDefinedMsg) msg); + } + } + + + public void processEvent(EventInMsg msg) { + if (msg instanceof InCustomEvent) processInCustomEvent((InCustomEvent) msg); else if (msg instanceof InFollowEvent) processInFollowEvent((InFollowEvent) msg); @@ -124,15 +109,11 @@ public abstract class MsgController extends Controller { else if (msg instanceof InNotDefinedEvent) { log.error("未能识别的事件类型。 消息 xml 内容为:\n" + getInMsgXml()); processIsNotDefinedEvent((InNotDefinedEvent) msg); - } else if (msg instanceof InNotDefinedMsg) { - log.error("未能识别的消息类型。 消息 xml 内容为:\n" + getInMsgXml()); - processIsNotDefinedMsg((InNotDefinedMsg) msg); } } /** * 在接收到微信服务器的 InMsg 消息后后响应 OutMsg 消息 - * * @param outMsg 输出对象 */ public void render(OutMsg outMsg) { @@ -141,7 +122,8 @@ public abstract class MsgController extends Controller { if (ApiConfigKit.isDevMode()) { System.out.println("发送消息:"); System.out.println(outMsgXml); - System.out.println("--------------------------------------------------------------------------------\n"); + System.out.println( + "--------------------------------------------------------------------------------\n"); } // 是否需要加密消息 @@ -152,10 +134,6 @@ public abstract class MsgController extends Controller { renderText(outMsgXml, "text/xml"); } - /** - * 消息输出 - * @param content 输出的消息 - */ public void renderOutTextMsg(String content) { OutTextMsg outMsg = new OutTextMsg(getInMsg()); outMsg.setContent(content); @@ -169,11 +147,15 @@ public abstract class MsgController extends Controller { // 是否需要解密消息 if (ApiConfigKit.getApiConfig().isEncryptMessage()) { - inMsgXml = MsgEncryptKit.decrypt(inMsgXml, getPara("timestamp"), getPara("nonce"), getPara("msg_signature")); + inMsgXml = MsgEncryptKit.decrypt(inMsgXml, + getPara("timestamp"), + getPara("nonce"), + getPara("msg_signature")); } } if (StrKit.isBlank(inMsgXml)) { - throw new RuntimeException("请不要在浏览器中请求该连接,调试请查看WIKI:http://git.oschina.net/jfinal/jfinal-weixin/wikis/JFinal-weixin-demo%E5%92%8C%E8%B0%83%E8%AF%95"); + throw new RuntimeException( + "请不要在浏览器中请求该连接,调试请查看WIKI:http://git.oschina.net/jfinal/jfinal-weixin/wikis/JFinal-weixin-demo%E5%92%8C%E8%B0%83%E8%AF%95"); } return inMsgXml; } @@ -185,251 +167,86 @@ public abstract class MsgController extends Controller { return inMsg; } - /** - * 处理接收到的文本消息 - * @param inTextMsg 处理接收到的文本消息 - */ + // 处理接收到的文本消息 protected abstract void processInTextMsg(InTextMsg inTextMsg); - /** - * 处理接收到的图片消息 - * @param inImageMsg 处理接收到的图片消息 - */ + // 处理接收到的图片消息 protected abstract void processInImageMsg(InImageMsg inImageMsg); - /** - * 处理接收到的语音消息 - * @param inVoiceMsg 处理接收到的语音消息 - */ + // 处理接收到的语音消息 protected abstract void processInVoiceMsg(InVoiceMsg inVoiceMsg); - /** - * 处理接收到的视频消息 - * @param inVideoMsg 处理接收到的视频消息 - */ + // 处理接收到的视频消息 protected abstract void processInVideoMsg(InVideoMsg inVideoMsg); - /** - * 处理接收到的小视频消息 - * @param inShortVideoMsg 处理接收到的小视频消息 - */ + // 处理接收到的视频消息 protected abstract void processInShortVideoMsg(InShortVideoMsg inShortVideoMsg); - /** - * 处理接收到的地址位置消息 - * @param inLocationMsg 处理接收到的地址位置消息 - */ + // 处理接收到的地址位置消息 protected abstract void processInLocationMsg(InLocationMsg inLocationMsg); - /** - * 处理接收到的链接消息 - * @param inLinkMsg 处理接收到的链接消息 - */ + // 处理接收到的链接消息 protected abstract void processInLinkMsg(InLinkMsg inLinkMsg); - /** - * 处理接收到的多客服管理事件 - * @param inCustomEvent 处理接收到的多客服管理事件 - */ + // 处理接收到的多客服管理事件 protected abstract void processInCustomEvent(InCustomEvent inCustomEvent); - /** - * 处理接收到的关注/取消关注事件 - * @param inFollowEvent 处理接收到的关注/取消关注事件 - */ + // 处理接收到的关注/取消关注事件 protected abstract void processInFollowEvent(InFollowEvent inFollowEvent); - /** - * 处理接收到的扫描带参数二维码事件 - * @param inQrCodeEvent 处理接收到的扫描带参数二维码事件 - */ + // 处理接收到的扫描带参数二维码事件 protected abstract void processInQrCodeEvent(InQrCodeEvent inQrCodeEvent); - /** - * 处理接收到的上报地理位置事件 - * @param inLocationEvent 处理接收到的上报地理位置事件 - */ + // 处理接收到的上报地理位置事件 protected abstract void processInLocationEvent(InLocationEvent inLocationEvent); - /** - * 处理接收到的群发任务结束时通知事件 - * @param inMassEvent 处理接收到的群发任务结束时通知事件 - */ + // 处理接收到的群发任务结束时通知事件 protected abstract void processInMassEvent(InMassEvent inMassEvent); - /** - * 处理接收到的自定义菜单事件 - * @param inMenuEvent 处理接收到的自定义菜单事件 - */ + // 处理接收到的自定义菜单事件 protected abstract void processInMenuEvent(InMenuEvent inMenuEvent); - /** - * 处理接收到的语音识别结果 - * @param inSpeechRecognitionResults 处理接收到的语音识别结果 - */ + // 处理接收到的语音识别结果 protected abstract void processInSpeechRecognitionResults(InSpeechRecognitionResults inSpeechRecognitionResults); - /** - * 处理接收到的模板消息是否送达成功通知事件 - * @param inTemplateMsgEvent 处理接收到的模板消息是否送达成功通知事件 - */ + // 处理接收到的模板消息是否送达成功通知事件 protected abstract void processInTemplateMsgEvent(InTemplateMsgEvent inTemplateMsgEvent); - /** - * 处理微信摇一摇事件 - * @param inShakearoundUserShakeEvent 处理微信摇一摇事件 - */ + // 处理微信摇一摇事件 protected abstract void processInShakearoundUserShakeEvent(InShakearoundUserShakeEvent inShakearoundUserShakeEvent); - /** - * 资质认证成功 || 名称认证成功 || 年审通知 || 认证过期失效通知 - * @param inVerifySuccessEvent 资质认证成功 || 名称认证成功 || 年审通知 || 认证过期失效通知 - */ + // 资质认证成功 || 名称认证成功 || 年审通知 || 认证过期失效通知 protected abstract void processInVerifySuccessEvent(InVerifySuccessEvent inVerifySuccessEvent); - /** - * 资质认证失败 || 名称认证失败 - * @param inVerifyFailEvent 资质认证失败 || 名称认证失败 - */ + // 资质认证失败 || 名称认证失败 protected abstract void processInVerifyFailEvent(InVerifyFailEvent inVerifyFailEvent); - /** - * 门店在审核事件消息 - * @param inPoiCheckNotifyEvent 门店在审核事件消息 - */ + // 门店在审核事件消息 protected abstract void processInPoiCheckNotifyEvent(InPoiCheckNotifyEvent inPoiCheckNotifyEvent); - /** - * WIFI连网后下发消息 by unas at 2016-1-29 - * @param inWifiEvent WIFI连网后下发消息 - */ + // WIFI连网后下发消息 by unas at 2016-1-29 protected abstract void processInWifiEvent(InWifiEvent inWifiEvent); - /** - * 微信会员卡二维码扫描领取接口 - * @param inUserViewCardEvent 微信会员卡二维码扫描领取接口 - */ + // 微信会员卡二维码扫描领取接口 protected abstract void processInUserViewCardEvent(InUserViewCardEvent inUserViewCardEvent); - /** - * 微信会员卡激活接口 - * @param inSubmitMemberCardEvent 微信会员卡激活接口 - */ + // 微信会员卡激活接口 protected abstract void processInSubmitMemberCardEvent(InSubmitMemberCardEvent inSubmitMemberCardEvent); - /** - * 微信会员卡积分变更 - * @param inUpdateMemberCardEvent 微信会员卡积分变更 - */ + // 微信会员卡积分变更 protected abstract void processInUpdateMemberCardEvent(InUpdateMemberCardEvent inUpdateMemberCardEvent); - /** - * 微信会员卡快速买单 - * @param inUserPayFromCardEvent 微信会员卡快速买单 - */ + // 微信会员卡快速买单 protected abstract void processInUserPayFromCardEvent(InUserPayFromCardEvent inUserPayFromCardEvent); - /** - * 微信小店订单支付成功接口消息 - * @param inMerChantOrderEvent 微信小店订单支付成功接口消息 - */ + // 微信小店订单支付成功接口消息 protected abstract void processInMerChantOrderEvent(InMerChantOrderEvent inMerChantOrderEvent); - // - /** - * 没有找到对应的事件消息 - * @param inNotDefinedEvent 没有对应的事件消息 - */ + // 没有找到对应的事件消息 protected abstract void processIsNotDefinedEvent(InNotDefinedEvent inNotDefinedEvent); - /** - * 没有找到对应的消息 - * @param inNotDefinedMsg 没有对应消息 - */ + // 没有找到对应的消息 protected abstract void processIsNotDefinedMsg(InNotDefinedMsg inNotDefinedMsg); - - /** - * 微信会话维持session Id,也就是FromUserName - */ - private String getWxSessionId() { - if (null == inMsg) { - throw new NullPointerException("weixin xml 尚未解析或者解析失败!"); - } - // 用户id - return inMsg.getFromUserName(); - } - - /** - * Return WxSession. - */ - public WxSession getWxSession() { - String sessionId = getWxSessionId(); - // 默认getSession(true) - if (null == sessionId) { - throw new NullPointerException("weixin FromUserName is null!"); - } - WxSessionManager sessionManager = ApiConfigKit.getWxSessionManager(); - WxSession session = sessionManager.get(sessionId); - if (null == session) { - session = new WxSession(sessionId); - sessionManager.save(session); - } - // 由于sessionManager不参与序列化,加上序列化的问题,故手动设置 - session.setManager(sessionManager); - return session; - } - - /** - * Return WxSession. - * @param create a boolean specifying create WxSession if it not exists - */ - public WxSession getWxSession(boolean create) { - if (create) { - return this.getWxSession(); - } - String sessionId = getWxSessionId(); - if (null == sessionId) { - return null; - } - WxSessionManager sessionManager = ApiConfigKit.getWxSessionManager(); - WxSession session = sessionManager.get(sessionId); - if (null != session) { - // 由于sessionManager不参与序列化,加上序列化的问题,故手动设置 - session.setManager(sessionManager); - } - return session; - } - - /** - * Return a Object from session. - * @param key a String specifying the key of the Object stored in session - */ - @SuppressWarnings("unchecked") - public T getWxSessionAttr(String key) { - WxSession session = this.getWxSession(false); - return session != null ? (T) session.getAttribute(key) : null; - } - - /** - * Store Object to session. - * @param key a String specifying the key of the Object stored in session - * @param value a Object specifying the value stored in session - */ - public Controller setWxSessionAttr(String key, Object value) { - this.getWxSession(true).setAttribute(key, value); - return this; - } - - /** - * Remove Object in session. - * @param key a String specifying the key of the Object stored in session - */ - public Controller removeWxSessionAttr(String key) { - WxSession session = this.getWxSession(false); - if (session != null) - session.removeAttribute(key); - return this; - } } diff --git a/src/main/java/com/jfinal/weixin/sdk/jfinal/component/AuthMessageController.java b/src/main/java/com/jfinal/weixin/sdk/jfinal/component/AuthMessageController.java new file mode 100644 index 0000000..d9df99c --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/jfinal/component/AuthMessageController.java @@ -0,0 +1,96 @@ +/** + * Copyright (c) 2011-2014, James Zhan 詹波 (jfinal@126.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +package com.jfinal.weixin.sdk.jfinal.component; + +import com.jfinal.aop.Before; +import com.jfinal.core.Controller; +import com.jfinal.ext.interceptor.NotAction; +import com.jfinal.kit.HttpKit; +import com.jfinal.kit.StrKit; +import com.jfinal.log.Log; +import com.jfinal.weixin.sdk.api.ApiConfig; +import com.jfinal.weixin.sdk.api.ApiConfigKit; +import com.jfinal.weixin.sdk.kit.MsgEncryptKit; +import com.jfinal.weixin.sdk.msg.ComponentMsgParser; +import com.jfinal.weixin.sdk.msg.component.*; + +/** + * 接收微信服务器消息,自动解析成 InMsg 并分发到相应的处理方法 + */ +public abstract class AuthMessageController extends Controller { + + private static final Log log = Log.getLog(AuthMessageController.class); + private String inMsgXml = null; // 本次请求 xml数据 + private AuthMsg inMsg = null; // 本次请求 xml 解析后的 InMsg 对象 + + public abstract ApiConfig getComponentApiConfig(); + + /** + * weixin 公众号服务器调用唯一入口,即在开发者中心输入的 URL 必须要指向此 action + */ + @Before(AuthMessageInterceptor.class) + public final void index() { + // 开发模式输出微信服务发送过来的 xml 消息 + if (ApiConfigKit.isDevMode()) { + System.out.println("接收消息:"); + System.out.println(getInMsgXml()); + } + + // 解析消息并根据消息类型分发到相应的处理方法 + AuthMsg msg = getAuthMsg(); + if (msg instanceof ComponentVerifyTicketMsg) { + processMessage((ComponentVerifyTicketMsg) msg); + } else if (msg instanceof AuthorizedMsg) { + processMessage((AuthorizedMsg) msg); + } else if (msg instanceof UnAuthorizedMsg) { + processMessage((UnAuthorizedMsg) msg); + } else if (msg instanceof UpdateAuthorizedMsg) { + processMessage((UpdateAuthorizedMsg) msg); + } else { + processMessage((NotDefinedComponentMsg) msg); + } + renderText("success"); + return; + } + + + abstract void processMessage(ComponentVerifyTicketMsg msg); + + abstract void processMessage(AuthorizedMsg msg); + + abstract void processMessage(UnAuthorizedMsg msg); + + abstract void processMessage(UpdateAuthorizedMsg msg); + + abstract void processMessage(NotDefinedComponentMsg msg); + + + @Before(NotAction.class) + public String getInMsgXml() { + if (inMsgXml == null) { + inMsgXml = HttpKit.readData(getRequest()); + inMsgXml = MsgEncryptKit.decryptComponent(inMsgXml, + getPara("timestamp"), + getPara("nonce"), + getPara("msg_signature")); + + } + if (StrKit.isBlank(inMsgXml)) { + throw new RuntimeException( + "请不要在浏览器中请求该连接,调试请查看WIKI:http://git.oschina.net/jfinal/jfinal-weixin/wikis/JFinal-weixin-demo%E5%92%8C%E8%B0%83%E8%AF%95"); + } + return inMsgXml; + } + + @Before(NotAction.class) + public AuthMsg getAuthMsg() { + if (inMsg == null) + inMsg = ComponentMsgParser.parse(getInMsgXml()); + return inMsg; + } + +} diff --git a/src/main/java/com/jfinal/weixin/sdk/jfinal/component/AuthMessageControllerAdapter.java b/src/main/java/com/jfinal/weixin/sdk/jfinal/component/AuthMessageControllerAdapter.java new file mode 100644 index 0000000..c49d8d1 --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/jfinal/component/AuthMessageControllerAdapter.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2011-2014, James Zhan 詹波 (jfinal@126.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +package com.jfinal.weixin.sdk.jfinal.component; + +import com.jfinal.log.Log; +import com.jfinal.weixin.sdk.api.ApiConfigKit; +import com.jfinal.weixin.sdk.api.component.ComponentVerifyTicket; +import com.jfinal.weixin.sdk.cache.IAccessTokenCache; +import com.jfinal.weixin.sdk.msg.component.*; + +/** + * 接收微信服务器消息,自动解析成 InMsg 并分发到相应的处理方法 + */ +public abstract class AuthMessageControllerAdapter extends AuthMessageController { + + private static final Log log = Log.getLog(AuthMessageController.class); + + protected static IAccessTokenCache accessTokenCache = ApiConfigKit.getAccessTokenCache(); + + @Override + protected void processMessage(ComponentVerifyTicketMsg msg) { + log.info("收到ComponentVerifyTicket:" + msg); + ComponentVerifyTicket verifyTicket = new ComponentVerifyTicket(msg.getAppId(), + msg.getCreateTime(), + msg.getComponentVerifyTicket()); + accessTokenCache.setComponentVerifyTicket(msg.getAppId(), verifyTicket); + } + + @Override + protected void processMessage(AuthorizedMsg msg) { + + } + + @Override + protected void processMessage(UnAuthorizedMsg msg) { + + } + + @Override + protected void processMessage(UpdateAuthorizedMsg msg) { + + } + + @Override + protected void processMessage(NotDefinedComponentMsg msg) { + + } +} diff --git a/src/main/java/com/jfinal/weixin/sdk/jfinal/component/AuthMessageInterceptor.java b/src/main/java/com/jfinal/weixin/sdk/jfinal/component/AuthMessageInterceptor.java new file mode 100644 index 0000000..d4d74fe --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/jfinal/component/AuthMessageInterceptor.java @@ -0,0 +1,84 @@ +/** + * Copyright (c) 2011-2014, James Zhan 詹波 (jfinal@126.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +package com.jfinal.weixin.sdk.jfinal.component; + +import com.jfinal.aop.Interceptor; +import com.jfinal.aop.Invocation; +import com.jfinal.core.Controller; +import com.jfinal.kit.StrKit; +import com.jfinal.log.Log; +import com.jfinal.weixin.sdk.api.ApiConfigKit; +import com.jfinal.weixin.sdk.kit.SignatureCheckKit; + +/** + * Msg 拦截器 + * 1:通过 MsgController.getComponentApiConfig() 得到 ApiConfig 对象,并将其绑定到当前线程之上(利用了 ApiConfigKit 中的 ThreadLocal 对象) + * 2:响应开发者中心服务器配置 URL 与 Token 请求 + * 3:签名检测 + * 注意: MsgController 的继承类如果覆盖了 index 方法,则需要对该 index 方法声明该拦截器 + * 因为子类覆盖父类方法会使父类方法配置的拦截器失效,从而失去本拦截器的功能 + */ +public class AuthMessageInterceptor implements Interceptor { + + private static final Log log = Log.getLog(AuthMessageInterceptor.class); + + public void intercept(Invocation inv) { + Controller controller = inv.getController(); + if (controller instanceof AuthMessageController == false) + throw new RuntimeException("控制器需要继承 AuthMessageController"); + + try { + // 将 ApiConfig 对象与当前线程绑定,以便在后续操作中方便获取该对象: ApiConfigKit.getComponentApiConfig(); + ApiConfigKit.setThreadLocalComponentApiConfig(((AuthMessageController) controller).getComponentApiConfig()); + + + // 对开发测试更加友好 + if (ApiConfigKit.isDevMode()) { + inv.invoke(); + } else { + // 签名检测 + if (checkSignature(controller)) { + inv.invoke(); + } else { + controller.renderText("签名验证失败,请确定是微信服务器在发送消息过来"); + } + } + + } finally { + ApiConfigKit.removeThreadLocalComponentApiConfig(); + } + } + + /** + * 检测签名 + */ + private boolean checkSignature(Controller controller) { + String signature = controller.getPara("signature"); + String timestamp = controller.getPara("timestamp"); + String nonce = controller.getPara("nonce"); + if (StrKit.isBlank(signature) || StrKit.isBlank(timestamp) || StrKit.isBlank(nonce)) { + controller.renderText("check signature failure"); + return false; + } + + if (SignatureCheckKit.me.checkSignature(signature, timestamp, nonce)) { + return true; + } else { + log.error("check signature failure: " + + " signature = " + controller.getPara("signature") + + " timestamp = " + controller.getPara("timestamp") + + " nonce = " + controller.getPara("nonce")); + + return false; + } + } + + +} + + + diff --git a/src/main/java/com/jfinal/weixin/sdk/jfinal/component/ComponentAPIController.java b/src/main/java/com/jfinal/weixin/sdk/jfinal/component/ComponentAPIController.java new file mode 100644 index 0000000..0aa98e7 --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/jfinal/component/ComponentAPIController.java @@ -0,0 +1,13 @@ +package com.jfinal.weixin.sdk.jfinal.component; + +import com.jfinal.aop.Before; +import com.jfinal.core.Controller; +import com.jfinal.weixin.sdk.api.ApiConfig; + +/** + * 所有使用 Api 的 controller 需要继承此类 + */ +@Before(ComponentAPIInterceptor.class) +public abstract class ComponentAPIController extends Controller { + public abstract ApiConfig getApiConfig(); +} diff --git a/src/main/java/com/jfinal/weixin/sdk/jfinal/component/ComponentAPIInterceptor.java b/src/main/java/com/jfinal/weixin/sdk/jfinal/component/ComponentAPIInterceptor.java new file mode 100644 index 0000000..72245cd --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/jfinal/component/ComponentAPIInterceptor.java @@ -0,0 +1,27 @@ +package com.jfinal.weixin.sdk.jfinal.component; + +import com.jfinal.aop.Interceptor; +import com.jfinal.aop.Invocation; +import com.jfinal.core.Controller; +import com.jfinal.weixin.sdk.api.ApiConfigKit; + +/** + * ApiController 为 ApiController 绑定 ApiConfig 对象到当前线程, + * 以便在后续的操作中可以使用 ApiConfigKit.getApiConfig() 获取到该对象 + */ +public class ComponentAPIInterceptor implements Interceptor { + + public void intercept(Invocation inv) { + Controller controller = inv.getController(); + if (controller instanceof ComponentAPIController == false) + throw new RuntimeException("控制器需要继承 ApiController"); + + try { + ApiConfigKit.setThreadLocalComponentApiConfig(((ComponentAPIController) controller).getApiConfig()); + inv.invoke(); + } finally { + ApiConfigKit.removeThreadLocalComponentApiConfig(); + } + } +} + diff --git a/src/main/java/com/jfinal/weixin/sdk/kit/MsgEncryptKit.java b/src/main/java/com/jfinal/weixin/sdk/kit/MsgEncryptKit.java index 6987976..7e6a8ac 100644 --- a/src/main/java/com/jfinal/weixin/sdk/kit/MsgEncryptKit.java +++ b/src/main/java/com/jfinal/weixin/sdk/kit/MsgEncryptKit.java @@ -1,15 +1,20 @@ package com.jfinal.weixin.sdk.kit; -import java.io.StringReader; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; +import com.jfinal.weixin.sdk.api.ApiConfig; +import com.jfinal.weixin.sdk.api.ApiConfigKit; +import com.jfinal.weixin.sdk.encrypt.AesException; +import com.jfinal.weixin.sdk.encrypt.WXBizMsgCrypt; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; -import com.jfinal.weixin.sdk.api.ApiConfig; -import com.jfinal.weixin.sdk.api.ApiConfigKit; -import com.jfinal.weixin.sdk.encrypt.WXBizMsgCrypt; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; +import java.io.StringReader; /** * 对微信平台官方给出的加密解析代码进行再次封装 @@ -74,6 +79,69 @@ public class MsgEncryptKit { throw new RuntimeException(e); } } + + private static String decrypt(String encryptedMsg, + String timestamp, + String nonce, + String msgSignature, + ApiConfig ac) throws ParserConfigurationException, SAXException, IOException, AesException { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder db = dbf.newDocumentBuilder(); + StringReader sr = new StringReader(encryptedMsg); + InputSource is = new InputSource(sr); + Document document = db.parse(is); + + Element root = document.getDocumentElement(); + NodeList nodelist1 = root.getElementsByTagName("Encrypt"); + // NodeList nodelist2 = root.getElementsByTagName("MsgSignature"); + + String encrypt = nodelist1.item(0).getTextContent(); + // String msgSignature = nodelist2.item(0).getTextContent(); + + String fromXML = String.format(format, encrypt); + + String encodingAesKey = ac.getEncodingAesKey(); + if (encodingAesKey == null) + throw new IllegalStateException( + "encodingAesKey can not be null, config encodingAesKey first."); + + WXBizMsgCrypt pc = new WXBizMsgCrypt(ac.getToken(), encodingAesKey, ac.getAppId()); + return pc.decryptMsg(msgSignature, + timestamp, + nonce, + fromXML); // 此处 timestamp 如果与加密前的不同则报签名不正确的异常 + } + + private static String encrypt(String msg, + String timestamp, + String nonce, + ApiConfig ac) throws AesException { + WXBizMsgCrypt pc = new WXBizMsgCrypt(ac.getToken(), ac.getEncodingAesKey(), ac.getAppId()); + return pc.encryptMsg(msg, timestamp, nonce); + } + + + public static String decryptComponent(String encryptedMsg, + String timestamp, + String nonce, + String msgSignature) { + try { + ApiConfig ac = ApiConfigKit.getComponentApiConfig(); + + return decrypt(encryptedMsg, timestamp, nonce, msgSignature, ac); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static String encryptComponent(String msg, String timestamp, String nonce) { + try { + ApiConfig ac = ApiConfigKit.getComponentApiConfig(); + return encrypt(msg, timestamp, nonce, ac); + } catch (Exception e) { + throw new RuntimeException(e); + } + } } diff --git a/src/main/java/com/jfinal/weixin/sdk/msg/ComponentMsgParser.java b/src/main/java/com/jfinal/weixin/sdk/msg/ComponentMsgParser.java new file mode 100644 index 0000000..381edad --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/msg/ComponentMsgParser.java @@ -0,0 +1,140 @@ +/** + * Copyright (c) 2011-2014, James Zhan 詹波 (jfinal@126.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +package com.jfinal.weixin.sdk.msg; + +import com.jfinal.kit.LogKit; +import com.jfinal.weixin.sdk.msg.component.*; +import com.jfinal.weixin.sdk.utils.XmlHelper; + +public class ComponentMsgParser { + private ComponentMsgParser() { + } + + /** + * 从 xml 中解析出各类消息与事件 + * + * @param xml xml字符串 + * @return {InMsg} + */ + public static AuthMsg parse(String xml) { + XmlHelper xmlHelper = XmlHelper.of(xml); + return doParse(xmlHelper); + } + + /** + * 消息类型 + * 1:text 文本消息 + * 2:image 图片消息 + * 3:voice 语音消息 + * 4:video 视频消息 + * shortvideo 小视频消息 + * 5:location 地址位置消息 + * 6:link 链接消息 + * 7:event 事件 + */ + private static AuthMsg doParse(XmlHelper xmlHelper) { + + String infoType = xmlHelper.getString("//InfoType"); + String appId = xmlHelper.getString("//AppId"); + Integer createTime = xmlHelper.getNumber("//CreateTime").intValue(); + if ("component_verify_ticket".equals(infoType)) + return parseComponentVerifyTicketMsg(xmlHelper, appId, createTime, infoType); + if ("unauthorized".equals(infoType)) + return parseUnAuthorizedMsg(xmlHelper, appId, createTime, infoType); + if ("authorized".equals(infoType)) + return parseAuthorizedMsg(xmlHelper, appId, createTime, infoType); + if ("updateauthorized".equals(infoType)) + return parseUpdateAuthorizedMsg(xmlHelper, appId, createTime, infoType); + + LogKit.error("无法识别的消息类型 " + infoType + ",请查阅微信公众平台开发文档"); + return parseNotDefinedComponentMsg(appId, createTime, infoType); + } + + private static AuthMsg parseNotDefinedComponentMsg(String appId, + Integer createTime, + String msgType) { + NotDefinedComponentMsg msg = new NotDefinedComponentMsg(appId, createTime, msgType); + return msg; + } + + private static AuthMsg parseComponentVerifyTicketMsg(XmlHelper xmlHelper, + String appId, + Integer createTime, + String infoType) { + final String ticket = xmlHelper.getString("//ComponentVerifyTicket"); + ComponentVerifyTicketMsg msg = new ComponentVerifyTicketMsg(appId, + createTime, + infoType, + ticket); + return msg; + } + + /** + * + * 第三方平台appid + * 1413192760 + * unauthorized + * 公众号appid + * + */ + + private static AuthMsg parseUnAuthorizedMsg(XmlHelper xmlHelper, + String appId, + Integer createTime, + String infoType) { + final String authorizerAppId = xmlHelper.getString("//AuthorizerAppid"); + UnAuthorizedMsg msg = new UnAuthorizedMsg(appId, + createTime, + infoType, + authorizerAppId); + return msg; + } + + /** + * + * 第三方平台appid + * 1413192760 + * authorized + * 公众号appid + * 授权码(code) + * 过期时间 + * + */ + private static AuthMsg parseAuthorizedMsg(XmlHelper xmlHelper, + String appId, + Integer createTime, + String infoType) { + final String authorizerAppId = xmlHelper.getString("//AuthorizerAppid"); + final String authorizationCode = xmlHelper.getString("//AuthorizationCode"); + final String authorizationCodeExpiredTime = xmlHelper.getString( + "//AuthorizationCodeExpiredTime"); + AuthorizedMsg msg = new AuthorizedMsg(appId, + createTime, + infoType, + authorizerAppId, + authorizationCode, + authorizationCodeExpiredTime); + return msg; + } + + private static AuthMsg parseUpdateAuthorizedMsg(XmlHelper xmlHelper, + String appId, + Integer createTime, + String infoType) { + final String authorizerAppId = xmlHelper.getString("//AuthorizerAppid"); + final String authorizationCode = xmlHelper.getString("//AuthorizationCode"); + final String authorizationCodeExpiredTime = xmlHelper.getString( + "//AuthorizationCodeExpiredTime"); + UpdateAuthorizedMsg msg = new UpdateAuthorizedMsg(appId, + createTime, + infoType, + authorizerAppId, + authorizationCode, + authorizationCodeExpiredTime); + return msg; + } +} diff --git a/src/main/java/com/jfinal/weixin/sdk/msg/InMsgParser.java b/src/main/java/com/jfinal/weixin/sdk/msg/InMsgParser.java index ec14043..4fd792a 100644 --- a/src/main/java/com/jfinal/weixin/sdk/msg/InMsgParser.java +++ b/src/main/java/com/jfinal/weixin/sdk/msg/InMsgParser.java @@ -1,6 +1,6 @@ /** * Copyright (c) 2011-2014, James Zhan 詹波 (jfinal@126.com). - * + *

* Licensed under the Apache License, Version 2.0 (the "License"); */ @@ -20,10 +20,12 @@ import java.util.ArrayList; import java.util.List; public class InMsgParser { - private InMsgParser() {} + private InMsgParser() { + } /** * 从 xml 中解析出各类消息与事件 + * * @param xml xml字符串 * @return {InMsg} */ @@ -38,22 +40,30 @@ public class InMsgParser { * 2:image 图片消息 * 3:voice 语音消息 * 4:video 视频消息 - * shortvideo 小视频消息 + * shortvideo 小视频消息 * 5:location 地址位置消息 * 6:link 链接消息 * 7:event 事件 */ private static InMsg doParse(XmlHelper xmlHelper) { - String toUserName = xmlHelper.getString("//ToUserName"); - String fromUserName = xmlHelper.getString("//FromUserName"); - Integer createTime = xmlHelper.getNumber("//CreateTime").intValue(); - String msgType = xmlHelper.getString("//MsgType"); + + String infoType = xmlHelper.getString("//InfoType"); + + + String toUserName = xmlHelper.getString("//ToUserName"); + String fromUserName = xmlHelper.getString("//FromUserName"); + Integer createTime = xmlHelper.getNumber("//CreateTime").intValue(); + String msgType = xmlHelper.getString("//MsgType"); if ("text".equals(msgType)) return parseInTextMsg(xmlHelper, toUserName, fromUserName, createTime, msgType); if ("image".equals(msgType)) return parseInImageMsg(xmlHelper, toUserName, fromUserName, createTime, msgType); if ("voice".equals(msgType)) - return parseInVoiceMsgAndInSpeechRecognitionResults(xmlHelper, toUserName, fromUserName, createTime, msgType); + return parseInVoiceMsgAndInSpeechRecognitionResults(xmlHelper, + toUserName, + fromUserName, + createTime, + msgType); if ("video".equals(msgType)) return parseInVideoMsg(xmlHelper, toUserName, fromUserName, createTime, msgType); if ("shortvideo".equals(msgType)) //支持小视频 @@ -66,23 +76,33 @@ public class InMsgParser { return parseInEvent(xmlHelper, toUserName, fromUserName, createTime, msgType); LogKit.error("无法识别的消息类型 " + msgType + ",请查阅微信公众平台开发文档"); - return parseInNotDefinedMsg(xmlHelper, toUserName, fromUserName, createTime, msgType); + return parseInNotDefinedMsg(toUserName, fromUserName, createTime, msgType); } - private static InMsg parseInNotDefinedMsg(XmlHelper xmlHelper, String toUserName, String fromUserName, Integer createTime, String msgType) { + private static InMsg parseInNotDefinedMsg(String toUserName, + String fromUserName, + Integer createTime, + String msgType) { InNotDefinedMsg msg = new InNotDefinedMsg(toUserName, fromUserName, createTime, msgType); - msg.setXmlHelper(xmlHelper); return msg; } - private static InMsg parseInTextMsg(XmlHelper xmlHelper, String toUserName, String fromUserName, Integer createTime, String msgType) { + private static InMsg parseInTextMsg(XmlHelper xmlHelper, + String toUserName, + String fromUserName, + Integer createTime, + String msgType) { InTextMsg msg = new InTextMsg(toUserName, fromUserName, createTime, msgType); msg.setContent(xmlHelper.getString("//Content")); msg.setMsgId(xmlHelper.getString("//MsgId")); return msg; } - private static InMsg parseInImageMsg(XmlHelper xmlHelper, String toUserName, String fromUserName, Integer createTime, String msgType) { + private static InMsg parseInImageMsg(XmlHelper xmlHelper, + String toUserName, + String fromUserName, + Integer createTime, + String msgType) { InImageMsg msg = new InImageMsg(toUserName, fromUserName, createTime, msgType); msg.setPicUrl(xmlHelper.getString("//PicUrl")); msg.setMediaId(xmlHelper.getString("//MediaId")); @@ -90,7 +110,11 @@ public class InMsgParser { return msg; } - private static InMsg parseInVoiceMsgAndInSpeechRecognitionResults(XmlHelper xmlHelper, String toUserName, String fromUserName, Integer createTime, String msgType) { + private static InMsg parseInVoiceMsgAndInSpeechRecognitionResults(XmlHelper xmlHelper, + String toUserName, + String fromUserName, + Integer createTime, + String msgType) { String recognition = xmlHelper.getString("//Recognition"); String mediaId = xmlHelper.getString("//MediaId"); String format = xmlHelper.getString("//Format"); @@ -102,7 +126,10 @@ public class InMsgParser { msg.setMsgId(msgId); return msg; } else { - InSpeechRecognitionResults msg = new InSpeechRecognitionResults(toUserName, fromUserName, createTime, msgType); + InSpeechRecognitionResults msg = new InSpeechRecognitionResults(toUserName, + fromUserName, + createTime, + msgType); msg.setMediaId(mediaId); msg.setFormat(format); msg.setMsgId(msgId); @@ -112,7 +139,11 @@ public class InMsgParser { } } - private static InMsg parseInVideoMsg(XmlHelper xmlHelper, String toUserName, String fromUserName, Integer createTime, String msgType) { + private static InMsg parseInVideoMsg(XmlHelper xmlHelper, + String toUserName, + String fromUserName, + Integer createTime, + String msgType) { InVideoMsg msg = new InVideoMsg(toUserName, fromUserName, createTime, msgType); msg.setMediaId(xmlHelper.getString("//MediaId")); msg.setThumbMediaId(xmlHelper.getString("//ThumbMediaId")); @@ -120,7 +151,11 @@ public class InMsgParser { return msg; } - private static InMsg parseInShortVideoMsg(XmlHelper xmlHelper, String toUserName, String fromUserName, Integer createTime, String msgType) { + private static InMsg parseInShortVideoMsg(XmlHelper xmlHelper, + String toUserName, + String fromUserName, + Integer createTime, + String msgType) { InShortVideoMsg msg = new InShortVideoMsg(toUserName, fromUserName, createTime, msgType); msg.setMediaId(xmlHelper.getString("//MediaId")); msg.setThumbMediaId(xmlHelper.getString("//ThumbMediaId")); @@ -128,7 +163,11 @@ public class InMsgParser { return msg; } - private static InMsg parseInLocationMsg(XmlHelper xmlHelper, String toUserName, String fromUserName, Integer createTime, String msgType) { + private static InMsg parseInLocationMsg(XmlHelper xmlHelper, + String toUserName, + String fromUserName, + Integer createTime, + String msgType) { InLocationMsg msg = new InLocationMsg(toUserName, fromUserName, createTime, msgType); msg.setLocation_X(xmlHelper.getString("//Location_X")); msg.setLocation_Y(xmlHelper.getString("//Location_Y")); @@ -138,7 +177,11 @@ public class InMsgParser { return msg; } - private static InMsg parseInLinkMsg(XmlHelper xmlHelper, String toUserName, String fromUserName, Integer createTime, String msgType) { + private static InMsg parseInLinkMsg(XmlHelper xmlHelper, + String toUserName, + String fromUserName, + Integer createTime, + String msgType) { InLinkMsg msg = new InLinkMsg(toUserName, fromUserName, createTime, msgType); msg.setTitle(xmlHelper.getString("//Title")); msg.setDescription(xmlHelper.getString("//Description")); @@ -148,8 +191,12 @@ public class InMsgParser { } // 解析各种事件 - private static InMsg parseInEvent(XmlHelper xmlHelper, String toUserName, String fromUserName, Integer createTime, String msgType) { - String event = xmlHelper.getString("//Event"); + private static InMsg parseInEvent(XmlHelper xmlHelper, + String toUserName, + String fromUserName, + Integer createTime, + String msgType) { + String event = xmlHelper.getString("//Event"); String eventKey = xmlHelper.getString("//EventKey"); /** @@ -163,17 +210,25 @@ public class InMsgParser { return new InFollowEvent(toUserName, fromUserName, createTime, msgType, event); } - // 扫描带参数二维码事件之一 1: 用户未关注时,进行关注后的事件推送 + // 扫描带参数二维码事件之一 1: 用户未关注时,进行关注后的事件推送 String ticket = xmlHelper.getString("//Ticket"); if ("subscribe".equals(event) && StrKit.notBlank(eventKey) && eventKey.startsWith("qrscene_")) { - InQrCodeEvent e = new InQrCodeEvent(toUserName, fromUserName, createTime, msgType, event); + InQrCodeEvent e = new InQrCodeEvent(toUserName, + fromUserName, + createTime, + msgType, + event); e.setEventKey(eventKey); e.setTicket(ticket); return e; } - // 扫描带参数二维码事件之二 2: 用户已关注时的事件推送 + // 扫描带参数二维码事件之二 2: 用户已关注时的事件推送 if ("SCAN".equals(event)) { - InQrCodeEvent e = new InQrCodeEvent(toUserName, fromUserName, createTime, msgType, event); + InQrCodeEvent e = new InQrCodeEvent(toUserName, + fromUserName, + createTime, + msgType, + event); e.setEventKey(eventKey); e.setTicket(ticket); return e; @@ -191,19 +246,23 @@ public class InMsgParser { // 上报地理位置事件 if ("LOCATION".equals(event)) { - InLocationEvent e = new InLocationEvent(toUserName, fromUserName, createTime, msgType, event); + InLocationEvent e = new InLocationEvent(toUserName, + fromUserName, + createTime, + msgType, + event); e.setLatitude(xmlHelper.getString("//Latitude")); e.setLongitude(xmlHelper.getString("//Longitude")); e.setPrecision(xmlHelper.getString("//Precision")); return e; } - // 自定义菜单事件之一 1:点击菜单拉取消息时的事件推送 + // 自定义菜单事件之一 1:点击菜单拉取消息时的事件推送 if ("CLICK".equals(event)) { InMenuEvent e = new InMenuEvent(toUserName, fromUserName, createTime, msgType, event); e.setEventKey(eventKey); return e; } - // 自定义菜单事件之二 2:点击菜单跳转链接时的事件推送 + // 自定义菜单事件之二 2:点击菜单跳转链接时的事件推送 if ("VIEW".equals(event)) { InMenuEvent e = new InMenuEvent(toUserName, fromUserName, createTime, msgType, event); e.setEventKey(eventKey); @@ -213,7 +272,7 @@ public class InMsgParser { if ("scancode_push".equals(event) || "scancode_waitmsg".equals(event)) { InMenuEvent e = new InMenuEvent(toUserName, fromUserName, createTime, msgType, event); e.setEventKey(eventKey); - String scanType = xmlHelper.getString("//ScanCodeInfo/ScanType"); + String scanType = xmlHelper.getString("//ScanCodeInfo/ScanType"); String scanResult = xmlHelper.getString("//ScanCodeInfo/ScanResult"); e.setScanCodeInfo(new ScanCodeInfo(scanType, scanResult)); return e; @@ -256,7 +315,11 @@ public class InMsgParser { } // 模板消息是否送达成功通知事件 if ("TEMPLATESENDJOBFINISH".equals(event)) { - InTemplateMsgEvent e = new InTemplateMsgEvent(toUserName, fromUserName, createTime, msgType, event); + InTemplateMsgEvent e = new InTemplateMsgEvent(toUserName, + fromUserName, + createTime, + msgType, + event); e.setMsgId(xmlHelper.getString("//MsgID")); e.setStatus(xmlHelper.getString("//Status")); return e; @@ -274,26 +337,42 @@ public class InMsgParser { } // 多客服接入会话事件 if ("kf_create_session".equals(event)) { - InCustomEvent e = new InCustomEvent(toUserName, fromUserName, createTime, msgType, event); + InCustomEvent e = new InCustomEvent(toUserName, + fromUserName, + createTime, + msgType, + event); e.setKfAccount(xmlHelper.getString("//KfAccount")); return e; } // 多客服关闭会话事件 if ("kf_close_session".equals(event)) { - InCustomEvent e = new InCustomEvent(toUserName, fromUserName, createTime, msgType, event); + InCustomEvent e = new InCustomEvent(toUserName, + fromUserName, + createTime, + msgType, + event); e.setKfAccount(xmlHelper.getString("//KfAccount")); return e; } // 多客服转接会话事件 if ("kf_switch_session".equals(event)) { - InCustomEvent e = new InCustomEvent(toUserName, fromUserName, createTime, msgType, event); + InCustomEvent e = new InCustomEvent(toUserName, + fromUserName, + createTime, + msgType, + event); e.setKfAccount(xmlHelper.getString("//KfAccount")); e.setToKfAccount(xmlHelper.getString("//ToKfAccount")); return e; } // 微信摇一摇事件 - if ("ShakearoundUserShake".equals(event)){ - InShakearoundUserShakeEvent e = new InShakearoundUserShakeEvent(toUserName, fromUserName, createTime, msgType); + if ("ShakearoundUserShake".equals(event)) { + InShakearoundUserShakeEvent e = new InShakearoundUserShakeEvent(toUserName, + fromUserName, + createTime, + msgType, + event); e.setEvent(event); e.setUuid(xmlHelper.getString("//ChosenBeacon/Uuid")); e.setMajor(xmlHelper.getNumber("//ChosenBeacon/Major").intValue()); @@ -302,7 +381,7 @@ public class InMsgParser { NodeList nodeList = xmlHelper.getNodeList("//AroundBeacons/AroundBeacon"); if (nodeList != null && nodeList.getLength() > 0) { - AroundBeacon aroundBeacon = null; + AroundBeacon aroundBeacon = null; List aroundBeacons = new ArrayList(); for (int i = 0; i < nodeList.getLength(); i++) { Node node = nodeList.item(i); @@ -317,25 +396,37 @@ public class InMsgParser { e.setAroundBeaconList(aroundBeacons); } return e; - } + } // 资质认证成功 || 名称认证成功 || 年审通知 || 认证过期失效通知 if ("qualification_verify_success".equals(event) || "naming_verify_success".equals(event) - || "annual_renew".equals(event) || "verify_expired".equals(event)) { - InVerifySuccessEvent e = new InVerifySuccessEvent(toUserName, fromUserName, createTime, msgType, event); + || "annual_renew".equals(event) || "verify_expired".equals(event)) { + InVerifySuccessEvent e = new InVerifySuccessEvent(toUserName, + fromUserName, + createTime, + msgType, + event); e.setExpiredTime(xmlHelper.getString("//expiredTime")); return e; } // 资质认证失败 || 名称认证失败 if ("qualification_verify_fail".equals(event) || "naming_verify_fail".equals(event)) { - InVerifyFailEvent e = new InVerifyFailEvent(toUserName, fromUserName, createTime, msgType, event); + InVerifyFailEvent e = new InVerifyFailEvent(toUserName, + fromUserName, + createTime, + msgType, + event); e.setFailTime(xmlHelper.getString("//failTime")); e.setFailReason(xmlHelper.getString("//failReason")); return e; } // 门店在审核事件消息 , update by unas at 2016-1-29,add event param if ("poi_check_notify".equals(event)) { - InPoiCheckNotifyEvent e = new InPoiCheckNotifyEvent(toUserName, fromUserName, createTime, msgType, event); + InPoiCheckNotifyEvent e = new InPoiCheckNotifyEvent(toUserName, + fromUserName, + createTime, + msgType, + event); e.setUniqId(xmlHelper.getString("//UniqId")); e.setPoiId(xmlHelper.getString("//PoiId")); e.setResult(xmlHelper.getString("//Result")); @@ -353,19 +444,31 @@ public class InMsgParser { return e; } if (InUserViewCardEvent.EVENT.equals(event)) { - InUserViewCardEvent e = new InUserViewCardEvent(toUserName, fromUserName, createTime, msgType, event); + InUserViewCardEvent e = new InUserViewCardEvent(toUserName, + fromUserName, + createTime, + msgType, + event); e.setCardId(xmlHelper.getString("//CardId")); e.setUserCardCode(xmlHelper.getString("//UserCardCode")); return e; } if (InSubmitMemberCardEvent.EVENT.equals(event)) { - InSubmitMemberCardEvent e = new InSubmitMemberCardEvent(toUserName, fromUserName, createTime, msgType, event); + InSubmitMemberCardEvent e = new InSubmitMemberCardEvent(toUserName, + fromUserName, + createTime, + msgType, + event); e.setCardId(xmlHelper.getString("//CardId")); e.setUserCardCode(xmlHelper.getString("//UserCardCode")); return e; } if (InUpdateMemberCardEvent.EVENT.equals(event)) { - InUpdateMemberCardEvent e = new InUpdateMemberCardEvent(toUserName, fromUserName, createTime, msgType, event); + InUpdateMemberCardEvent e = new InUpdateMemberCardEvent(toUserName, + fromUserName, + createTime, + msgType, + event); e.setCardId(xmlHelper.getString("//CardId")); e.setUserCardCode(xmlHelper.getString("//UserCardCode")); e.setModifyBonus(xmlHelper.getString("//ModifyBonus")); @@ -373,7 +476,11 @@ public class InMsgParser { return e; } if (InUserPayFromCardEvent.EVENT.equals(event)) { - InUserPayFromCardEvent e = new InUserPayFromCardEvent(toUserName, fromUserName, createTime, msgType, event); + InUserPayFromCardEvent e = new InUserPayFromCardEvent(toUserName, + fromUserName, + createTime, + msgType, + event); e.setCardId(xmlHelper.getString("//CardId")); e.setUserCardCode(xmlHelper.getString("//UserCardCode")); e.setLocationId(xmlHelper.getString("//LocationId")); @@ -384,7 +491,11 @@ public class InMsgParser { } // 微信小店支付消息 if (InMerChantOrderEvent.EVENT.equals(event)) { - InMerChantOrderEvent e = new InMerChantOrderEvent(toUserName, fromUserName, createTime, msgType, event); + InMerChantOrderEvent e = new InMerChantOrderEvent(toUserName, + fromUserName, + createTime, + msgType, + event); e.setOrderId(xmlHelper.getString("//OrderId")); e.setOrderStatus(xmlHelper.getNumber("//OrderStatus").intValue()); e.setProductId(xmlHelper.getString("//ProductId")); @@ -393,8 +504,11 @@ public class InMsgParser { } LogKit.error("无法识别的事件类型" + event + ",请查阅微信公众平台开发文档"); - InNotDefinedEvent e = new InNotDefinedEvent(toUserName, fromUserName, createTime, msgType, event); - e.setXmlHelper(xmlHelper); + InNotDefinedEvent e = new InNotDefinedEvent(toUserName, + fromUserName, + createTime, + msgType, + event); return e; } diff --git a/src/main/java/com/jfinal/weixin/sdk/msg/component/AuthMsg.java b/src/main/java/com/jfinal/weixin/sdk/msg/component/AuthMsg.java new file mode 100644 index 0000000..b5bed6e --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/msg/component/AuthMsg.java @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2011-2014, James Zhan 詹波 (jfinal@126.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +package com.jfinal.weixin.sdk.msg.component; + +/** + *

+ * 接收消息,以下是接收文本消息的例子
+ * 接收文本消息
+ * <xml>
+ * <ToUserName><![CDATA[toUser]]></ToUserName>
+ * <FromUserName><![CDATA[fromUser]]></FromUserName>
+ * <CreateTime>1348831860</CreateTime>
+ * <MsgType><![CDATA[text]]></MsgType>
+ * <Content><![CDATA[this is a test]]></Content>
+ * <MsgId>1234567890123456</MsgId>
+ * </xml>
+ * 
+ */ +public abstract class AuthMsg { + + // 开发者微信号 + protected String appId; + + // 消息创建时间 (整型) + protected Integer createTime; + + /** + * 消息类型 + * 1:text 文本消息 + * 2:image 图片消息 + * 3:voice 语音消息 + * 4:video 视频消息 + * 5:location 地址位置消息 + * 6:link 链接消息 + * 7:event 事件 + */ + protected String infoType; + + public AuthMsg(String appId, Integer createTime, String infoType) { + this.appId = appId; + this.createTime = createTime; + this.infoType = infoType; + } + + public String getAppId() { + return appId; + } + + public Integer getCreateTime() { + return createTime; + } + + public String getInfoType() { + return infoType; + } + + @Override + public String toString() { + return "AuthMsg{" + + "appId='" + appId + '\'' + + ", createTime=" + createTime + + ", infoType='" + infoType + '\'' + + '}'; + } +} + + + + + + + diff --git a/src/main/java/com/jfinal/weixin/sdk/msg/component/AuthorizedMsg.java b/src/main/java/com/jfinal/weixin/sdk/msg/component/AuthorizedMsg.java new file mode 100644 index 0000000..ac05c0e --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/msg/component/AuthorizedMsg.java @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2011-2014, James Zhan 詹波 (jfinal@126.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +package com.jfinal.weixin.sdk.msg.component; + +/** + *

+ * 接收文本消息
+ * 
+ *  
+ * 1413192605 
+ *  
+ *  
+ * 
+ * 
+ */ +public class AuthorizedMsg extends AuthMsg { + /** + * AppId 第三方平台appid + * CreateTime 时间戳 + * InfoType component_verify_ticket + * ComponentVerifyTicket Ticket内容 + */ + + private String authorizerAppId; + private String authorizationCode; + private String authorizationCodeExpiredTime; + + public AuthorizedMsg(String appId, + Integer createTime, + String infoType, + String authorizerAppId, + String authorizationCode, + String authorizationCodeExpiredTime) { + super(appId, createTime, infoType); + this.authorizerAppId = authorizerAppId; + this.authorizationCode = authorizationCode; + this.authorizationCodeExpiredTime = authorizationCodeExpiredTime; + } + + public String getAuthorizerAppId() { + return authorizerAppId; + } + + public String getAuthorizationCode() { + return authorizationCode; + } + + public String getAuthorizationCodeExpiredTime() { + return authorizationCodeExpiredTime; + } + + @Override + public String toString() { + return "AuthorizedMsg{" + + "authorizerAppId='" + authorizerAppId + '\'' + + ", authorizationCode='" + authorizationCode + '\'' + + ", authorizationCodeExpiredTime='" + authorizationCodeExpiredTime + '\'' + + "} " + super.toString(); + } +} diff --git a/src/main/java/com/jfinal/weixin/sdk/msg/component/ComponentVerifyTicketMsg.java b/src/main/java/com/jfinal/weixin/sdk/msg/component/ComponentVerifyTicketMsg.java new file mode 100644 index 0000000..569026c --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/msg/component/ComponentVerifyTicketMsg.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2011-2014, James Zhan 詹波 (jfinal@126.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +package com.jfinal.weixin.sdk.msg.component; + +/** + *

+ * 接收文本消息
+ * 
+ *  
+ * 1413192605 
+ *  
+ *  
+ * 
+ * 
+ */ +public class ComponentVerifyTicketMsg extends AuthMsg { + /** + * AppId 第三方平台appid + * CreateTime 时间戳 + * InfoType component_verify_ticket + * ComponentVerifyTicket Ticket内容 + */ + + private String componentVerifyTicket; + + public ComponentVerifyTicketMsg(String appId, + Integer createTime, + String infoType, + String componentVerifyTicket) { + super(appId, createTime, infoType); + this.componentVerifyTicket = componentVerifyTicket; + } + + public String getComponentVerifyTicket() { + return componentVerifyTicket; + } + + + @Override + public String toString() { + return "ComponentVerifyTicketMsg{" + + "componentVerifyTicket='" + componentVerifyTicket + '\'' + + "} " + super.toString(); + } +} + + + + diff --git a/src/main/java/com/jfinal/weixin/sdk/msg/component/NotDefinedComponentMsg.java b/src/main/java/com/jfinal/weixin/sdk/msg/component/NotDefinedComponentMsg.java new file mode 100644 index 0000000..d02d1d5 --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/msg/component/NotDefinedComponentMsg.java @@ -0,0 +1,11 @@ +package com.jfinal.weixin.sdk.msg.component; + +/** + * 没有找到对应的消息类型 + */ +public class NotDefinedComponentMsg extends AuthMsg { + + public NotDefinedComponentMsg(String appId, Integer createTime, String infoType) { + super(appId, createTime, infoType); + } +} diff --git a/src/main/java/com/jfinal/weixin/sdk/msg/component/UnAuthorizedMsg.java b/src/main/java/com/jfinal/weixin/sdk/msg/component/UnAuthorizedMsg.java new file mode 100644 index 0000000..efbbc17 --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/msg/component/UnAuthorizedMsg.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2011-2014, James Zhan 詹波 (jfinal@126.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +package com.jfinal.weixin.sdk.msg.component; + +/** + *

+ * 接收文本消息
+ * 
+ * 第三方平台appid
+ * 1413192760
+ * authorized
+ * 公众号appid
+ * 授权码(code)
+ * 过期时间
+ * 
+ * 
+ */ +public class UnAuthorizedMsg extends AuthMsg { + /** + * AppId 第三方平台appid + * CreateTime 时间戳 + * InfoType component_verify_ticket + * ComponentVerifyTicket Ticket内容 + */ + + private String authorizerAppId; + + + public UnAuthorizedMsg(String appId, + Integer createTime, + String infoType, + String authorizerAppId) { + super(appId, createTime, infoType); + this.authorizerAppId = authorizerAppId; + } + + public String getAuthorizerAppId() { + return authorizerAppId; + } + + @Override + public String toString() { + return "UnAuthorizedMsg{" + + "authorizerAppId='" + authorizerAppId + '\'' + + "} " + super.toString(); + } +} + + + + diff --git a/src/main/java/com/jfinal/weixin/sdk/msg/component/UpdateAuthorizedMsg.java b/src/main/java/com/jfinal/weixin/sdk/msg/component/UpdateAuthorizedMsg.java new file mode 100644 index 0000000..28a538b --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/msg/component/UpdateAuthorizedMsg.java @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2011-2014, James Zhan 詹波 (jfinal@126.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +package com.jfinal.weixin.sdk.msg.component; + +/** + *

+ * 接收文本消息
+ * 
+ * 第三方平台appid
+ * 1413192760
+ * updateauthorized
+ * 公众号appid
+ * 授权码(code)
+ * 过期时间
+ * 
+ * 
+ */ +public class UpdateAuthorizedMsg extends AuthMsg { + /** + * AppId 第三方平台appid + * CreateTime 时间戳 + * InfoType component_verify_ticket + * ComponentVerifyTicket Ticket内容 + */ + + private String authorizerAppId; + private String authorizationCode; + private String authorizationCodeExpiredTime; + + public UpdateAuthorizedMsg(String appId, + Integer createTime, + String infoType, + String authorizerAppId, + String authorizationCode, + String authorizationCodeExpiredTime) { + super(appId, createTime, infoType); + this.authorizerAppId = authorizerAppId; + this.authorizationCode = authorizationCode; + this.authorizationCodeExpiredTime = authorizationCodeExpiredTime; + } + + public String getAuthorizerAppId() { + return authorizerAppId; + } + + public String getAuthorizationCode() { + return authorizationCode; + } + + public String getAuthorizationCodeExpiredTime() { + return authorizationCodeExpiredTime; + } + + @Override + public String toString() { + return "AuthorizedMsg{" + + "authorizerAppId='" + authorizerAppId + '\'' + + ", authorizationCode='" + authorizationCode + '\'' + + ", authorizationCodeExpiredTime='" + authorizationCodeExpiredTime + '\'' + + "} " + super.toString(); + } +} + + + + diff --git a/src/main/java/com/jfinal/weixin/sdk/msg/in/event/InShakearoundUserShakeEvent.java b/src/main/java/com/jfinal/weixin/sdk/msg/in/event/InShakearoundUserShakeEvent.java index d481a79..3a5ca5d 100644 --- a/src/main/java/com/jfinal/weixin/sdk/msg/in/event/InShakearoundUserShakeEvent.java +++ b/src/main/java/com/jfinal/weixin/sdk/msg/in/event/InShakearoundUserShakeEvent.java @@ -1,60 +1,60 @@ package com.jfinal.weixin.sdk.msg.in.event; -import com.jfinal.weixin.sdk.msg.in.InMsg; - import java.util.ArrayList; import java.util.List; /** - * * 来自:http://my.oschina.net/u/1993676/blog/490124 - * + *

* 用户进入摇一摇界面,在“周边”页卡下摇一摇时, * 微信会把这个事件推送到开发者填写的URL(登录公众平台进入开发者中心设置)。 * 推送内容包含摇一摇时“周边”页卡展示出来的页面所对应的设备信息, * 以及附近最多五个属于该公众账号的设备的信息。 -

- <xml>
- <ToUserName><![CDATA[toUser]]></ToUserName>
- <FromUserName><![CDATA[fromUser]]></FromUserName>
- <CreateTime>1433332012</CreateTime>
- <MsgType><![CDATA[event]]></MsgType>
- <Event><![CDATA[ShakearoundUserShake]]></Event>
- <ChosenBeacon>
- <Uuid><![CDATA[uuid]]></Uuid>
- <Major>major</Major>
- <Minor>minor</Minor>
- <Distance>0.057</Distance>
- </ChosenBeacon>
- <AroundBeacons>
- <AroundBeacon>
- <Uuid><![CDATA[uuid]]></Uuid>
- <Major>major</Major>
- <Minor>minor</Minor>
- <Distance>166.816</Distance>
- </AroundBeacon>
- <AroundBeacon>
- <Uuid><![CDATA[uuid]]></Uuid>
- <Major>major</Major>
- <Minor>minor</Minor>
- <Distance>15.013</Distance>
- </AroundBeacon>
- </AroundBeacons>
- </xml>
- 
-*/ -public class InShakearoundUserShakeEvent extends InMsg { - - private String event;//事件 - private String uuid; + *
+ * <xml>
+ * <ToUserName><![CDATA[toUser]]></ToUserName>
+ * <FromUserName><![CDATA[fromUser]]></FromUserName>
+ * <CreateTime>1433332012</CreateTime>
+ * <MsgType><![CDATA[event]]></MsgType>
+ * <Event><![CDATA[ShakearoundUserShake]]></Event>
+ * <ChosenBeacon>
+ * <Uuid><![CDATA[uuid]]></Uuid>
+ * <Major>major</Major>
+ * <Minor>minor</Minor>
+ * <Distance>0.057</Distance>
+ * </ChosenBeacon>
+ * <AroundBeacons>
+ * <AroundBeacon>
+ * <Uuid><![CDATA[uuid]]></Uuid>
+ * <Major>major</Major>
+ * <Minor>minor</Minor>
+ * <Distance>166.816</Distance>
+ * </AroundBeacon>
+ * <AroundBeacon>
+ * <Uuid><![CDATA[uuid]]></Uuid>
+ * <Major>major</Major>
+ * <Minor>minor</Minor>
+ * <Distance>15.013</Distance>
+ * </AroundBeacon>
+ * </AroundBeacons>
+ * </xml>
+ * 
+ */ +public class InShakearoundUserShakeEvent extends EventInMsg { + + private String uuid; private Integer major; private Integer minor; - private Float distance;//设备与用户的距离(浮点数;单位:米) + private Float distance;//设备与用户的距离(浮点数;单位:米) private List aroundBeaconList = new ArrayList(); - public InShakearoundUserShakeEvent(String toUserName, String fromUserName, Integer createTime, String msgType) { - super(toUserName, fromUserName, createTime, msgType); + public InShakearoundUserShakeEvent(String toUserName, + String fromUserName, + Integer createTime, + String msgType, + String event) { + super(toUserName, fromUserName, createTime, msgType, event); } public String getEvent() { @@ -106,10 +106,10 @@ public class InShakearoundUserShakeEvent extends InMsg { } public static class AroundBeacon { - private String uuid; + private String uuid; private Integer major; private Integer minor; - private Float distance;//设备与用户的距离(浮点数;单位:米) + private Float distance;//设备与用户的距离(浮点数;单位:米) public String getUuid() { return uuid; diff --git a/src/main/java/com/jfinal/weixin/sdk/utils/ComponentWeixinUtil.java b/src/main/java/com/jfinal/weixin/sdk/utils/ComponentWeixinUtil.java new file mode 100755 index 0000000..a70c470 --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/utils/ComponentWeixinUtil.java @@ -0,0 +1,61 @@ +package com.jfinal.weixin.sdk.utils; + +import com.jfinal.kit.PropKit; +import com.jfinal.weixin.sdk.api.ApiConfig; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; + +/** + * @author Javen + * @Email javenlife@126.com + * 公众平台通用接口工具类 + */ +public class ComponentWeixinUtil { + /** + * 获取配置 + */ + public static ApiConfig getApiConfig() { + ApiConfig ac = new ApiConfig(); + + // 配置微信 API 相关常量 + ac.setToken(PropKit.get("component_token")); + ac.setAppId(PropKit.get("component_app_id")); + ac.setAppSecret(PropKit.get("component_app_secret")); + + /** + * 是否对消息进行加密,对应于微信平台的消息加解密方式: + * 1:true进行加密且必须配置 encodingAesKey + * 2:false采用明文模式,同时也支持混合模式 + */ + ac.setEncryptMessage(true); + ac.setEncodingAesKey(PropKit.get("component_encoding_aes_key", + "setting it in config file")); + return ac; + } + + /** + * emoji表情转换(hex -> utf-16) + * + * @param hexEmoji + * @return + */ + public static String emoji(int hexEmoji) { + return String.valueOf(Character.toChars(hexEmoji)); + } + + /** + * UTF-8编码 + * + * @param source + * @return + */ + public static String urlEncodeUTF8(String source) { + try { + return URLEncoder.encode(source, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/com/jfinal/weixin/sdk/utils/WeixinUtil.java b/src/main/java/com/jfinal/weixin/sdk/utils/WeixinUtil.java new file mode 100755 index 0000000..62d4ffa --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/utils/WeixinUtil.java @@ -0,0 +1,60 @@ +package com.jfinal.weixin.sdk.utils; + +import com.jfinal.kit.PropKit; +import com.jfinal.weixin.sdk.api.ApiConfig; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; + +/** + * @author Javen + * @Email javenlife@126.com + * 公众平台通用接口工具类 + */ +public class WeixinUtil { + /** + * 获取配置 + */ + public static ApiConfig getApiConfig() { + ApiConfig ac = new ApiConfig(); + + // 配置微信 API 相关常量 + ac.setToken(PropKit.get("token")); + ac.setAppId(PropKit.get("appId")); + ac.setAppSecret(PropKit.get("appSecret")); + + /** + * 是否对消息进行加密,对应于微信平台的消息加解密方式: + * 1:true进行加密且必须配置 encodingAesKey + * 2:false采用明文模式,同时也支持混合模式 + */ + ac.setEncryptMessage(PropKit.getBoolean("encryptMessage", false)); + ac.setEncodingAesKey(PropKit.get("encodingAesKey", "setting it in config file")); + return ac; + } + + /** + * emoji表情转换(hex -> utf-16) + * + * @param hexEmoji + * @return + */ + public static String emoji(int hexEmoji) { + return String.valueOf(Character.toChars(hexEmoji)); + } + + /** + * UTF-8编码 + * + * @param source + * @return + */ + public static String urlEncodeUTF8(String source) { + try { + return URLEncoder.encode(source, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + +} -- Gitee