diff --git a/src/main/java/com/jfinal/weixin/demo/WeixinMsgController.java b/src/main/java/com/jfinal/weixin/demo/WeixinMsgController.java index 4a989245e2585c5a22656873e914719e89f09553..34b61a0ec6f7b148f291bcbc78c406476252354d 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/AccessToken.java b/src/main/java/com/jfinal/weixin/sdk/api/AccessToken.java index 9e88c4572620b14a4da187649421afad2d87ac32..12d918808ec457c76573a9490a71a84a0da134c4 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 5ca846cc913cd722ac56be777850c76fb4b62ee8..14e391f027986ce6a828e1f930a8abf3e2f8859a 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
+ * 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 0000000000000000000000000000000000000000..ffe4abce523f13472f518a56b51d0bb1dd8cfa1d --- /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
+ * 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到数据库中实现分布式可用
+ *
+ * 具体配置:
+ *
+ * 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到数据库中实现分布式可用
+ *
+ * 具体配置:
+ *
+ * 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
+ * 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
+ * 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到数据库中实现分布式可用
+ *
+ * 具体配置:
+ *
+ * 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
+ * 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到数据库中实现分布式可用
+ *
+ * 具体配置:
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ */
+
+package com.jfinal.weixin.sdk.api.component;
+
+import java.io.Serializable;
+
+/**
+ *
+ * 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 0000000000000000000000000000000000000000..51cbb548bdcfdf2a6e0bdd69eeab33442c722d6a
--- /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
+ * 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
+ * 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
+ * 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 0000000000000000000000000000000000000000..e851a3237adaba16f212c45816a311b70f9e0896
--- /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 0000000000000000000000000000000000000000..2fecac9ddad917f446b54329860713b6730383b6
--- /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 0000000000000000000000000000000000000000..dd5e068a365b5d04911e7a1270790611892b80c1
--- /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 0000000000000000000000000000000000000000..82754bd733ff955db4b5615911e7abf819732a3f
--- /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 0000000000000000000000000000000000000000..e80bb9a808e30898481a167bb3f3838d99a044cf
--- /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
+ * 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 0000000000000000000000000000000000000000..c49d8d1b915f0f5b8f8255b5b6d16b5cdc8f4054
--- /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 0000000000000000000000000000000000000000..d4d74feaa948bba287267b20ffa18273c0ec8f67
--- /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 0000000000000000000000000000000000000000..0aa98e76ec9c029e6b713d2b9ae7c44150009cda
--- /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 0000000000000000000000000000000000000000..72245cdb0a7600563c7de489d5796e498ddf9241
--- /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 6987976e6a05fea635dcef8cd319c3bb63f244d6..7e6a8ac9a1ccfdc3984f5efccb0ae34875313390 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 0000000000000000000000000000000000000000..381edad7f25afc349ab626a578897ed3d223ae87
--- /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;
+ }
+
+ /**
+ *
* 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
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ */
+
+package com.jfinal.weixin.sdk.msg.component;
+
+/**
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ */
+
+package com.jfinal.weixin.sdk.msg.component;
+
+/**
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ */
+
+package com.jfinal.weixin.sdk.msg.component;
+
+/**
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ */
+
+package com.jfinal.weixin.sdk.msg.component;
+
+/**
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ */
+
+package com.jfinal.weixin.sdk.msg.component;
+
+/**
+ *
* 用户进入摇一摇界面,在“周边”页卡下摇一摇时,
* 微信会把这个事件推送到开发者填写的URL(登录公众平台进入开发者中心设置)。
* 推送内容包含摇一摇时“周边”页卡展示出来的页面所对应的设备信息,
* 以及附近最多五个属于该公众账号的设备的信息。
-
+ * 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
+ * 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
+ * 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
+ * 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
+ * 接收文本消息
+ *
+ */
+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 0000000000000000000000000000000000000000..d01684a65bb83d467544205330dbdafaf9de9111
--- /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;
+
+/**
+ *
+ * 接收消息,以下是接收文本消息的例子
+ * 接收文本消息
+ * <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 0000000000000000000000000000000000000000..ac05c0e9ea7689871a3aec7371bf3c0bd59d948e
--- /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).
+ *
+ * 接收文本消息
+ *
+ */
+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 0000000000000000000000000000000000000000..569026c6d3c19792f2ab9c5a98365172aaae698f
--- /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).
+ *
+ * 接收文本消息
+ *
+ */
+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 0000000000000000000000000000000000000000..d02d1d58efafde12f316d04e975a8619bb3db201
--- /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 0000000000000000000000000000000000000000..efbbc175ed95cfedf7467f583ecaa33fd5ba86b2
--- /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).
+ *
+ * 接收文本消息
+ *
+ */
+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 0000000000000000000000000000000000000000..28a538b475e32d4cbe061c4d1d893286d1728843
--- /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).
+ *
+ * 接收文本消息
+ *
+ */
+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 d481a79e7337540d6930d7d83c2fa586ede4f4e6..3a5ca5db0e10ae44077b933f82ff8d10398b6659 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
- *
+ *
- <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