From 2ad3ddcfc449ea173f6de183b0fdc678dd2146bb Mon Sep 17 00:00:00 2001 From: zxd Date: Thu, 2 Mar 2017 17:00:05 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E3=80=8C=E5=85=AC=E4=BC=97?= =?UTF-8?q?=E5=8F=B7=E7=AC=AC=E4=B8=89=E6=96=B9=E5=B9=B3=E5=8F=B0=E3=80=8D?= =?UTF-8?q?API=E3=80=82=E5=A2=9E=E5=8A=A0=20demo=20Controller=20=E6=BC=94?= =?UTF-8?q?=E7=A4=BA=E5=A6=82=E4=BD=95=E4=BD=BF=E7=94=A8=E3=80=82=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=81=E4=BF=AE=E6=AD=A3=E6=B3=A8=E9=87=8A=E4=BD=BF?= =?UTF-8?q?=E4=B9=8B=E6=98=93=E8=AF=BB=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 7 + pom.xml | 2 +- .../demo/WeixinAuthMessageController.java | 21 +++ .../demo/WeixinComponentAuthController.java | 62 ++++++++ .../demo/WeixinComponentSnsController.java | 57 ++++++++ .../com/jfinal/weixin/demo/WeixinConfig.java | 51 +++++-- .../weixin/demo/WeixinMsgController.java | 23 ++- .../jfinal/weixin/sdk/api/AccessTokenApi.java | 11 +- .../com/jfinal/weixin/sdk/api/ApiConfig.java | 1 + .../jfinal/weixin/sdk/api/ApiConfigKit.java | 43 +++++- .../com/jfinal/weixin/sdk/api/ApiResult.java | 13 +- .../jfinal/weixin/sdk/api/TemplateMsgApi.java | 3 +- .../sdk/api/component/ComponentAuthApi.java | 63 +++++--- .../ComponentAuthorizerAccessToken.java | 24 ++++ .../component/ComponentSnsAccessToken.java | 135 ++++++++++++++++++ .../component/ComponentSnsAccessTokenApi.java | 127 ++++++++++++++++ .../weixin/sdk/jfinal/MsgInterceptor.java | 23 ++- .../component/AuthMessageController.java | 9 +- .../AuthMessageControllerAdapter.java | 7 +- .../component/ComponentAPIInterceptor.java | 6 +- .../weixin/sdk/msg/component/AuthMsg.java | 22 +-- .../sdk/msg/component/AuthorizedMsg.java | 10 +- .../component/ComponentVerifyTicketMsg.java | 8 +- .../sdk/msg/component/UnAuthorizedMsg.java | 6 +- .../msg/component/UpdateAuthorizedMsg.java | 2 +- src/main/resources/a_little_config.txt | 14 +- .../com.jfinal.weixin.sdk.api.component | 1 + .../_front/common/location_replace.html | 13 ++ 28 files changed, 675 insertions(+), 89 deletions(-) create mode 100644 src/main/java/com/jfinal/weixin/demo/WeixinAuthMessageController.java create mode 100644 src/main/java/com/jfinal/weixin/demo/WeixinComponentAuthController.java create mode 100644 src/main/java/com/jfinal/weixin/demo/WeixinComponentSnsController.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/api/component/ComponentSnsAccessToken.java create mode 100644 src/main/java/com/jfinal/weixin/sdk/api/component/ComponentSnsAccessTokenApi.java create mode 100644 src/main/webapp/WEB-INF/services/com.jfinal.weixin.sdk.api.component create mode 100644 src/main/webapp/_front/common/location_replace.html diff --git a/README.md b/README.md index 5ffa7a0..f1fcfaa 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,13 @@ # Jfinal Weixin 极速开发 JFinal Weixin 是基于 JFinal 的微信公众号极速开发 SDK,只需浏览 Demo 代码即可进行极速开发,自 JFinal Weixin 1.2 版本开始已添加对多公众号支持。 +新增[公众号第三方平台](https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419318292&token=&lang=zh_CN)支持,在**公众号第三方平台**配置如下信息和其他必要信息即可使用。 + +登录授权的发起页域名:`xx域名.com` +授权事件接收URL:`http://xx域名.com/open/authmsg` +公众号消息与事件接收URL:`http://xx域名.com/open/msg/$APPID$` +网页开发域名:`xx域名.com` + ## 1、WeixinConfig配置 `详情请见`:[JFinal weixin中的WeixinConfig配置](http://git.oschina.net/jfinal/jfinal-weixin/wikis/JFinal-weixin%E4%B8%AD%E7%9A%84WeixinConfig%E9%85%8D%E7%BD%AE) diff --git a/pom.xml b/pom.xml index 590e8b2..0f26358 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,7 @@ com.jfinal jfinal - 2.2 + 3.0 com.fasterxml.jackson.core diff --git a/src/main/java/com/jfinal/weixin/demo/WeixinAuthMessageController.java b/src/main/java/com/jfinal/weixin/demo/WeixinAuthMessageController.java new file mode 100644 index 0000000..06cc11e --- /dev/null +++ b/src/main/java/com/jfinal/weixin/demo/WeixinAuthMessageController.java @@ -0,0 +1,21 @@ +package com.jfinal.weixin.demo; + +import com.jfinal.weixin.sdk.api.ApiConfig; +import com.jfinal.weixin.sdk.api.ApiConfigKit; +import com.jfinal.weixin.sdk.jfinal.component.AuthMessageControllerAdapter; + +/** + * 接收微信服务器消息,自动解析成 InMsg 并分发到相应的处理方法
+ * 消息类型:
+ * component_verify_ticket 微信服务器每隔10分钟会向第三方的消息接收地址推送一次component_verify_ticket,用于获取第三方平台接口调用凭据
+ * authorized 授权成功通知
+ * unauthorized 取消授权通知
+ * updateauthorized 授权更新通知
+ */ +public class WeixinAuthMessageController extends AuthMessageControllerAdapter { + + public ApiConfig getComponentApiConfig() { + return ApiConfigKit.getApiConfig(); + } + +} diff --git a/src/main/java/com/jfinal/weixin/demo/WeixinComponentAuthController.java b/src/main/java/com/jfinal/weixin/demo/WeixinComponentAuthController.java new file mode 100644 index 0000000..fdb20b9 --- /dev/null +++ b/src/main/java/com/jfinal/weixin/demo/WeixinComponentAuthController.java @@ -0,0 +1,62 @@ +package com.jfinal.weixin.demo; + +import com.jfinal.kit.JsonKit; +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.api.ApiResult; +import com.jfinal.weixin.sdk.api.component.ComponentAuthApi; +import com.jfinal.weixin.sdk.api.component.ComponentAuthorizerAccessToken; +import com.jfinal.weixin.sdk.jfinal.component.ComponentAPIController; + +/** + * 微信公众号授权,授权方扫描二维码授权给第三方平台 + * + */ +public class WeixinComponentAuthController extends ComponentAPIController { + + private static final Log log = Log.getLog(WeixinComponentAuthController.class); + + @Override + public ApiConfig getApiConfig() { + return ApiConfigKit.getApiConfig(); + } + + public void index() { + String redirect_uri = "http%3a%2f%2f" + getRequest().getServerName() + "%2fauth%2fcallback"; + String authorizeURL = ComponentAuthApi.getAuthorizeURL(ApiConfigKit.getAppId(), redirect_uri); + setAttr("replaceActionUrl", authorizeURL); + render("/_front/common/location_replace.html"); + } + + /** + * 受理授权回调,使用授权码换取公众号的接口调用凭据和授权信息 + */ + public void callback() { + + if (!StrKit.isBlank(getPara("auth_code"))) { + + String query_auth_code = getPara("auth_code"); + final ApiResult auth = ComponentAuthApi.auth(query_auth_code); + + if (auth != null && auth.isSucceed()) { + final String authorization_info = JsonKit.toJson(auth.get("authorization_info")); + + log.info("auth json >> " + auth.toString()); + log.info("authorization_info >> " + authorization_info); + + ComponentAuthorizerAccessToken token = new ComponentAuthorizerAccessToken(authorization_info); + + log.info("授权方 AppId : " + token.getAuthorizerAppId()); + + } + + // TODO 微信公众号授权成功页面 + renderNull(); + + } + + } + +} diff --git a/src/main/java/com/jfinal/weixin/demo/WeixinComponentSnsController.java b/src/main/java/com/jfinal/weixin/demo/WeixinComponentSnsController.java new file mode 100644 index 0000000..5c1b4b7 --- /dev/null +++ b/src/main/java/com/jfinal/weixin/demo/WeixinComponentSnsController.java @@ -0,0 +1,57 @@ +package com.jfinal.weixin.demo; + +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.api.component.ComponentAccessTokenApi; +import com.jfinal.weixin.sdk.api.component.ComponentSnsAccessToken; +import com.jfinal.weixin.sdk.api.component.ComponentSnsAccessTokenApi; +import com.jfinal.weixin.sdk.jfinal.component.ComponentAPIController; + +/** + * 代公众号发起网页授权 + * + */ +public class WeixinComponentSnsController extends ComponentAPIController { + + private static final Log log = Log.getLog(WeixinComponentSnsController.class); + + @Override + public ApiConfig getApiConfig() { + return ApiConfigKit.getApiConfig(); + } + + /** + * 组织代公众号网页授权 URL,传到前端 HTML 发起请求 + */ + public void index() { + + String authorizerAppId = getPara("authorizer_appid"); + if(!StrKit.isBlank(authorizerAppId)){ + String redirect_uri = "http%3a%2f%2f" + getRequest().getServerName() + "%2fcomponentsns%2fcallback"; + String authorizeURL = ComponentSnsAccessTokenApi.getAuthorizeURL(authorizerAppId, redirect_uri, ApiConfigKit.getAppId(), true); + log.info("authorizeURL : " + authorizeURL); + setAttr("replaceActionUrl", authorizeURL); + render("/_front/common/location_replace.html"); + } else { + renderNull(); + } + } + + /** + * 代公众号发起网页授权的回调处理 + */ + public void callback() { + + if(!StrKit.isBlank(getPara("code")) && !StrKit.isBlank(getPara("appid"))){ + ComponentSnsAccessToken sns = ComponentSnsAccessTokenApi.getSnsAccessToken(getPara("appid"), getPara("code"), ApiConfigKit.getAppId(),ComponentAccessTokenApi.getComponentAccessTokenStr()); + log.info("代公众号发起网页授权,获得 openid >> " + sns.getOpenid()); + } + + // TODO 网页授权成功页面 + renderNull(); + + } + +} diff --git a/src/main/java/com/jfinal/weixin/demo/WeixinConfig.java b/src/main/java/com/jfinal/weixin/demo/WeixinConfig.java index 9b6e390..b50c43f 100644 --- a/src/main/java/com/jfinal/weixin/demo/WeixinConfig.java +++ b/src/main/java/com/jfinal/weixin/demo/WeixinConfig.java @@ -14,12 +14,19 @@ import com.jfinal.config.Plugins; import com.jfinal.config.Routes; import com.jfinal.core.JFinal; import com.jfinal.kit.PropKit; +import com.jfinal.log.Log; +import com.jfinal.plugin.redis.RedisPlugin; +import com.jfinal.plugin.redis.serializer.JdkSerializer; +import com.jfinal.template.Engine; import com.jfinal.weixin.sdk.api.ApiConfig; import com.jfinal.weixin.sdk.api.ApiConfigKit; import com.jfinal.weixin.sdk.cache.LocalTestTokenCache; +import com.jfinal.weixin.sdk.cache.RedisAccessTokenCache; public class WeixinConfig extends JFinalConfig { + Log logger = Log.getLog(WeixinConfig.class); + /** * 如果生产环境配置文件存在,则优先加载该配置,否则加载开发环境配置文件 * @param pro 生产环境配置文件 @@ -37,15 +44,25 @@ public class WeixinConfig extends JFinalConfig { public void configConstant(Constants me) { loadProp("a_little_config_pro.txt", "a_little_config.txt"); me.setDevMode(PropKit.getBoolean("devMode", false)); - // ApiConfigKit 设为开发模式可以在开发阶段输出请求交互的 xml 与 json 数据 ApiConfigKit.setDevMode(me.getDevMode()); + // 设置为公众号第三方平台模式 + ApiConfigKit.setComponentMode(PropKit.getBoolean("componentMode", true)); } public void configRoute(Routes me) { + + me.setBaseViewPath("/_front"); + me.add("/msg", WeixinMsgController.class); me.add("/api", WeixinApiController.class, "/api"); me.add("/pay", WeixinPayController.class); + + me.add("/open/authmsg", WeixinAuthMessageController.class); + me.add("/open/msg", WeixinMsgController.class); // 接收处理授权方公众号的消息和事件 + me.add("/auth",WeixinComponentAuthController.class); + me.add("/componentsns",WeixinComponentSnsController.class); + } public void configPlugin(Plugins me) { @@ -56,9 +73,9 @@ public class WeixinConfig extends JFinalConfig { // me.add(ecp); // 使用redis分布accessToken - // RedisPlugin redisPlugin = new RedisPlugin("weixin", "127.0.0.1"); - // redisPlugin.setSerializer(JdkSerializer.me); // 需要使用fst高性能序列化的用户请删除这一行(Fst jar依赖请查看WIKI) - // me.add(redisPlugin); + RedisPlugin redisPlugin = new RedisPlugin("weixin", "127.0.0.1"); + redisPlugin.setSerializer(JdkSerializer.me); // 需要使用fst高性能序列化的用户请删除这一行(Fst jar依赖请查看WIKI) + me.add(redisPlugin); } public void configInterceptor(Interceptors me) { @@ -73,10 +90,10 @@ public class WeixinConfig extends JFinalConfig { public void afterJFinalStart() { // 1.5 之后支持redis存储access_token、js_ticket,需要先启动RedisPlugin -// ApiConfigKit.setAccessTokenCache(new RedisAccessTokenCache()); + // ApiConfigKit.setAccessTokenCache(new RedisAccessTokenCache()); // 1.6新增的2种初始化 -// ApiConfigKit.setAccessTokenCache(new RedisAccessTokenCache(Redis.use("weixin"))); -// ApiConfigKit.setAccessTokenCache(new RedisAccessTokenCache("weixin")); + // ApiConfigKit.setAccessTokenCache(new RedisAccessTokenCache(Redis.use("weixin"))); + // ApiConfigKit.setAccessTokenCache(new RedisAccessTokenCache("weixin")); ApiConfig ac = new ApiConfig(); // 配置微信 API 相关参数 @@ -89,7 +106,7 @@ public class WeixinConfig extends JFinalConfig { * 1:true进行加密且必须配置 encodingAesKey * 2:false采用明文模式,同时也支持混合模式 */ - ac.setEncryptMessage(PropKit.getBoolean("encryptMessage", false)); + ac.setEncryptMessage(PropKit.getBoolean("encryptMessage", true)); ac.setEncodingAesKey(PropKit.get("encodingAesKey", "setting it in config file")); /** @@ -97,17 +114,24 @@ public class WeixinConfig extends JFinalConfig { */ ApiConfigKit.putApiConfig(ac); + // 微信 WxSession的配置 // 启用默认的Session管理器 -// ApiConfigKit.enableDefaultWxSessionManager(); + // ApiConfigKit.enableDefaultWxSessionManager(); // 启用redis Session管理器 -// ApiConfigKit.setWxSessionManager(new RedisWxSessionManager("weixin")); + // ApiConfigKit.setWxSessionManager(new RedisWxSessionManager("weixin")); + + /** + * 公众号第三方平台模式下,建议使用 RedisAccessTokenCache(),应用重启后授权信息不丢失 + */ + ApiConfigKit.setAccessTokenCache(new RedisAccessTokenCache()); + //ApiConfigKit.setAccessTokenCache(new DefaultAccessTokenCache()); /** * 1.9 新增LocalTestTokenCache用于本地和线上同时使用一套appId时避免本地将线上AccessToken冲掉 * @see WeixinApiController#getToken() */ - boolean isLocal = true; + boolean isLocal = false; if (isLocal) { String onLineTokenUrl = "http://www.jfinal.com/weixin/api/getToken"; ApiConfigKit.setAccessTokenCache(new LocalTestTokenCache(onLineTokenUrl)); @@ -118,4 +142,9 @@ public class WeixinConfig extends JFinalConfig { public static void main(String[] args) { JFinal.start("src/main/webapp", 80, "/", 5); } + + @Override + public void configEngine(Engine me) { + me.addSharedFunction("/_front/common/location_replace.html"); + } } diff --git a/src/main/java/com/jfinal/weixin/demo/WeixinMsgController.java b/src/main/java/com/jfinal/weixin/demo/WeixinMsgController.java index 34b61a0..d0af2a5 100644 --- a/src/main/java/com/jfinal/weixin/demo/WeixinMsgController.java +++ b/src/main/java/com/jfinal/weixin/demo/WeixinMsgController.java @@ -15,8 +15,21 @@ 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.InImageMsg; +import com.jfinal.weixin.sdk.msg.in.InLinkMsg; +import com.jfinal.weixin.sdk.msg.in.InLocationMsg; +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.EventInMsg; +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.InQrCodeEvent; +import com.jfinal.weixin.sdk.msg.in.event.InTemplateMsgEvent; 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; @@ -40,6 +53,7 @@ public class WeixinMsgController extends MsgControllerAdapter { "QUERY_AUTH_CODE")) { processTestTextMsg(inTextMsg); } + //转发给多客服PC客户端 OutCustomMsg outCustomMsg = new OutCustomMsg(inTextMsg); render(outCustomMsg); @@ -115,6 +129,7 @@ public class WeixinMsgController extends MsgControllerAdapter { if (InFollowEvent.EVENT_INFOLLOW_UNSUBSCRIBE.equals(inFollowEvent.getEvent())) { logger.debug("取消关注:" + inFollowEvent.getFromUserName()); + renderNull(); } } @@ -180,7 +195,7 @@ public class WeixinMsgController extends MsgControllerAdapter { @Override public ApiConfig getApiConfig() { - return null; + return ApiConfigKit.getApiConfig(); } @Override @@ -224,7 +239,7 @@ public class WeixinMsgController extends MsgControllerAdapter { "authorization_info")); ComponentAuthorizerAccessToken token = new ComponentAuthorizerAccessToken( authorization_info); - + // TODO 持久化 ApiConfigKit.putApiConfig(new ApiConfig("", token.getAuthorizerAppId(), "")); 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 14e391f..e788c53 100644 --- a/src/main/java/com/jfinal/weixin/sdk/api/AccessTokenApi.java +++ b/src/main/java/com/jfinal/weixin/sdk/api/AccessTokenApi.java @@ -38,7 +38,6 @@ public class AccessTokenApi { 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"; protected static boolean isAuthorized(String appId) { @@ -61,12 +60,18 @@ public class AccessTokenApi { } /** - * 直接获取 accessToken 字符串,方便使用 + * 直接获取 accessToken 字符串,方便使用。 + * 根据是否为公众号第三方模式来获取对应的 accessToken * * @return String accessToken */ public static String getAccessTokenStr() { - return getAccessToken().getAccessToken(); + + if(ApiConfigKit.isComponentMode()){ + return ComponentAuthApi.getComponentAuthorizerAccessTokenStr(); + } else { + return getAccessToken().getAccessToken(); + } } /** diff --git a/src/main/java/com/jfinal/weixin/sdk/api/ApiConfig.java b/src/main/java/com/jfinal/weixin/sdk/api/ApiConfig.java index da7a17c..6535d03 100644 --- a/src/main/java/com/jfinal/weixin/sdk/api/ApiConfig.java +++ b/src/main/java/com/jfinal/weixin/sdk/api/ApiConfig.java @@ -104,6 +104,7 @@ public class ApiConfig implements Serializable { public void setEncryptMessage(boolean messageEncrypt) { this.messageEncrypt = messageEncrypt; } + } 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 6c263a3..d4ccf92 100644 --- a/src/main/java/com/jfinal/weixin/sdk/api/ApiConfigKit.java +++ b/src/main/java/com/jfinal/weixin/sdk/api/ApiConfigKit.java @@ -23,11 +23,16 @@ public class ApiConfigKit { private static final ThreadLocal API_CONFIG_THREAD_LOCAL = new ThreadLocal(); private static final ThreadLocal COMPONENT_API_CONFIG_THREAD_LOCAL = new ThreadLocal(); private static final ThreadLocal TL = new ThreadLocal(); + private static final ThreadLocal TL_AUTHORIZER_APPID = new ThreadLocal(); // 存放授权方 AppId private static final Map CFG_MAP = new ConcurrentHashMap(); private static final String DEFAULT_CFG_KEY = "_default_cfg_key_"; - private static IAccessTokenCache accessTokenCache = new DefaultAccessTokenCache(); + private static IAccessTokenCache accessTokenCache = getAccessTokenCache(); // 开发模式将输出消息交互 xml 到控制台 private static boolean devMode = false; + /** + * 公众号第三方平台模式,默认 flase + */ + private static boolean componentMode = false; /** * 是否启用session,默认不启用 */ @@ -41,6 +46,22 @@ public class ApiConfigKit { ApiConfigKit.devMode = devMode; } + /** + * 获取是否为公众号第三方平台模式 + * @return + */ + public static boolean isComponentMode(){ + return componentMode; + } + + /** + * 设置公众号第三方平台模式 + * @param componentMode + */ + public static void setComponentMode(boolean componentMode){ + ApiConfigKit.componentMode = componentMode; + } + /** * 添加公众号配置,每个appId只需添加一次,相同appId将被覆盖。 * 第一个添加的将作为默认公众号配置 @@ -74,6 +95,10 @@ public class ApiConfigKit { TL.remove(); } + /** + * 在公众号第三方模式(componentMode=true)下,该属性存储的是第三方平台 appId + * @return + */ public static String getAppId() { String appId = TL.get(); if (StrKit.isBlank(appId)) { @@ -150,4 +175,20 @@ public class ApiConfigKit { "需要事先使用 ApiConfigKit.setThreadLocalApiConfig(apiConfig) 将 ApiConfig对象存入,才可以调用 ApiConfigKit.getComponentApiConfig() 方法"); return result; } + + public static void setThreadLocalAuthorizerAppId(String authorizerAppId) { + TL_AUTHORIZER_APPID.set(authorizerAppId); + } + + public static void removeThreadLocalAuthorizerAppId() { + TL_AUTHORIZER_APPID.remove(); + } + + public static String getThreadLocalAuthorizerAppId() { + String appId = TL_AUTHORIZER_APPID.get(); + if (appId == null) + throw new IllegalStateException( + "需要事先使用 ApiConfigKit.setThreadLocalAuthorizerAppId(String authorizerAppId) 将 authorizerAppId 存入,才可以调用 ApiConfigKit.getThreadLocalAuthorizerAppId() 方法"); + return appId; + } } diff --git a/src/main/java/com/jfinal/weixin/sdk/api/ApiResult.java b/src/main/java/com/jfinal/weixin/sdk/api/ApiResult.java index f7ae06a..279a9ba 100644 --- a/src/main/java/com/jfinal/weixin/sdk/api/ApiResult.java +++ b/src/main/java/com/jfinal/weixin/sdk/api/ApiResult.java @@ -6,6 +6,7 @@ package com.jfinal.weixin.sdk.api; +import com.jfinal.weixin.sdk.api.component.ComponentAuthApi; import com.jfinal.weixin.sdk.utils.JsonUtils; import java.math.BigDecimal; @@ -70,10 +71,18 @@ public class ApiResult { * * 2016-06-21 by L.cm 添加 synchronized 锁感谢Git@osc #Lucare * + * 根据是否为公众号第三方模式来获取对应的 accessToken */ private synchronized void refreshAccessTokenIfInvalid() { - if (isAccessTokenInvalid()) - AccessTokenApi.refreshAccessToken(); + if (isAccessTokenInvalid()){ + + if(ApiConfigKit.isComponentMode()){ + ComponentAuthApi.refreshAccessToken(); + } else { + AccessTokenApi.refreshAccessToken(); + } + + } } public String getJson() { diff --git a/src/main/java/com/jfinal/weixin/sdk/api/TemplateMsgApi.java b/src/main/java/com/jfinal/weixin/sdk/api/TemplateMsgApi.java index 14530df..41c14df 100644 --- a/src/main/java/com/jfinal/weixin/sdk/api/TemplateMsgApi.java +++ b/src/main/java/com/jfinal/weixin/sdk/api/TemplateMsgApi.java @@ -22,9 +22,10 @@ public class TemplateMsgApi { * @return {ApiResult} */ public static ApiResult send(String jsonStr) { - String jsonResult = HttpUtils.post(sendApiUrl + AccessTokenApi.getAccessToken().getAccessToken(), jsonStr); + String jsonResult = HttpUtils.post(sendApiUrl + AccessTokenApi.getAccessTokenStr(), jsonStr); return new ApiResult(jsonResult); } + } 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 index 634de7c..95da6e6 100644 --- a/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentAuthApi.java +++ b/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentAuthApi.java @@ -51,8 +51,7 @@ public class ComponentAuthApi { 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 ServiceLoader apiLoader = ServiceLoader.load(AuthorizedApi.class); static AuthorizedApi authorizedApi = apiLoader.iterator().next(); /** @@ -94,22 +93,6 @@ public class ComponentAuthApi { 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(); @@ -123,6 +106,16 @@ public class ComponentAuthApi { return new AuthorizedInfo(json); } + /** + * 直接获取 accessToken 字符串,方便使用 + * + * @return String accessToken + */ + public static String getComponentAuthorizerAccessTokenStr() { + String authorizerAppId =ApiConfigKit.getThreadLocalAuthorizerAppId(); + return getComponentAuthorizerAccessToken(authorizerAppId).getAuthorizerAccessToken(); + } + /** * 直接获取 accessToken 字符串,方便使用 * @@ -131,19 +124,43 @@ public class ComponentAuthApi { public static String getComponentAuthorizerAccessTokenStr(String authorizer_appid) { return getComponentAuthorizerAccessToken(authorizer_appid).getAuthorizerAccessToken(); } - + + /** + * 从缓存中获取 access token,如果未取到或者 access token 不可用则先更新再获取 + * @param authorizer_appid 授权方 appid + * @return ComponentAuthorizerAccessToken 授权方 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); + } + + /** + * 强制更新 access token 值 + */ + public static synchronized void refreshAccessToken() { + String authorizerAppId =ApiConfigKit.getThreadLocalAuthorizerAppId(); + refreshAccessToken(authorizerAppId); + } + /** * 强制更新 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); + if (accessToken == null) { + throw new NullPointerException("授权方令牌 ComponentAuthorizerAccessToken 为 null"); + //accessToken = authorizedApi.of(authorizer_appid); } final Map queryParas = ParaMap.create("component_appid", appId) .put("authorizer_appid", @@ -169,7 +186,7 @@ public class ComponentAuthApi { }); // 三次请求如果仍然返回了不可用的 access token 仍然 put 进去,便于上层通过 AccessToken 中的属性判断底层的情况 - tokenCache.setComponentAuthorizerAccessToken(appId, authorizer_appid, result); + tokenCache.setComponentAuthorizerAccessToken(appId, authorizer_appid, new ComponentAuthorizerAccessToken(accessToken.getAuthorizerAppId(),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 index 5feeba8..83118a6 100644 --- a/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentAuthorizerAccessToken.java +++ b/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentAuthorizerAccessToken.java @@ -119,5 +119,29 @@ public class ComponentAuthorizerAccessToken implements ResultCheck, Serializable public boolean matching() { return isAvailable(); } + + /** + * 组织存储刷新后的令牌。 + * 刷新令牌接口不返回授权方 AppId,所以要重新组织存储 + * @param tokenCache + * @param refreshToken + */ + public ComponentAuthorizerAccessToken (String authorizerAppId,ComponentAuthorizerAccessToken refreshToken){ + + try { + authorizer_appid = authorizerAppId; + authorizer_refresh_token = refreshToken.getAuthorizerRefreshToken(); + authorizer_access_token = refreshToken.getAuthorizerAccessToken(); + expires_in = refreshToken.getExpiresIn(); + errcode = refreshToken.getErrorCode(); + errmsg = refreshToken.getErrorMsg(); + + if (expires_in != null) + expiredTime = System.currentTimeMillis() + ((expires_in - 5) * 1000); + + } catch (Exception e) { + throw new RuntimeException(e); + } + } } diff --git a/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentSnsAccessToken.java b/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentSnsAccessToken.java new file mode 100644 index 0000000..e4c3e19 --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentSnsAccessToken.java @@ -0,0 +1,135 @@ +/** + * Copyright ® 2016 DQ ENCH Co. Ltd. + * All right reserved. + */ + +package com.jfinal.weixin.sdk.api.component; + +import java.io.Serializable; +import java.util.Map; + +import com.jfinal.weixin.sdk.api.ReturnCode; +import com.jfinal.weixin.sdk.utils.JsonUtils; +import com.jfinal.weixin.sdk.utils.RetryUtils.ResultCheck; + + +/** + * SnsAccessToken + * 封装 access_token + */ +public class ComponentSnsAccessToken implements ResultCheck, Serializable +{ + + private static final long serialVersionUID = 6369625123403343963L; + + private String access_token; // 正确获取到 access_token 时有值 + private Integer expires_in; // 正确获取到 access_token 时有值 + private String refresh_token; // + private String openid; // + private String scope; // + private String unionid; // + private Integer errcode; // 出错时有值 + private String errmsg; // 出错时有值 + + private Long expiredTime; // 正确获取到 access_token 时有值,存放过期时间 + private String json; + + public ComponentSnsAccessToken(String jsonStr) + { + this.json = jsonStr; + + try + { + @SuppressWarnings("unchecked") + Map temp = JsonUtils.parse(jsonStr, Map.class); + access_token = (String) temp.get("access_token"); + expires_in = getInt(temp, "expires_in"); + refresh_token = (String) temp.get("refresh_token"); + openid = (String) temp.get("openid"); + unionid = (String) temp.get("unionid"); + scope = (String) temp.get("scope"); + 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; + } + + private Integer getInt(Map temp, String key) { + Number number = (Number) temp.get(key); + return number == null ? null : number.intValue(); + } + + public boolean isAvailable() + { + if (expiredTime == null) + return false; + if (errcode != null) + return false; + if (expiredTime < System.currentTimeMillis()) + return false; + return access_token != null; + } + + public String getAccessToken() + { + return access_token; + } + + public Integer getExpiresIn() + { + return expires_in; + } + + public String getRefresh_token() + { + return refresh_token; + } + + public String getOpenid() + { + return openid; + } + + public String getScope() + { + return scope; + } + + public Integer getErrorCode() + { + return errcode; + } + + public String getErrorMsg() + { + if (errcode != null) + { + String result = ReturnCode.get(errcode); + if (result != null) + return result; + } + return errmsg; + } + + public String getUnionid() + { + return unionid; + } + + @Override + public boolean matching() { + return isAvailable(); + } + +} diff --git a/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentSnsAccessTokenApi.java b/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentSnsAccessTokenApi.java new file mode 100644 index 0000000..9c603d5 --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/api/component/ComponentSnsAccessTokenApi.java @@ -0,0 +1,127 @@ +/** + * Copyright ® 2016 DQ ENCH Co. Ltd. + * All right reserved. + */ + +package com.jfinal.weixin.sdk.api.component; + +import com.jfinal.kit.StrKit; +import com.jfinal.weixin.sdk.kit.ParaMap; +import com.jfinal.weixin.sdk.kit.PaymentKit; +import com.jfinal.weixin.sdk.utils.HttpUtils; +import com.jfinal.weixin.sdk.utils.RetryUtils; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Callable; + +/** + * 代公众号发起网页授权获取 API + */ +public class ComponentSnsAccessTokenApi +{ + private static String url = "https://api.weixin.qq.com/sns/oauth2/component/access_token?grant_type=authorization_code"; + private static String authorize_uri = "https://open.weixin.qq.com/connect/oauth2/authorize"; + private static String qrconnect_url = "https://open.weixin.qq.com/connect/qrconnect"; + + /** + * 生成Authorize链接 + * @param authorizerAppId 授权方公众号appid + * @param redirect_uri 重定向地址,需要urlencode,这里填写的应是服务开发方的回调地址 + * @param componentAppId 服务方的appid,在申请创建公众号服务成功后,可在公众号服务详情页找到 + * @param snsapiBase snsapi_base(不弹出授权页面,只能拿到用户openid)snsapi_userinfo(弹出授权页面,这个可以通过 openid 拿到昵称、性别、所在地) + * @return url + */ + public static String getAuthorizeURL(String authorizerAppId, String redirect_uri, String componentAppId, boolean snsapiBase) { + return getAuthorizeURL(authorizerAppId, redirect_uri, null, componentAppId, snsapiBase); + } + + /** + * 生成Authorize链接 + * @param authorizerAppId 授权方公众号appid + * @param redirectUri 重定向地址,需要urlencode,这里填写的应是服务开发方的回调地址 + * @param state 重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节 + * @param componentAppid 服务方的appid,在申请创建公众号服务成功后,可在公众号服务详情页找到 + * @param snsapiBase snsapi_base(不弹出授权页面,只能拿到用户openid)snsapi_userinfo(弹出授权页面,这个可以通过 openid 拿到昵称、性别、所在地) + * @return url + */ + public static String getAuthorizeURL(String authorizerAppId, String redirectUri, String state,String componentAppId, boolean snsapiBase) { + Map params = new HashMap(); + params.put("appid", authorizerAppId); + params.put("response_type", "code"); + params.put("redirect_uri", redirectUri); + // snsapi_base(不弹出授权页面,只能拿到用户openid) + // snsapi_userinfo(弹出授权页面,这个可以通过 openid 拿到昵称、性别、所在地) + if (snsapiBase) { + params.put("scope", "snsapi_base"); + } else { + params.put("scope", "snsapi_userinfo"); + } + if (StrKit.isBlank(state)) { + params.put("state", "wx#wechat_redirect"); + } else { + params.put("state", state.concat("#wechat_redirect")); + } + params.put("component_appid",componentAppId); + + String para = PaymentKit.packageSign(params, false); + return authorize_uri + "?" + para; + } + + + /** + * 生成网页二维码授权链接 TODO 待验证正确性 + * @param appId 应用id + * @param redirect_uri 回跳地址 + * @return url + */ + public static String getQrConnectURL(String appId, String redirect_uri) { + return getQrConnectURL(appId, redirect_uri, null); + } + + /** + * 生成网页二维码授权链接 TODO 待验证正确性 + * @param appId 应用id + * @param redirect_uri 回跳地址 + * @param state 重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节 + * @return url + */ + public static String getQrConnectURL(String appId, String redirect_uri, String state) { + Map params = new HashMap(); + params.put("appid", appId); + params.put("response_type", "code"); + params.put("redirect_uri", redirect_uri); + params.put("scope", "snsapi_login"); + if (StrKit.isBlank(state)) { + params.put("state", "wx#wechat_redirect"); + } else { + params.put("state", state.concat("#wechat_redirect")); + } + String para = PaymentKit.packageSign(params, false); + return qrconnect_url + "?" + para; + } + + /** + * 通过code获取access_token + * + * @param appId 应用唯一标识 + * @param code 第一步获取的code参数 + * @param grantType 填authorization_code + * @param componentAppid 服务开发方的appid + * @param componentAccessToken 服务开发方的access_token + * @return ComponentSnsAccessToken + */ + public static ComponentSnsAccessToken getSnsAccessToken(String appId, String code, String componentAppid, String componentAccessToken) + { + final Map queryParas = ParaMap.create("appid", appId).put("code", code).put("component_appid", componentAppid).put("component_access_token", componentAccessToken).getData(); + + return RetryUtils.retryOnException(3, new Callable() { + + @Override + public ComponentSnsAccessToken call() throws Exception { + String json = HttpUtils.get(url, queryParas); + return new ComponentSnsAccessToken(json); + } + }); + } +} diff --git a/src/main/java/com/jfinal/weixin/sdk/jfinal/MsgInterceptor.java b/src/main/java/com/jfinal/weixin/sdk/jfinal/MsgInterceptor.java index ccb2002..632d69c 100644 --- a/src/main/java/com/jfinal/weixin/sdk/jfinal/MsgInterceptor.java +++ b/src/main/java/com/jfinal/weixin/sdk/jfinal/MsgInterceptor.java @@ -37,10 +37,22 @@ public class MsgInterceptor implements Interceptor { throw new RuntimeException("控制器需要继承 MsgController"); try { - String appId = _parser.getAppId(controller); - // 将 appId 与当前线程绑定,以便在后续操作中方便获取ApiConfig对象: ApiConfigKit.getApiConfig(); - ApiConfigKit.setThreadLocalAppId(appId); - + + if(ApiConfigKit.isComponentMode()){ + /** + * 代公众号处理消息和事件,获取 appid + * 收到消息的示例 /msg/wx1d72971e8550ae68?signature=8c9…… + */ + String authorizerAppId = controller.getPara(0); + ApiConfigKit.setThreadLocalAuthorizerAppId(authorizerAppId); + ApiConfigKit.setThreadLocalComponentApiConfig(ApiConfigKit.getApiConfig()); + + } else { + String appId = _parser.getAppId(controller); + // 将 appId 与当前线程绑定,以便在后续操作中方便获取ApiConfig对象: ApiConfigKit.getApiConfig(); + ApiConfigKit.setThreadLocalAppId(appId); + } + // 如果是服务器配置请求,则配置服务器并返回 if (isConfigServerRequest(controller)) { configServer(controller); @@ -60,7 +72,8 @@ public class MsgInterceptor implements Interceptor { } } finally { - ApiConfigKit.removeThreadLocalAppId(); + ApiConfigKit.removeThreadLocalAuthorizerAppId(); + ApiConfigKit.removeThreadLocalComponentApiConfig(); } } 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 index d9df99c..00e725d 100644 --- a/src/main/java/com/jfinal/weixin/sdk/jfinal/component/AuthMessageController.java +++ b/src/main/java/com/jfinal/weixin/sdk/jfinal/component/AuthMessageController.java @@ -11,19 +11,22 @@ 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.*; +import com.jfinal.weixin.sdk.msg.component.AuthMsg; +import com.jfinal.weixin.sdk.msg.component.AuthorizedMsg; +import com.jfinal.weixin.sdk.msg.component.ComponentVerifyTicketMsg; +import com.jfinal.weixin.sdk.msg.component.NotDefinedComponentMsg; +import com.jfinal.weixin.sdk.msg.component.UnAuthorizedMsg; +import com.jfinal.weixin.sdk.msg.component.UpdateAuthorizedMsg; /** * 接收微信服务器消息,自动解析成 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 对象 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 index c49d8d1..fa8bc07 100644 --- a/src/main/java/com/jfinal/weixin/sdk/jfinal/component/AuthMessageControllerAdapter.java +++ b/src/main/java/com/jfinal/weixin/sdk/jfinal/component/AuthMessageControllerAdapter.java @@ -13,7 +13,12 @@ import com.jfinal.weixin.sdk.cache.IAccessTokenCache; import com.jfinal.weixin.sdk.msg.component.*; /** - * 接收微信服务器消息,自动解析成 InMsg 并分发到相应的处理方法 + * 接收微信服务器消息,自动解析成 InMsg 并分发到相应的处理方法
+ * 消息类型:
+ * component_verify_ticket 微信服务器每隔10分钟会向第三方的消息接收地址推送一次component_verify_ticket,用于获取第三方平台接口调用凭据
+ * authorized 授权成功通知
+ * unauthorized 取消授权通知
+ * updateauthorized 授权更新通知
*/ public abstract class AuthMessageControllerAdapter extends AuthMessageController { 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 index 72245cd..fad7834 100644 --- a/src/main/java/com/jfinal/weixin/sdk/jfinal/component/ComponentAPIInterceptor.java +++ b/src/main/java/com/jfinal/weixin/sdk/jfinal/component/ComponentAPIInterceptor.java @@ -17,9 +17,13 @@ public class ComponentAPIInterceptor implements Interceptor { throw new RuntimeException("控制器需要继承 ApiController"); try { - ApiConfigKit.setThreadLocalComponentApiConfig(((ComponentAPIController) controller).getApiConfig()); + + ApiConfigKit.setThreadLocalComponentApiConfig(ApiConfigKit.getApiConfig()); + inv.invoke(); + } finally { + ApiConfigKit.removeThreadLocalAuthorizerAppId(); ApiConfigKit.removeThreadLocalComponentApiConfig(); } } 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 index b5bed6e..c6ff17a 100644 --- a/src/main/java/com/jfinal/weixin/sdk/msg/component/AuthMsg.java +++ b/src/main/java/com/jfinal/weixin/sdk/msg/component/AuthMsg.java @@ -8,16 +8,7 @@ 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 { @@ -30,13 +21,10 @@ public abstract class AuthMsg { /** * 消息类型 - * 1:text 文本消息 - * 2:image 图片消息 - * 3:voice 语音消息 - * 4:video 视频消息 - * 5:location 地址位置消息 - * 6:link 链接消息 - * 7:event 事件 + * 1:component_verify_ticket 每10分钟推送一次的安全ticket + * 2:authorized 授权成功通知 + * 3:unauthorized 取消授权通知 + * 4:updateauthorized 授权更新通知 */ protected String 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 index ac05c0e..e567eb2 100644 --- a/src/main/java/com/jfinal/weixin/sdk/msg/component/AuthorizedMsg.java +++ b/src/main/java/com/jfinal/weixin/sdk/msg/component/AuthorizedMsg.java @@ -8,12 +8,14 @@ package com.jfinal.weixin.sdk.msg.component; /** *
- * 接收文本消息
+ * 接收公众号对第三方平台授权成功的通知
  * 
- *  
+ * 第三方平台appid
  * 1413192605 
- *  
- *  
+ * authorized
+ * 公众号appid
+ * 授权码(code)
+ * 过期时间
  * 
  * 
*/ 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 index 569026c..004c782 100644 --- a/src/main/java/com/jfinal/weixin/sdk/msg/component/ComponentVerifyTicketMsg.java +++ b/src/main/java/com/jfinal/weixin/sdk/msg/component/ComponentVerifyTicketMsg.java @@ -8,12 +8,12 @@ package com.jfinal.weixin.sdk.msg.component; /** *
- * 接收文本消息
+ * 接收微信服务器每隔 10 分钟定时推送 component_verify_ticket 的消息
  * 
- *  
+ * 第三方平台appid
  * 1413192605 
- *  
- *  
+ * component_verify_ticket
+ * Ticket内容
  * 
  * 
*/ 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 index efbbc17..f98245d 100644 --- a/src/main/java/com/jfinal/weixin/sdk/msg/component/UnAuthorizedMsg.java +++ b/src/main/java/com/jfinal/weixin/sdk/msg/component/UnAuthorizedMsg.java @@ -8,14 +8,12 @@ package com.jfinal.weixin.sdk.msg.component; /** *
- * 接收文本消息
+ * 接收公众号对第三方平台取消授权的通知
  * 
  * 第三方平台appid
  * 1413192760
- * authorized
+ * unauthorized
  * 公众号appid
- * 授权码(code)
- * 过期时间
  * 
  * 
*/ 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 index 28a538b..1606497 100644 --- a/src/main/java/com/jfinal/weixin/sdk/msg/component/UpdateAuthorizedMsg.java +++ b/src/main/java/com/jfinal/weixin/sdk/msg/component/UpdateAuthorizedMsg.java @@ -8,7 +8,7 @@ package com.jfinal.weixin.sdk.msg.component; /** *
- * 接收文本消息
+ * 接收公众号对第三方平台更新授权的通知
  * 
  * 第三方平台appid
  * 1413192760
diff --git a/src/main/resources/a_little_config.txt b/src/main/resources/a_little_config.txt
index ab466ce..7fbeaf6 100644
--- a/src/main/resources/a_little_config.txt
+++ b/src/main/resources/a_little_config.txt
@@ -2,6 +2,8 @@ jdbcUrl = jdbc:mysql://127.0.0.1/jfinal_weixin?characterEncoding=utf8&zeroDateTi
 user = root
 password =
 devMode = true
+# 公众号第三方平台模式
+componentMode = true
 
 # 微信服务器回调所用的 token
 token=__my__token__
@@ -10,8 +12,14 @@ token=__my__token__
 appId=wx9803d1188fa5fbda
 appSecret=db859c968763c582794e7c3d003c3d87
 
-#是否对消息进行加密,是否对消息进行加密,对应于微信平台的消息加解密方式,false支持明文模式及兼容模式,true支持安全模式及兼容模式
-#encryptMessage=true
-#encodingAesKey=yourEncodingAesKey
+# 公众号第三方平台
+# 注意:最后一对 appId、appSecret 才是有效的
+appId=wx1***************
+appSecret=779*****************************
+
+# 是否对消息进行加密,是否对消息进行加密,对应于微信平台的消息加解密方式,false支持明文模式及兼容模式,true支持安全模式及兼容模式
+# 如果公众号第三方平台模式= true,encryptMessage 必须设置为 true
+encryptMessage=true
+encodingAesKey=yourEncodingAesKey
 
 
diff --git a/src/main/webapp/WEB-INF/services/com.jfinal.weixin.sdk.api.component b/src/main/webapp/WEB-INF/services/com.jfinal.weixin.sdk.api.component
new file mode 100644
index 0000000..7f4c7c1
--- /dev/null
+++ b/src/main/webapp/WEB-INF/services/com.jfinal.weixin.sdk.api.component
@@ -0,0 +1 @@
+com.jfinal.weixin.sdk.api.component.DefaultAuthorizedApi
\ No newline at end of file
diff --git a/src/main/webapp/_front/common/location_replace.html b/src/main/webapp/_front/common/location_replace.html
new file mode 100644
index 0000000..304a2a9
--- /dev/null
+++ b/src/main/webapp/_front/common/location_replace.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
-- 
Gitee