├── LICENSE
├── README.md
├── pom.xml
└── src
├── main
├── java
│ └── cn
│ │ └── newtol
│ │ └── weixin
│ │ ├── Enum
│ │ └── ResultEnum.java
│ │ ├── WeixinApplication.java
│ │ ├── aspect
│ │ └── HttpAspect.java
│ │ ├── config
│ │ ├── JacksonConfig.java
│ │ └── SessionConfig.java
│ │ ├── controller
│ │ └── WeiXinBaseController.java
│ │ ├── domain
│ │ ├── BaseDO.java
│ │ ├── HttpClientResult.java
│ │ ├── Result.java
│ │ ├── WeiXinSendAutoMessage.java
│ │ ├── WeiXinUserInfo.java
│ │ └── dto
│ │ │ ├── BaseWeiXinMessage.java
│ │ │ ├── RedirectUrlWeiXinConfig.java
│ │ │ ├── WeiXinConfigInfo.java
│ │ │ ├── WeiXinMenu.java
│ │ │ ├── WeiXinReceiveMessage.java
│ │ │ ├── WeiXinUserInfoOpenId.java
│ │ │ ├── WeiXinVerify.java
│ │ │ └── WeiXinWebAuthorize.java
│ │ ├── exceptions
│ │ └── TestException.java
│ │ ├── handle
│ │ └── ExceptionHandle.java
│ │ ├── repository
│ │ └── BaseUserInfoRepository.java
│ │ ├── service
│ │ ├── WeiXinBaseService.java
│ │ └── WeiXinBaseServiceImpl.java
│ │ └── utils
│ │ ├── EncryptUtil.java
│ │ ├── HttpClientUtil.java
│ │ ├── HttpServletUtil.java
│ │ ├── JacksonUtil.java
│ │ ├── RedisUtil.java
│ │ ├── ResultUtil.java
│ │ └── WeiXinUtil.java
└── resources
│ └── application.yml
└── test
└── java
└── cn
└── newtol
└── weixin
└── WeixinApplicationTests.java
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SpringBoot-wechat
2 | SpringBoot开发微信公众号后台(可同时为多个公众号提供服务,非第三方平台)
3 |
4 | ## 配置使用
5 |
6 | ### 接入微信服务器
7 | 1. 将application.yml的weiXin.token更改为在微信服务器配置的token,如果需要服务于多个公众号,将每个公众号的token设置为一致即可接入
8 |
9 | ### 网页授权
10 | 实现为不同公众号分为提供网页授权和跳转服务
11 | 1. 将在微信服务器将网页授权的跳转地址和application.yml中weiXin.getWebAuthorize的设置为:`http://xxx/xxx/getWebAuthorize`
12 | 2. 请求`http://xxx/xxx/setRedirectUrl`设置需要跳转的链接,参数为appId,appSecret,redirectUrl,获取返回的state
13 | 3. 将第二步获取到的state添加`http://xxx/redirectUrl?state=STATE`中,将该链接设置为自动回复或者菜单中,即可实现网页授权和跳转
14 |
15 | ### 获取AccessToken
16 | 1. 请求`http://xxx/accessToken`,即可获得accessToken,当一个公众号有多个子项目运行时,可以避免accessToken冲突
17 |
18 | ### 自定义菜单实现
19 |
20 | 1. 在application.yml中按照yml格式进行菜单编辑,例如:
21 | ```yml
22 | menu:
23 | button:
24 | - sub_button:
25 | - clickButton:
26 | type: click
27 | name: 点击
28 | key: hi
29 | - clickButton:
30 | type: click
31 | name: 点击2
32 | key: hi1
33 | name: 视图
34 | - sub_button:
35 | - clickButton:
36 | type: view
37 | name: 视图2
38 | url: http://www.baidu.com
39 | name: 菜单
40 | ```
41 | 2. 重启项目后,请求:`http://xxx//menu`,菜单即可生效
42 |
43 | ### 自动回复
44 | 1. 请求`http://xxx/setAutoMessage`,设置自动回复的内容(支持回复语音、图片、文字、图文、视频等)例如:
45 | ```json
46 | {
47 | "fromUserName":"你的微信公众号账号",
48 | "key":"图文",
49 | "msgType":"news",
50 | "articleCount":2,
51 | "articles":{
52 | "item":[{
53 | "title":"test",
54 | "description":"这是测试内容",
55 | "picUrl":"http://hongyan.cqupt.edu.cn/images/index_top.jpg",
56 | "url":"http://hongyan.cqupt.edu.cn/"
57 |
58 | },{
59 | "title":"test2",
60 | "description":"这是测试内容3",
61 | "picUrl":"http://www.hers.cn/uploadfile/2011/1006/20111006022157183.jpg",
62 | "url":"http://mp.weixin.qq.com/mp/appmsg/show?__biz=MjM5MDE4Njg2MQ==&appmsgid=10000072&itemidx=1&sign=bea6deb75836dbe1249dcf394e8f3c21#wechat_redirect"
63 | }]
64 | }
65 | ```
66 |
67 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | cn.newtol
7 | weixin
8 | 0.0.1-SNAPSHOT
9 | jar
10 |
11 | weixin
12 | Demo project for Spring Boot
13 |
14 |
15 | org.springframework.boot
16 | spring-boot-starter-parent
17 | 2.1.0.RELEASE
18 |
19 |
20 |
21 | UTF-8
22 | UTF-8
23 | 1.8
24 |
25 |
26 |
27 |
28 |
29 | org.springframework.boot
30 | spring-boot-starter-data-redis
31 |
32 |
33 | org.springframework.boot
34 | spring-boot-starter-web
35 |
36 |
37 |
38 | org.springframework.boot
39 | spring-boot-starter-test
40 | test
41 |
42 |
43 |
44 | org.springframework.boot
45 | spring-boot-starter-aop
46 |
47 |
48 |
49 | org.projectlombok
50 | lombok
51 | provided
52 |
53 |
54 |
55 | org.apache.httpcomponents
56 | httpclient
57 |
58 |
59 |
60 | org.jsoup
61 | jsoup
62 | 1.11.3
63 |
64 |
65 |
66 | com.fasterxml.jackson.dataformat
67 | jackson-dataformat-xml
68 |
69 |
70 | com.alibaba
71 | fastjson
72 | 1.2.35
73 |
74 |
75 |
76 | org.springframework.boot
77 | spring-boot-configuration-processor
78 | true
79 |
80 |
81 |
82 | org.springframework.session
83 | spring-session-data-redis
84 |
85 |
86 |
87 |
88 | org.springframework.boot
89 | spring-boot-starter-data-jpa
90 |
91 |
92 | org.springframework.boot
93 | spring-boot-starter-jdbc
94 |
95 |
96 | mysql
97 | mysql-connector-java
98 | runtime
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | org.springframework.boot
107 | spring-boot-maven-plugin
108 |
109 |
110 |
111 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/src/main/java/cn/newtol/weixin/Enum/ResultEnum.java:
--------------------------------------------------------------------------------
1 | package cn.newtol.weixin.Enum;
2 |
3 | /**
4 | * @Author: 公众号:Newtol
5 | * @Description:
6 | * @Date: Created in 13:46 2018/11/10
7 | */
8 |
9 | @SuppressWarnings("AlibabaLowerCamelCaseVariableNaming")
10 | public enum ResultEnum {
11 | /**
12 | * 系统未知错误
13 | */
14 | UNKONW_ERROR(-1,"未知错误:"),
15 | /**
16 | * 请求成功
17 | */
18 | SUCCESS(0,"success"),
19 | /**
20 | * 请求缺少参数
21 | */
22 | LACK_PARAMETER(1,"缺少参数"),
23 | /**
24 | * 微信公众号的appId或者appSecret错误
25 | */
26 | ERROR_WEIXINBASEINFO(2,"appId或者appSecret错误");
27 |
28 | /**
29 | * 错误码
30 | */
31 | private Integer errorCode;
32 | /**
33 | * 错误提示信息
34 | */
35 | private String message;
36 |
37 | ResultEnum(Integer errorCode, String message) {
38 | this.errorCode = errorCode;
39 | this.message = message;
40 | }
41 |
42 | public Integer getErrorCode() {
43 | return errorCode;
44 | }
45 |
46 |
47 | public String getMessage() {
48 | return message;
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/cn/newtol/weixin/WeixinApplication.java:
--------------------------------------------------------------------------------
1 | package cn.newtol.weixin;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
6 |
7 | @EnableJpaAuditing
8 | @SpringBootApplication
9 | public class WeixinApplication {
10 |
11 | public static void main(String[] args) {
12 | SpringApplication.run(WeixinApplication.class, args);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/cn/newtol/weixin/aspect/HttpAspect.java:
--------------------------------------------------------------------------------
1 | //package cn.newtol.weixin.aspect;
2 | //
3 | ////import org.aspectj.lang.JoinPoint;
4 | ////import org.aspectj.lang.annotation.Aspect;
5 | ////import org.aspectj.lang.annotation.Before;
6 | ////import org.aspectj.lang.annotation.Pointcut;
7 | //import org.aspectj.lang.JoinPoint;
8 | //import org.aspectj.lang.annotation.Aspect;
9 | //import org.aspectj.lang.annotation.Before;
10 | //import org.aspectj.lang.annotation.Pointcut;
11 | //import org.springframework.stereotype.Component;
12 | //import org.springframework.web.context.request.RequestContextHolder;
13 | //import org.springframework.web.context.request.ServletRequestAttributes;
14 | //
15 | //import javax.servlet.http.HttpServletRequest;
16 | //import javax.servlet.http.HttpServletResponse;
17 | //import java.io.IOException;
18 | //
19 | ///**
20 | // * @Author: 公众号:Newtol
21 | // * @Description:
22 | // * @Date: Created in 15:35 2018/11/10
23 | // */
24 | //@Aspect
25 | //@Component
26 | //public class HttpAspect {
27 | // @Pointcut("execution(public * cn.newtol.weixin.controller.WeiXinBaseController.setSession(..))")
28 | // public void log() {
29 | //
30 | // }
31 | //
32 | // @Before("log()")
33 | // public void forward(JoinPoint joinPoint) throws IOException {
34 | // ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
35 | // HttpServletRequest request = attributes.getRequest();
36 | // HttpServletResponse response = attributes.getResponse();
37 | // if(request.getSession().getAttribute("User") == null || "".equals(request.getSession().getAttribute("User"))){
38 | // StringBuffer url = request.getRequestURL();
39 | // System.out.println(url);
40 | // response.setContentType("text/html;charset=UTF-8");
41 | // // 要重定向的新位置
42 | // String site = new String("http://www.baidu.com");
43 | // response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
44 | // response.setHeader("Location", site);
45 | // }
46 | //
47 | // }
48 | //}
49 |
--------------------------------------------------------------------------------
/src/main/java/cn/newtol/weixin/config/JacksonConfig.java:
--------------------------------------------------------------------------------
1 | package cn.newtol.weixin.config;
2 |
3 | import com.fasterxml.jackson.core.JsonGenerator;
4 | import com.fasterxml.jackson.core.JsonProcessingException;
5 | import com.fasterxml.jackson.databind.JsonSerializer;
6 | import com.fasterxml.jackson.databind.ObjectMapper;
7 | import com.fasterxml.jackson.databind.SerializerProvider;
8 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
9 | import org.springframework.context.annotation.Bean;
10 | import org.springframework.context.annotation.Configuration;
11 | import org.springframework.context.annotation.Primary;
12 | import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
13 |
14 | import java.io.IOException;
15 |
16 | /**
17 | * @Author: 公众号:Newtol
18 | * @Description:
19 | * @Date: Created in 22:17 2018/11/20
20 | */
21 | @Configuration
22 | public class JacksonConfig {
23 | @Bean
24 | @Primary
25 | @ConditionalOnMissingBean(ObjectMapper.class)
26 | public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
27 | ObjectMapper objectMapper = builder.createXmlMapper(false).build();
28 | objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer() {
29 |
30 | @Override
31 | public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
32 | jsonGenerator.writeString("");
33 | }
34 | });
35 | return objectMapper;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/cn/newtol/weixin/config/SessionConfig.java:
--------------------------------------------------------------------------------
1 | package cn.newtol.weixin.config;
2 |
3 | import org.springframework.context.annotation.Configuration;
4 | import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
5 |
6 |
7 | /**
8 | * @Author: 公众号:Newtol
9 | * @Description:
10 | * @Date: Created in 11:05 2018/11/13
11 | */
12 | @Configuration
13 | @EnableRedisHttpSession(maxInactiveIntervalInSeconds = 600)
14 | public class SessionConfig {
15 |
16 |
17 | }
--------------------------------------------------------------------------------
/src/main/java/cn/newtol/weixin/controller/WeiXinBaseController.java:
--------------------------------------------------------------------------------
1 | package cn.newtol.weixin.controller;
2 |
3 | import cn.newtol.weixin.domain.Result;
4 | import cn.newtol.weixin.domain.WeiXinSendAutoMessage;
5 | import cn.newtol.weixin.domain.dto.*;
6 | import cn.newtol.weixin.service.WeiXinBaseService;
7 | import cn.newtol.weixin.utils.ResultUtil;
8 | import com.fasterxml.jackson.annotation.JsonInclude;
9 | import com.fasterxml.jackson.core.JsonProcessingException;
10 | import com.fasterxml.jackson.dataformat.xml.XmlMapper;
11 | import org.springframework.stereotype.Controller;
12 | import org.springframework.web.bind.annotation.*;
13 | import javax.annotation.Resource;
14 | import javax.servlet.http.HttpServletRequest;
15 | import javax.servlet.http.HttpServletResponse;
16 | import javax.validation.Valid;
17 | import java.security.NoSuchAlgorithmException;
18 |
19 |
20 | /**
21 | * @Author: 公众号:Newtol
22 | * @Description:
23 | * @Date: Created in 18:51 2018/11/10
24 | */
25 | @Controller
26 | public class WeiXinBaseController {
27 |
28 | @Resource
29 | private WeiXinBaseService weiXinBaseService;
30 |
31 |
32 | /**
33 | * @Author: 公众号:Newtol
34 | * @Description: 接入微信服务器
35 | * @Date: Created in 20:47
36 | * @param: WeiXinBase 微信服务器传来的四个参数:signature,timestamp,nonce,echostr
37 | */
38 |
39 | @GetMapping(value = "/")
40 | @ResponseBody
41 | public String joinWeiXin(@Valid WeiXinVerify weiXinVerify){
42 | return weiXinBaseService.joinWeiXin(weiXinVerify);
43 | }
44 | /**
45 | * @Author: 公众号:Newtol
46 | * @Description: 接收微信服务器消息
47 | * @Date: Created in 22:41
48 | * @param:
49 | */
50 |
51 | @PostMapping(value = "/",consumes = "text/xml; charset=utf-8", produces = "text/xml; charset=utf-8")
52 | @ResponseBody
53 | public String getWeiXinMessage(@RequestBody WeiXinReceiveMessage weiXinReceiveMessage) throws JsonProcessingException {
54 | System.out.println(weiXinReceiveMessage.toString());
55 |
56 | return weiXinBaseService.sendAutoMessage(weiXinReceiveMessage);
57 | }
58 | /**
59 | * @Author: 公众号:Newtol
60 | * @Description: AccessToken获取
61 | * @Date: Created in 21:13
62 | * @param:
63 | */
64 |
65 | @GetMapping(value = "/accessToken")
66 | @ResponseBody
67 | public Result getAccessToken(@Valid WeiXinConfigInfo weiXinConfigInfo) throws Exception {
68 | return weiXinBaseService.getAccessToken(weiXinConfigInfo);
69 | }
70 |
71 | /**
72 | * @Author: 公众号:Newtol
73 | * @Description: 微信公众号菜单的设置
74 | * @Date: Created in 19:58
75 | * @param:
76 | */
77 | @GetMapping(value = "/menu")
78 | @ResponseBody
79 | public Result setMenu(@Valid WeiXinConfigInfo weiXinConfigInfo) throws Exception {
80 | return weiXinBaseService.setWeiXinMenu(weiXinConfigInfo);
81 | }
82 |
83 | /**
84 | * @Author: 公众号:Newtol
85 | * @Description: 获取网页授权
86 | * @Date: Created in 20:58
87 | * @param:
88 | */
89 | @GetMapping(value = "/getWebAuthorize")
90 | @ResponseBody
91 | public void getWeiXinWebAuthorize(@Valid WeiXinWebAuthorize weiXinWebAuthorize, HttpServletRequest request, HttpServletResponse response) throws Exception {
92 | weiXinBaseService.weiXinWebAuthorize(weiXinWebAuthorize,request,response);
93 | }
94 |
95 | /**
96 | * @Author: 公众号:Newtol
97 | * @Description: 获取跳转微信网页授权获取链接时需要使用的state
98 | * @Date: Created in 13:07
99 | * @param:
100 | */
101 | @GetMapping(value = "/setRedirectUrl")
102 | @ResponseBody
103 | public Result setRedirectUrl(@Valid RedirectUrlWeiXinConfig weiXinRedirectUrl) throws NoSuchAlgorithmException {
104 | return weiXinBaseService.setRedirect(weiXinRedirectUrl);
105 | }
106 |
107 | /**
108 | * @Author: 公众号:Newtol
109 | * @Description: 跳转到微信获取网页授权的链接地址
110 | * @Date: Created in 19:21
111 | * @param:
112 | */
113 | @GetMapping(value = "/redirectUrl")
114 | @ResponseBody
115 | public void redirectToWeiXin(@RequestParam String state, HttpServletResponse response){
116 | weiXinBaseService.redirectToWeiXin(response,state);
117 | }
118 |
119 | @RequestMapping(value = "/test", method = RequestMethod.POST, consumes = { "text/xml" }, produces = { "application/xml" })
120 | @ResponseBody
121 | public Result setSession(@RequestBody WeiXinSendAutoMessage weiXinSendAutoMessage) throws JsonProcessingException {
122 | System.out.println(weiXinSendAutoMessage.toString());
123 | XmlMapper xmlMapper = new XmlMapper();
124 | xmlMapper.setDefaultUseWrapper(false);
125 | //字段为null,自动忽略,不再序列化
126 | xmlMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
127 | String xml = xmlMapper.writeValueAsString(weiXinSendAutoMessage);
128 | System.out.println(xml);
129 | return ResultUtil.success();
130 | }
131 |
132 |
133 | /**
134 | * @Author: 公众号:Newtol
135 | * @Description: 设置自动回复内容
136 | * @Date: Created in 23:57
137 | * @param:
138 | */
139 | @PostMapping(value = "/setAutoMessage")
140 | @ResponseBody
141 | public Result setAutoMessage(@RequestBody WeiXinSendAutoMessage weiXinSendAutoMessage){
142 | return ResultUtil.success(weiXinBaseService.setAutoMessage(weiXinSendAutoMessage));
143 | }
144 | }
--------------------------------------------------------------------------------
/src/main/java/cn/newtol/weixin/domain/BaseDO.java:
--------------------------------------------------------------------------------
1 | package cn.newtol.weixin.domain;
2 |
3 | import lombok.Data;
4 | import org.springframework.data.annotation.CreatedDate;
5 | import org.springframework.data.annotation.LastModifiedDate;
6 | import org.springframework.data.jpa.domain.support.AuditingEntityListener;
7 | import org.springframework.stereotype.Component;
8 |
9 | import javax.persistence.*;
10 | import java.util.Date;
11 |
12 | /**
13 | * @Author: 公众号:Newtol
14 | * @Description:
15 | * @Date: Created in 1:16 2018/11/25
16 | */
17 | @Data
18 | @Component
19 | @MappedSuperclass
20 | @EntityListeners(AuditingEntityListener.class)
21 | public abstract class BaseDO {
22 | @Id
23 | @GeneratedValue(strategy = GenerationType.IDENTITY)
24 | public Integer id;
25 |
26 | @CreatedDate
27 | @Column(updatable = false)
28 | public Date createTime ;
29 | @LastModifiedDate
30 | @Column
31 | public Date updateTime ;
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/cn/newtol/weixin/domain/HttpClientResult.java:
--------------------------------------------------------------------------------
1 | package cn.newtol.weixin.domain;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @Author: 公众号:Newtol
7 | * @Description:
8 | * @Date: Created in 16:50 2018/11/10
9 | */
10 |
11 | @Data
12 | public class HttpClientResult{
13 | private static final long serialVersionUID = 2168152194164783950L;
14 | /**
15 | * 响应状态码
16 | */
17 | private int code;
18 |
19 | /**
20 | * 响应数据
21 | */
22 | private String content;
23 |
24 |
25 | public HttpClientResult(int code, String content) {
26 | this.code = code;
27 | this.content = content;
28 | }
29 |
30 | public HttpClientResult(int code) {
31 | this.code = code;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/cn/newtol/weixin/domain/Result.java:
--------------------------------------------------------------------------------
1 | package cn.newtol.weixin.domain;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @Author: 公众号:Newtol
7 | * @Description:
8 | * @Date: Created in 13:13 2018/11/10
9 | */
10 |
11 | @Data
12 | public class Result {
13 | /**
14 | * 错误码
15 | */
16 | private Integer errorCode;
17 |
18 | /**
19 | * 错误提示信息
20 | */
21 | private String message;
22 |
23 | /**
24 | * 数据块
25 | */
26 | private T data;
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/cn/newtol/weixin/domain/WeiXinSendAutoMessage.java:
--------------------------------------------------------------------------------
1 | package cn.newtol.weixin.domain;
2 |
3 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
4 |
5 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
6 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlText;
7 | import lombok.Data;
8 | import org.springframework.stereotype.Component;
9 |
10 | import javax.validation.constraints.NotEmpty;
11 |
12 | /**
13 | * @Author: 公众号:Newtol
14 | * @Description:
15 | * @Date: Created in 19:26 2018/11/27
16 | */
17 | @Data
18 | @Component
19 | @JacksonXmlRootElement(localName = "xml")
20 | public class WeiXinSendAutoMessage {
21 | /**
22 | * 接收者Id
23 | */
24 | @JacksonXmlProperty(localName = "ToUserName")
25 | private String toUserName;
26 | /**
27 | * 发送者Id
28 | */
29 | @NotEmpty(message = "发送者不能为空")
30 | @JacksonXmlProperty(localName = "FromUserName")
31 | private String fromUserName;
32 | /**
33 | * 消息创建时间
34 | */
35 | @JacksonXmlProperty(localName = "CreateTime")
36 | private Long createTime;
37 | /**
38 | * 消息类型
39 | * 文本为:text; 图片为:image; 语音为:voice;
40 | *
41 | */
42 | @NotEmpty(message = "文章类型不能为空")
43 | @JacksonXmlProperty(localName = "MsgType")
44 | private String msgType;
45 |
46 | /**
47 | * 回复的关键词
48 | */
49 | @JacksonXmlText
50 | private String key;
51 |
52 | /**
53 | * 多媒体Id,回复图片、语音时使用
54 | */
55 |
56 | @Data
57 | public static class MediaId {
58 | @JacksonXmlText(value =false)
59 | @JacksonXmlProperty(localName = "MediaId")
60 | private String mediaId;
61 | public MediaId(String mediaId) {
62 | this.mediaId = mediaId;
63 | }
64 | public MediaId() {
65 | }
66 | }
67 |
68 | /**
69 | * 自动回复图片消息时使用
70 | */
71 | @JacksonXmlProperty(localName = "Image")
72 | private MediaId image;
73 |
74 | /**
75 | * 回复语音消息时使用
76 | */
77 | @JacksonXmlProperty(localName = "Voice")
78 | private MediaId voice;
79 |
80 | /**
81 | * 回复文本消息
82 | */
83 | @JacksonXmlProperty(localName = "Content")
84 | private String Content;
85 |
86 |
87 | /**
88 | * 回复视频消息
89 | */
90 | @Data
91 | public static class VideoContent {
92 | @JacksonXmlText(value =false)
93 | @JacksonXmlProperty(localName = "MediaId")
94 | private String mediaId;
95 | @JacksonXmlText(value =false)
96 | @JacksonXmlProperty(localName = "Title")
97 | private String title;
98 | @JacksonXmlText(value =false)
99 | @JacksonXmlProperty(localName = "Description")
100 | private String description;
101 | }
102 | @JacksonXmlProperty(localName = "Video")
103 | private VideoContent video;
104 |
105 | /**
106 | * 回复音乐消息
107 | */
108 |
109 | @Data
110 | public static class MusicContent{
111 | public MusicContent() {
112 | }
113 |
114 | /**
115 | * 音乐标题
116 | */
117 | @JacksonXmlText(value =false)
118 | @JacksonXmlProperty(localName = "Title")
119 | private String title;
120 | /**
121 | * 音乐描述
122 | */
123 | @JacksonXmlText(value =false)
124 | @JacksonXmlProperty(localName = "Description")
125 | private String description ;
126 | /**
127 | * 音乐链接
128 | */
129 | @JacksonXmlText(value =false)
130 | @JacksonXmlProperty(localName = "MusicUrl")
131 | private String musicUrl ;
132 | /**
133 | * 高质量音乐链接,WIFI环境优先使用该链接播放音乐
134 | */
135 | @JacksonXmlText(value =false)
136 | @JacksonXmlProperty(localName = "HQMusicUrl")
137 | private String hqMusicUrl;
138 | /**
139 | * 缩略图的媒体id,通过素材管理中的接口上传多媒体文件,得到的id
140 | */
141 | @JacksonXmlText(value =false)
142 | @JacksonXmlProperty(localName ="ThumbMediaId" )
143 | private String thumbMediaId;
144 | }
145 |
146 | @JacksonXmlProperty(localName = "Music")
147 | private MusicContent music;
148 |
149 |
150 | /**
151 | * 回复图文消息
152 | */
153 | @Data
154 | public static class PicAndContent {
155 | public PicAndContent() {
156 | }
157 |
158 | /**
159 | * 图文消息个数;当用户发送文本、图片、视频、图文、地理位置这五种消息时,开发者只能回复1条图文消息;其余场景最多可回复8条图文消息
160 | */
161 | @JacksonXmlText(value =false)
162 | @JacksonXmlProperty(localName ="ArticleCount" )
163 | private Integer articleCount;
164 | /**
165 | * 图文消息信息,注意,如果图文数超过限制,则将只发限制内的条数
166 | */
167 | @JacksonXmlText(value =false)
168 | @JacksonXmlProperty(localName ="Articles" )
169 | private String articles;
170 | /**
171 | * 图文消息标题
172 | */
173 | @JacksonXmlText(value =false)
174 | @JacksonXmlProperty(localName ="Title" )
175 | private String title;
176 | /**
177 | * 图文消息描述
178 | */
179 | @JacksonXmlText(value =false)
180 | @JacksonXmlProperty(localName ="Description" )
181 | private String description ;
182 | /**
183 | * 图片链接,支持JPG、PNG格式,较好的效果为大图360*200,小图200*200
184 | */
185 | @JacksonXmlText(value =false)
186 | @JacksonXmlProperty(localName ="PicUrl" )
187 | private String picUrl;
188 | /**
189 | * 图文跳转链接
190 | */
191 | @JacksonXmlText(value =false)
192 | @JacksonXmlProperty(localName ="Url" )
193 | private String url;
194 | }
195 |
196 | @JacksonXmlProperty(localName = "Articles")
197 | private String articles;
198 |
199 | }
200 |
--------------------------------------------------------------------------------
/src/main/java/cn/newtol/weixin/domain/WeiXinUserInfo.java:
--------------------------------------------------------------------------------
1 | package cn.newtol.weixin.domain;
2 |
3 | import lombok.Data;
4 | import org.springframework.stereotype.Component;
5 | import javax.persistence.*;
6 | import java.io.Serializable;
7 |
8 |
9 | /**
10 | * @Author: 公众号:Newtol
11 | * @Description:
12 | * @Date: Created in 22:09 2018/11/12
13 | */
14 | @Component
15 | @Data
16 | @Entity
17 | @Table(name = "weixin_userinfo")
18 | public class WeiXinUserInfo extends BaseDO implements Serializable {
19 | @Column(nullable = false,unique = true)
20 | private String openId;
21 | @Column(nullable = false)
22 | private String nickName;
23 | @Column
24 | private String city;
25 | @Column
26 | private Integer sex;
27 | @Column(nullable = false)
28 | private String headImgUrl;
29 | @Column
30 | private String province;
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/cn/newtol/weixin/domain/dto/BaseWeiXinMessage.java:
--------------------------------------------------------------------------------
1 | package cn.newtol.weixin.domain.dto;
2 |
3 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
4 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
5 | import lombok.Data;
6 | import org.springframework.stereotype.Component;
7 |
8 | import javax.validation.constraints.NotEmpty;
9 |
10 | /**
11 | * @Author: 公众号:Newtol
12 | * @Description:
13 | * @Date: Created in 11:01 2018/11/29
14 | */
15 |
16 | @Data
17 | @Component
18 | public abstract class BaseWeiXinMessage {
19 | /**
20 | * 接收者Id
21 | */
22 | @JacksonXmlProperty(localName = "ToUserName")
23 | private String toUserName;
24 | /**
25 | * 发送者Id
26 | */
27 | @NotEmpty(message = "发送者不能为空")
28 | @JacksonXmlProperty(localName = "FromUserName")
29 | private String fromUserName;
30 | /**
31 | * 消息创建时间
32 | */
33 | @JacksonXmlProperty(localName = "CreateTime")
34 | private Long createTime;
35 | /**
36 | * 消息类型
37 | * 文本为:text; 图片为:image; 语音为:voice;
38 | *
39 | */
40 | @NotEmpty(message = "文章类型不能为空")
41 | @JacksonXmlProperty(localName = "MsgType")
42 | private String msgType;
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/cn/newtol/weixin/domain/dto/RedirectUrlWeiXinConfig.java:
--------------------------------------------------------------------------------
1 | package cn.newtol.weixin.domain.dto;
2 |
3 | import lombok.Data;
4 | import org.springframework.stereotype.Component;
5 |
6 | import javax.validation.constraints.NotEmpty;
7 |
8 | /**
9 | * @Author: 公众号:Newtol
10 | * @Description:
11 | * @Date: Created in 13:13 2018/11/24
12 | */
13 | @Data
14 | @Component
15 | public class RedirectUrlWeiXinConfig extends WeiXinConfigInfo {
16 |
17 | /** 获取网页授权时,需要跳转的链接 */
18 | @NotEmpty(message = "跳转链接不能为空")
19 | private String redirectUrl;
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/cn/newtol/weixin/domain/dto/WeiXinConfigInfo.java:
--------------------------------------------------------------------------------
1 | package cn.newtol.weixin.domain.dto;
2 |
3 | import lombok.Data;
4 | import org.springframework.stereotype.Component;
5 |
6 |
7 | import javax.validation.constraints.NotEmpty;
8 |
9 |
10 | /**
11 | * @Author: 公众号:Newtol
12 | * @Description: 微信公众号的基本信息:appId和appSecret
13 | * @Date: Created in 13:03 2018/11/12
14 | */
15 | @Data
16 | @Component
17 | public class WeiXinConfigInfo {
18 | /** 公众号的appId*/
19 | @NotEmpty(message = "appId不能为空" )
20 | public String appId;
21 |
22 | /** 公众号的appSecret*/
23 | @NotEmpty(message = "appSecret不能为空")
24 | public String appSecret;
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/cn/newtol/weixin/domain/dto/WeiXinMenu.java:
--------------------------------------------------------------------------------
1 | package cn.newtol.weixin.domain.dto;
2 |
3 | import lombok.Data;
4 | import org.springframework.boot.context.properties.ConfigurationProperties;
5 | import org.springframework.stereotype.Component;
6 |
7 | /**
8 | * @Author: 公众号:Newtol
9 | * @Description: 菜单
10 | * @Date: Created in 0:17 2018/11/11
11 | */
12 | @Data
13 | @Component
14 | @ConfigurationProperties(prefix= "menu")
15 | public class WeiXinMenu {
16 | /** 菜单 */
17 | private SubButton[] button;
18 |
19 |
20 | @Data
21 | public static class SubButton {
22 | /** 二级菜单 */
23 | private Button[] sub_button;
24 | /** 二级菜单的名字 */
25 | private String name;
26 | }
27 |
28 | @Data
29 | public static class Button {
30 | /** 按钮的种类 */
31 | private String type;
32 | /** 按钮的名字 */
33 | private String name;
34 | /** 当类型为view类型时,跳转的链接*/
35 | private String url;
36 | /** 当类型为小程序时,小程序的appId*/
37 | private String appId;
38 | /** 当类型为小程序时使用*/
39 | private String pagePath;
40 | /** 当类型为click时,发送给后台的key*/
41 | private String key;
42 |
43 | }
44 |
45 |
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/cn/newtol/weixin/domain/dto/WeiXinReceiveMessage.java:
--------------------------------------------------------------------------------
1 | package cn.newtol.weixin.domain.dto;
2 |
3 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
4 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
5 | import lombok.Data;
6 |
7 | /**
8 | * @Author: 公众号:Newtol
9 | * @Description:
10 | * @Date: Created in 22:44 2018/11/25
11 | */
12 | @Data
13 | @JacksonXmlRootElement(localName = "xml")
14 | public class WeiXinReceiveMessage extends BaseWeiXinMessage{
15 | /**
16 | * 文本消息内容
17 | */
18 | @JacksonXmlProperty(localName = "Content")
19 | private String content;
20 | /**
21 | * 消息ID
22 | */
23 | @JacksonXmlProperty(localName = "MsgId")
24 | private Long msgId;
25 | /**
26 | * 图片链接
27 | */
28 | @JacksonXmlProperty(localName = "PicUrl")
29 | private String picUrl;
30 | /**
31 | * 图片消息媒体id,可以调用多媒体文件下载接口拉取数据
32 | */
33 | @JacksonXmlProperty(localName ="MediaId" )
34 | private String mediaId;
35 | /**
36 | * 语音格式
37 | */
38 | @JacksonXmlProperty(localName = "Format")
39 | private String format;
40 | /**
41 | * 语音识别结果
42 | */
43 | @JacksonXmlProperty(localName = "Recognition")
44 | private String recognition;
45 | /**
46 | * 视频消息缩略图的媒体id,可以调用多媒体文件下载接口拉取数据。
47 | */
48 | @JacksonXmlProperty(localName = "ThumbMediaId")
49 | private String thumbMediaId;
50 | /**
51 | * 地理位置维度
52 | */
53 | @JacksonXmlProperty(localName = "Location_X")
54 | private String location_X;
55 | /**
56 | * 地理位置经度
57 | */
58 | @JacksonXmlProperty(localName = "Location_Y")
59 | private String location_Y;
60 | /**
61 | * 地图缩放大小
62 | */
63 | @JacksonXmlProperty(localName = "Scale")
64 | private String scale;
65 | /**
66 | * 地理位置信息
67 | */
68 | @JacksonXmlProperty(localName = "Label")
69 | private String label;
70 | /**
71 | * 消息标题
72 | */
73 | @JacksonXmlProperty(localName = "Title")
74 | private String title;
75 |
76 | /**
77 | * 消息描述
78 | */
79 | @JacksonXmlProperty(localName = "Description")
80 | private String description;
81 |
82 | /**
83 | * 消息链接
84 | */
85 | @JacksonXmlProperty(localName = "Url")
86 | private String url;
87 | }
88 |
--------------------------------------------------------------------------------
/src/main/java/cn/newtol/weixin/domain/dto/WeiXinUserInfoOpenId.java:
--------------------------------------------------------------------------------
1 | package cn.newtol.weixin.domain.dto;
2 |
3 | import lombok.Data;
4 | import org.springframework.stereotype.Component;
5 |
6 | /**
7 | * @Author: 公众号:Newtol
8 | * @Description:
9 | * @Date: Created in 14:42 2018/11/24
10 | */
11 | @Data
12 | @Component
13 | public class WeiXinUserInfoOpenId {
14 | /** snsapi_userinfo为scope时,获取用户信息使用的access_token */
15 | private String accessToken;
16 | /** 用户的openId */
17 | private String openId;
18 | /** 用来刷新用户access_token使用 */
19 | private String refreshToken;
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/cn/newtol/weixin/domain/dto/WeiXinVerify.java:
--------------------------------------------------------------------------------
1 | package cn.newtol.weixin.domain.dto;
2 |
3 | import lombok.Data;
4 |
5 |
6 | import org.springframework.stereotype.Component;
7 |
8 | import javax.validation.constraints.NotEmpty;
9 |
10 |
11 | /**
12 | * @Author: 公众号:Newtol
13 | * @Description: 接入微信服务器时,微信服务器传过来的参数
14 | * @Date: Created in 19:08 2018/11/10
15 | */
16 | @Data
17 | @Component
18 | public class WeiXinVerify {
19 | @NotEmpty(message = "微信接入缺少参数")
20 | private String signature;
21 | @NotEmpty(message = "微信接入缺少参数")
22 | private String timestamp;
23 | @NotEmpty(message = "微信接入缺少参数")
24 | private String nonce;
25 | @NotEmpty(message = "微信接入缺少参数")
26 | private String echostr;
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/cn/newtol/weixin/domain/dto/WeiXinWebAuthorize.java:
--------------------------------------------------------------------------------
1 | package cn.newtol.weixin.domain.dto;
2 |
3 | import lombok.Data;
4 | import org.springframework.stereotype.Component;
5 |
6 | import javax.validation.constraints.NotEmpty;
7 |
8 | /**
9 | * @Author: 公众号:Newtol
10 | * @Description: 获取网页授权时,需要的code和state
11 | * @Date: Created in 21:13 2018/11/22
12 | */
13 | @Component
14 | @Data
15 | public class WeiXinWebAuthorize {
16 | @NotEmpty(message = "code不能为空")
17 | private String code;
18 | @NotEmpty(message = "state不能为空")
19 | private String state;
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/cn/newtol/weixin/exceptions/TestException.java:
--------------------------------------------------------------------------------
1 | package cn.newtol.weixin.exceptions;
2 |
3 | import cn.newtol.weixin.Enum.ResultEnum;
4 | import lombok.Data;
5 |
6 | /**
7 | * @Author: 公众号:Newtol
8 | * @Description:
9 | * @Date: Created in 13:35 2018/11/10
10 | */
11 | @Data
12 | public class TestException extends RuntimeException {
13 | private Integer errorCode;
14 |
15 | public TestException(ResultEnum resultEnum) {
16 | super(resultEnum.getMessage());
17 | this.errorCode = resultEnum.getErrorCode();
18 | }
19 |
20 | public Integer getErrorCode() {
21 | return errorCode;
22 | }
23 |
24 | public void setErrorCode(Integer errorCode) {
25 | this.errorCode = errorCode;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/cn/newtol/weixin/handle/ExceptionHandle.java:
--------------------------------------------------------------------------------
1 | package cn.newtol.weixin.handle;
2 |
3 | import cn.newtol.weixin.Enum.ResultEnum;
4 | import cn.newtol.weixin.domain.Result;
5 | import cn.newtol.weixin.exceptions.TestException;
6 | import cn.newtol.weixin.utils.ResultUtil;
7 | import com.fasterxml.jackson.core.JsonProcessingException;
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 | import org.springframework.validation.BindException;
11 | import org.springframework.web.bind.annotation.ControllerAdvice;
12 | import org.springframework.web.bind.annotation.ExceptionHandler;
13 | import org.springframework.web.bind.annotation.ResponseBody;
14 |
15 | /**
16 | * @Author: 公众号:Newtol
17 | * @Description:
18 | * @Date: Created in 13:55 2018/11/10
19 | */
20 |
21 | @ControllerAdvice
22 | public class ExceptionHandle {
23 |
24 |
25 | private static final Logger logger = LoggerFactory.getLogger(ExceptionHandle.class);
26 | /**
27 | * @Author: 公众号:Newtol
28 | * @Description: 拦截一些自定义的警告
29 | * @Date: Created in 0:07
30 | * @param:
31 | */
32 | @ExceptionHandler(TestException.class)
33 | @ResponseBody
34 | public Result testExceptionHandler(TestException e){
35 | return ResultUtil.error(e.getErrorCode(),e.getMessage());
36 | }
37 |
38 | /**
39 | * @Author: 公众号:Newtol
40 | * @Description: 拦截参数验证错误
41 | * @Date: Created in 0:07
42 | * @param:
43 | */
44 | @ExceptionHandler(BindException.class)
45 | @ResponseBody
46 | public Result validExceptionHandler(BindException e){
47 | String msg = e.getBindingResult().getFieldError().getDefaultMessage();
48 | logger.info("参数验证错误:"+msg);
49 | return ResultUtil.error(ResultEnum.LACK_PARAMETER.getErrorCode(),msg);
50 | }
51 |
52 | /**
53 | * @Author: 公众号:Newtol
54 | * @Description: 系统未知错误拦截
55 | * @Date: Created in 0:15
56 | * @param:
57 | */
58 | @ExceptionHandler(Exception.class)
59 | @ResponseBody
60 | public Result systemExceptionHandler(Exception e){
61 | logger.error("系统未知错误:"+e.getMessage());
62 | return ResultUtil.error(ResultEnum.UNKONW_ERROR);
63 | }
64 |
65 | /**
66 | * @Author: 公众号:Newtol
67 | * @Description: XML序列化失败
68 | * @Date: Created in 23:19
69 | * @param:
70 | */
71 | @ExceptionHandler(JsonProcessingException.class)
72 | @ResponseBody
73 | public void jsonProcessingExceptionHandler (Exception e){
74 | logger.error("XML序列化失败"+e.getMessage());
75 | }
76 |
77 |
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/src/main/java/cn/newtol/weixin/repository/BaseUserInfoRepository.java:
--------------------------------------------------------------------------------
1 | package cn.newtol.weixin.repository;
2 |
3 | import cn.newtol.weixin.domain.WeiXinUserInfo;
4 | import org.springframework.data.jpa.repository.JpaRepository;
5 |
6 | /**
7 | * @Author: 公众号:Newtol
8 | * @Description:
9 | * @Date: Created in 16:31 2018/11/24
10 | */
11 | public interface BaseUserInfoRepository extends JpaRepository {
12 | /**
13 | * @Author: 公众号:Newtol
14 | * @Description: 通过openId查找用户
15 | * @Date: Created in 20:56
16 | * @param:
17 | */
18 | WeiXinUserInfo findBaseUserInfoByOpenId(String openId);
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/cn/newtol/weixin/service/WeiXinBaseService.java:
--------------------------------------------------------------------------------
1 | package cn.newtol.weixin.service;
2 |
3 | import cn.newtol.weixin.domain.Result;
4 | import cn.newtol.weixin.domain.WeiXinSendAutoMessage;
5 | import cn.newtol.weixin.domain.dto.*;
6 | import com.fasterxml.jackson.core.JsonProcessingException;
7 | import org.springframework.stereotype.Service;
8 |
9 | import javax.servlet.http.HttpServletRequest;
10 | import javax.servlet.http.HttpServletResponse;
11 | import java.security.NoSuchAlgorithmException;
12 |
13 | /**
14 | * @Author: 公众号:Newtol
15 | * @Description:
16 | * @Date: Created in 19:21 2018/11/10
17 | */
18 | @Service
19 | public interface WeiXinBaseService {
20 |
21 | /**
22 | * @Author: 公众号:Newtol
23 | * @Description: 接入微信服务器
24 | * @Date: Created in 19:38
25 | * @param:
26 | */
27 | String joinWeiXin(WeiXinVerify weiXinVerify);
28 |
29 | /**
30 | * @Author: 公众号:Newtol
31 | * @Description: 获取access_token
32 | * @Date: Created in 19:38
33 | * @param:
34 | */
35 | Result getAccessToken(WeiXinConfigInfo weiXinConfigInfo) throws Exception;
36 |
37 | /**
38 | * @Author: 公众号:Newtol
39 | * @Description: 设置微信公众号的后台
40 | * @Date: Created in 19:49
41 | * @param:
42 | */
43 | Result setWeiXinMenu(WeiXinConfigInfo weiXinConfigInfo) throws Exception;
44 |
45 | /**
46 | * @Author: 公众号:Newtol
47 | * @Description: 微信获取网页授权
48 | * @Date: Created in 22:15
49 | * @param:
50 | */
51 | void weiXinWebAuthorize(WeiXinWebAuthorize weiXinWebAuthorize, HttpServletRequest request, HttpServletResponse response) throws Exception;
52 |
53 | /**
54 | * @Author: 公众号:Newtol
55 | * @Description: 网页授权时,获取code时,使用的state
56 | * @Date: Created in 13:16
57 | * @param:
58 | */
59 | Result setRedirect(RedirectUrlWeiXinConfig weiXinRedirectUrl) throws NoSuchAlgorithmException;
60 |
61 | /**
62 | * @Author: 公众号:Newtol
63 | * @Description: 跳转到微信网页授权链接
64 | * @Date: Created in 19:51
65 | * @param:
66 | */
67 | void redirectToWeiXin(HttpServletResponse response,String state);
68 |
69 | /**
70 | * @Author: 公众号:Newtol
71 | * @Description: 微信自动回复关键词
72 | * @Date: Created in 22:44
73 | * @param:
74 | */
75 | String sendAutoMessage(WeiXinReceiveMessage weiXinReceiveMessage) throws JsonProcessingException;
76 |
77 | /**
78 | * @Author: 公众号:Newtol
79 | * @Description: 设置自动回复内容
80 | * @Date: Created in 23:58
81 | * @param:
82 | */
83 | Result setAutoMessage(WeiXinSendAutoMessage weiXinSendAutoMessage);
84 | }
85 |
--------------------------------------------------------------------------------
/src/main/java/cn/newtol/weixin/service/WeiXinBaseServiceImpl.java:
--------------------------------------------------------------------------------
1 | package cn.newtol.weixin.service;
2 |
3 | import cn.newtol.weixin.Enum.ResultEnum;
4 | import cn.newtol.weixin.domain.WeiXinSendAutoMessage;
5 | import cn.newtol.weixin.domain.WeiXinUserInfo;
6 | import cn.newtol.weixin.domain.HttpClientResult;
7 | import cn.newtol.weixin.domain.Result;
8 | import cn.newtol.weixin.domain.dto.*;
9 | import cn.newtol.weixin.repository.BaseUserInfoRepository;
10 | import cn.newtol.weixin.utils.*;
11 | import com.alibaba.fastjson.JSON;
12 | import com.alibaba.fastjson.JSONObject;
13 | import com.fasterxml.jackson.annotation.JsonInclude;
14 | import com.fasterxml.jackson.core.JsonProcessingException;
15 | import com.fasterxml.jackson.dataformat.xml.XmlMapper;
16 | import org.springframework.beans.factory.annotation.Autowired;
17 | import org.springframework.beans.factory.annotation.Value;
18 | import org.slf4j.Logger;
19 | import org.slf4j.LoggerFactory;
20 | import org.springframework.stereotype.Component;
21 | import org.springframework.stereotype.Service;
22 | import javax.annotation.Resource;
23 | import javax.servlet.http.HttpServletRequest;
24 | import javax.servlet.http.HttpServletResponse;
25 | import java.security.NoSuchAlgorithmException;
26 | import java.util.HashMap;
27 | import java.util.Map;
28 |
29 | /**
30 | * @Author: 公众号:Newtol
31 | * @Description:
32 | * @Date: Created in 19:23 2018/11/10
33 | */
34 | @Service
35 | @Component
36 | public class WeiXinBaseServiceImpl implements WeiXinBaseService {
37 |
38 | private static final Logger logger = LoggerFactory.getLogger(WeiXinBaseServiceImpl.class);
39 |
40 | @Resource
41 | private WeiXinUtil weiXinUtil;
42 |
43 | @Autowired
44 | BaseUserInfoRepository baseUserInfoRepository;
45 |
46 | @Autowired
47 | RedisUtil redisUtil;
48 |
49 | @Autowired
50 | HttpServletUtil httpServletUtil;
51 |
52 | @Autowired
53 | JacksonUtil jacksonUtil;
54 |
55 | /**
56 | * 从配置文件中获取redis中AccessToken存入的key
57 | */
58 | @Value("${weiXin.accessTokenKey}")
59 | private String accessTokenKey;
60 |
61 | /**
62 | * 微信服务器配置的token
63 | */
64 | @Value("${weiXin.token}")
65 | private String token;
66 |
67 | /**
68 | * RedirectUrl链接的key
69 | */
70 | @Value("${weiXin.redirectUrlKey}")
71 | private String redirectUrlKey;
72 |
73 | /**
74 | * 跳转到本地的网页授权页面
75 | */
76 | @Value("${weiXin.getWebAuthorize}")
77 | private String getWebAuthorizeUrl;
78 |
79 | /**
80 | * 微信公众号菜单
81 | */
82 | @Resource
83 | private WeiXinMenu weiXinMenu;
84 |
85 | @Override
86 | public String joinWeiXin(WeiXinVerify weiXinVerify) {
87 | return weiXinUtil.joinWeiXin(weiXinVerify,token);
88 | }
89 |
90 | @Override
91 | public Result getAccessToken(WeiXinConfigInfo weiXinConfigInfo) throws Exception {
92 | String access_token = weiXinUtil.getAccessToken(weiXinConfigInfo.getAppId(), weiXinConfigInfo.getAppSecret(), accessTokenKey);
93 | Map map = new HashMap<>(1);
94 | map.put("access_token",access_token);
95 | return ResultUtil.success(map);
96 | }
97 |
98 | /**
99 | * @param weiXinConfigInfo
100 | * @Author: 公众号:Newtol
101 | * @Description: 设置微信公众号的菜单
102 | * @Date: Created in 19:49
103 | */
104 | @Override
105 | public Result setWeiXinMenu(WeiXinConfigInfo weiXinConfigInfo) throws Exception {
106 | String access_token = weiXinUtil.getAccessToken(weiXinConfigInfo.getAppId(), weiXinConfigInfo.getAppSecret(), accessTokenKey);
107 | if (access_token == null ||"".equals(access_token)){
108 | logger.error("请求菜单时access_token为空:"+ weiXinConfigInfo);
109 | return ResultUtil.error(ResultEnum.ERROR_WEIXINBASEINFO);
110 | }
111 | /* 封装url,参数,Json数据*/
112 | String url = "https://api.weixin.qq.com/cgi-bin/menu/create";
113 | Map map = new HashMap<>(1);
114 | map.put("access_token",access_token);
115 | String jsonData = JSON.toJSONString(weiXinMenu);
116 | HttpClientResult httpClientResult = HttpClientUtil.doPostForJson(url,map,jsonData);
117 | /* 获取返回结果*/
118 | Result result = ResultUtil.success(httpClientResult);
119 | logger.info("微信公众号菜单设置请求的JSON参数:"+jsonData);
120 | return result;
121 | }
122 |
123 | /**
124 | * @param weiXinWebAuthorize
125 | * @Author: 公众号:Newtol
126 | * @Description: 获取网页授权时,用于获取用户openId
127 | * @Date: Created in 21:16
128 | * @param:
129 | */
130 | @Override
131 | public void weiXinWebAuthorize(WeiXinWebAuthorize weiXinWebAuthorize, HttpServletRequest request, HttpServletResponse response) throws Exception {
132 | /* 从redis中获取appId,appSecret,redirectUrl等信息*/
133 | String stateInfo = redisUtil.getHash(redirectUrlKey,weiXinWebAuthorize.getState());
134 | RedirectUrlWeiXinConfig weiXinRedirectUrl = JSONObject.parseObject(stateInfo, RedirectUrlWeiXinConfig.class);
135 | String redirectUrl = weiXinRedirectUrl.getRedirectUrl();
136 |
137 | /* 获取openId等信息 */
138 | WeiXinUserInfoOpenId weiXinUserInfoOpenId = getUserOpenId(weiXinRedirectUrl.getAppId(),weiXinRedirectUrl.getAppSecret(),weiXinWebAuthorize.getCode());
139 |
140 | /* 获取公众号的access_token信息 */
141 | String accessToken = weiXinUtil.getAccessToken(weiXinRedirectUrl.getAppId(),weiXinRedirectUrl.getAppSecret(), accessTokenKey);
142 |
143 | /* 获取用户信息 */
144 | WeiXinUserInfo weiXinUserInfo = weiXinUtil.getUserInfo(accessToken, weiXinUserInfoOpenId.getOpenId());
145 |
146 | // WeiXinUserInfo weiXinUserInfo = getWeiXinNoSubUserInfo(weiXinUserInfoOpenId);
147 |
148 | /* 看数据库是否存在,存在则更新,不存在就插入*/
149 | WeiXinUserInfo weiXinUserInfoFromDatabase = baseUserInfoRepository.findBaseUserInfoByOpenId(weiXinUserInfoOpenId.getOpenId());
150 | if (weiXinUserInfoFromDatabase != null || "".equals(weiXinUserInfoFromDatabase)){
151 | weiXinUserInfo.setId(weiXinUserInfoFromDatabase.getId());
152 | }
153 |
154 | /* 保存数据库*/
155 | baseUserInfoRepository.save(weiXinUserInfo);
156 | /* 保存Session */
157 | Map map = new HashMap<>(2);
158 | map.put("User",weiXinUserInfo);
159 | map.put("stateInfo",stateInfo);
160 | httpServletUtil.setSession(map,request);
161 | /* 重定向到业务页面*/
162 | httpServletUtil.redirectToUrl(response,redirectUrl);
163 | }
164 |
165 | /**
166 | * @Author: 公众号:Newtol
167 | * @Description: 网页授权时,用户授权获取用户信息
168 | * @Date: Created in 23:42
169 | * @param:
170 | */
171 | public WeiXinUserInfo getWeiXinNoSubUserInfo(WeiXinUserInfoOpenId weiXinUserInfoOpenId) throws Exception {
172 | String url = "https://api.weixin.qq.com/sns/userinfo";
173 | Map map = new HashMap<>(3);
174 | map.put("access_token",weiXinUserInfoOpenId.getAccessToken());
175 | map.put("openid",weiXinUserInfoOpenId.getOpenId());
176 | map.put("lang","zh_CN");
177 | HttpClientResult httpClientResult = HttpClientUtil.doGet(url,map);
178 | System.out.println(httpClientResult.getContent());
179 | WeiXinUserInfo weiXinUserInfo = JSONObject.parseObject(httpClientResult.getContent(), WeiXinUserInfo.class);
180 | if (weiXinUserInfo ==null || "".equals(weiXinUserInfo)){
181 | logger.info("获取用户信息失败:"+httpClientResult.getContent()+"==="+"openid"+weiXinUserInfoOpenId.getOpenId()+"==="+"access_token"+weiXinUserInfoOpenId.getAccessToken());
182 | }
183 | return weiXinUserInfo;
184 | }
185 | /**
186 | * @Author: 公众号:Newtol
187 | * @Description: 网页授权时获取用户的openId
188 | * @Date: Created in 15:56
189 | * @param:
190 | */
191 | private WeiXinUserInfoOpenId getUserOpenId(String appId, String appSecret, String code) throws Exception {
192 | String url = "https://api.weixin.qq.com/sns/oauth2/access_token";
193 | /* 封装请求的参数*/
194 | Map map = new HashMap<>(4);
195 | map.put("appid",appId);
196 | map.put("secret",appSecret);
197 | map.put("code",code);
198 | map.put("grant_type","authorization_code");
199 | /* 获取请求结果 */
200 | HttpClientResult httpClientResult = HttpClientUtil.doGet(url,map);
201 | return JSONObject.parseObject(httpClientResult.getContent(), WeiXinUserInfoOpenId.class);
202 | }
203 |
204 | /**
205 | * @param weiXinRedirectUrl
206 | * @Author: 公众号:Newtol
207 | * @Description: 网页授权时,设置跳转的链接,即为返回获取code时,使用的state
208 | * @Date: Created in 13:16
209 | * @param:
210 | */
211 | @Override
212 | public Result setRedirect(RedirectUrlWeiXinConfig weiXinRedirectUrl) throws NoSuchAlgorithmException {
213 | String state = EncryptUtil.md5(weiXinRedirectUrl.toString());
214 | String redirectUrl = JSONObject.toJSONString(weiXinRedirectUrl);
215 | redisUtil.setHash(redirectUrlKey,state,redirectUrl);
216 | return ResultUtil.success(state);
217 | }
218 |
219 | /**
220 | * @param response
221 | * @param state
222 | * @Author: 公众号:Newtol
223 | * @Description: 跳转到微信网页授权链接
224 | * @Date: Created in 19:51
225 | * @param:
226 | */
227 | @Override
228 | public void redirectToWeiXin(HttpServletResponse response, String state) {
229 | String stateInfo = redisUtil.getHash(redirectUrlKey,state);
230 | RedirectUrlWeiXinConfig weiXinRedirectUrl = JSONObject.parseObject(stateInfo, RedirectUrlWeiXinConfig.class);
231 | String url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid="+weiXinRedirectUrl.getAppId()+"&redirect_uri="+getWebAuthorizeUrl+"&response_type=code&scope=snsapi_base&state="+state+"#wechat_redirect";
232 | httpServletUtil.redirectToUrl(response,url);
233 | }
234 |
235 | /**
236 | * @param weiXinReceiveMessage
237 | * @Author: 公众号:Newtol
238 | * @Description: 微信自动回复关键词
239 | * @Date: Created in 22:44
240 | * @param:
241 | */
242 | @Override
243 | public String sendAutoMessage(WeiXinReceiveMessage weiXinReceiveMessage) throws JsonProcessingException {
244 | /* 返回结果*/
245 | String result = null;
246 | /* 获得公众号的id*/
247 | String appId = weiXinReceiveMessage.getToUserName();
248 | /* 获得关键词*/
249 | String content = weiXinReceiveMessage.getContent();
250 | /* 如果查找到关键词就根据关键词回复,否则调用客服接口*/
251 | if (content == null ) {
252 | //调用客服接口
253 | return result;
254 | }
255 | /* 获得需要回复的内容,为空则调用客服接口*/
256 | String str = redisUtil.getHash(appId,content);
257 | if (str == null || "".equals(str)){
258 | //调用客服接口
259 | return result;
260 | }
261 |
262 | WeiXinSendAutoMessage weiXinSendAutoMessage = JSONObject.parseObject(str,WeiXinSendAutoMessage.class);
263 | /* 设置接收的用户openId*/
264 | String openId = weiXinReceiveMessage.getFromUserName();
265 | weiXinSendAutoMessage.setToUserName(openId);
266 | /* 设置时间*/
267 | Long createTime = System.currentTimeMillis()/1000;
268 | weiXinSendAutoMessage.setCreateTime(createTime);
269 | /* 将数据转换为xml格式*/
270 | result = jacksonUtil.getXmlFromBean(weiXinSendAutoMessage);
271 | System.out.println("re:"+result);
272 | return result;
273 | }
274 |
275 | /**
276 | * @param
277 | * @Author: 公众号:Newtol
278 | * @Description: 设置自动回复图片内容
279 | * @Date: Created in 23:58
280 | * @param:
281 | */
282 | @Override
283 | public Result setAutoMessage(WeiXinSendAutoMessage weiXinSendAutoMessage) {
284 | String appId = weiXinSendAutoMessage.getFromUserName();
285 | String content = JSONObject.toJSONString(weiXinSendAutoMessage);
286 | redisUtil.setHash(appId,weiXinSendAutoMessage.getKey(),content);
287 | return ResultUtil.success(weiXinSendAutoMessage);
288 | }
289 |
290 | }
291 |
--------------------------------------------------------------------------------
/src/main/java/cn/newtol/weixin/utils/EncryptUtil.java:
--------------------------------------------------------------------------------
1 | package cn.newtol.weixin.utils;
2 | import sun.misc.BASE64Decoder;
3 | import sun.misc.BASE64Encoder;
4 |
5 | import java.io.IOException;
6 | import java.io.UnsupportedEncodingException;
7 | import java.security.MessageDigest;
8 | import java.security.NoSuchAlgorithmException;
9 |
10 | /**
11 | * @Author: REN
12 | * @Description: 加密工具类
13 | * @Date: Created in 21:57 2018/3/20
14 | */
15 | public class EncryptUtil {
16 | //md5加密
17 | public static String md5(String data) throws NoSuchAlgorithmException {
18 | MessageDigest md = MessageDigest.getInstance("MD5");
19 | md.update(data.getBytes());
20 | StringBuffer buf = new StringBuffer();
21 | byte[] bits = md.digest();
22 | for (int i = 0; i < bits.length; i++) {
23 | int a = bits[i];
24 | if (a < 0){
25 | a += 256;
26 | }
27 | if (a < 16){
28 | buf.append("0");
29 | }
30 | buf.append(Integer.toHexString(a));
31 | }
32 | return buf.toString();
33 | }
34 | /**
35 | * BASE64解密
36 | *
37 | * @param key
38 | * @return
39 | * @throws Exception
40 | */
41 | public static String decryptBASE64(String key) {
42 | byte[] bt;
43 | try {
44 | bt = (new BASE64Decoder()).decodeBuffer(key);
45 | //如果出现乱码可以改成: String(bt, "utf-8")或 gbk
46 | return new String(bt,"utf-8");
47 |
48 | } catch (IOException e) {
49 | e.printStackTrace();
50 | return "";
51 | }
52 | }
53 |
54 | /**
55 | * BASE64加密
56 | *
57 | * @param key
58 | * @return
59 | * @throws Exception
60 | */
61 | public static String encryptBASE64(String key) {
62 | byte[] bt = key.getBytes();
63 | return (new BASE64Encoder()).encodeBuffer(bt);
64 | }
65 |
66 | private static final char[] digit = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
67 |
68 | /**
69 | * 此类不需要实例化
70 | */
71 | private EncryptUtil() {
72 | }
73 |
74 | /**
75 | * 32位MD5加密,结果大写
76 | * @param str
77 | * @return
78 | */
79 | public static String MD5(String str) {
80 | return encode(str, "MD5");
81 | }
82 |
83 | /**
84 | * 32位SHA1加密,结果大写
85 | * @param str
86 | * @return
87 | */
88 | public static String sha1(String str) {
89 | return encode(str, "SHA-1");
90 | }
91 |
92 | private static String encode(String str, String algorithm) {
93 | String rs = null;
94 | try {
95 | MessageDigest md = MessageDigest.getInstance(algorithm);
96 | byte[] digest = md.digest(str.toString().getBytes("UTF-8"));
97 | rs = byteToStr(digest);
98 | } catch (NoSuchAlgorithmException e) {
99 | System.out.println("该加密方式不存在");
100 | } catch (UnsupportedEncodingException e) {
101 | e.printStackTrace();
102 | }
103 | return rs;
104 | }
105 | /**
106 | * 将byte数组变为16进制对应的字符串
107 | * @param byteArray byte数组
108 | * @return 转换结果
109 | */
110 | private static String byteToStr(byte[] byteArray) {
111 | int len = byteArray.length;
112 | StringBuilder strDigest = new StringBuilder(len * 2);
113 | for (byte aByteArray : byteArray) {
114 | strDigest.append(byteToHexStr(aByteArray));
115 | }
116 | return strDigest.toString();
117 | }
118 |
119 | private static String byteToHexStr(byte mByte) {
120 | char[] tempArr = new char[2];
121 | tempArr[0] = digit[(mByte >>> 4) & 0X0F];
122 | tempArr[1] = digit[mByte & 0X0F];
123 | return new String(tempArr);
124 |
125 | }
126 | }
--------------------------------------------------------------------------------
/src/main/java/cn/newtol/weixin/utils/HttpClientUtil.java:
--------------------------------------------------------------------------------
1 | package cn.newtol.weixin.utils;
2 |
3 | import cn.newtol.weixin.domain.HttpClientResult;
4 | import java.io.IOException;
5 | import java.io.UnsupportedEncodingException;
6 | import java.util.ArrayList;
7 | import java.util.HashMap;
8 | import java.util.List;
9 | import java.util.Map;
10 | import java.util.Map.Entry;
11 | import java.util.Set;
12 |
13 | import org.apache.http.HttpStatus;
14 | import org.apache.http.NameValuePair;
15 | import org.apache.http.client.config.RequestConfig;
16 | import org.apache.http.client.entity.UrlEncodedFormEntity;
17 | import org.apache.http.client.methods.CloseableHttpResponse;
18 | import org.apache.http.client.methods.HttpDelete;
19 | import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
20 | import org.apache.http.client.methods.HttpGet;
21 | import org.apache.http.client.methods.HttpPost;
22 | import org.apache.http.client.methods.HttpPut;
23 | import org.apache.http.client.methods.HttpRequestBase;
24 | import org.apache.http.client.utils.URIBuilder;
25 | import org.apache.http.entity.StringEntity;
26 | import org.apache.http.impl.client.CloseableHttpClient;
27 | import org.apache.http.impl.client.HttpClients;
28 | import org.apache.http.message.BasicNameValuePair;
29 | import org.apache.http.util.EntityUtils;
30 |
31 |
32 | /**
33 | * @Author: 公众号:Newtol
34 | * @Description: http请求工具类
35 | * @Date: Created in 16:45 2018/11/10
36 | */
37 | public class HttpClientUtil {
38 | // 编码格式。发送编码格式统一用UTF-8
39 | private static final String ENCODING = "UTF-8";
40 |
41 | // 设置连接超时时间,单位毫秒。
42 | private static final int CONNECT_TIMEOUT = 6000;
43 |
44 | // 请求获取数据的超时时间(即响应时间),单位毫秒。
45 | private static final int SOCKET_TIMEOUT = 6000;
46 |
47 | /**
48 | * 发送get请求;不带请求头和请求参数
49 | *
50 | * @param url 请求地址
51 | * @return
52 | * @throws Exception
53 | */
54 | public static HttpClientResult doGet(String url) throws Exception {
55 | return doGet(url, null, null);
56 | }
57 |
58 | /**
59 | * 发送get请求;带请求参数
60 | *
61 | * @param url 请求地址
62 | * @param params 请求参数集合
63 | * @return
64 | * @throws Exception
65 | */
66 | public static HttpClientResult doGet(String url, Map params) throws Exception {
67 | return doGet(url, null, params);
68 | }
69 |
70 | /**
71 | * 发送get请求;带请求头和请求参数
72 | *
73 | * @param url 请求地址
74 | * @param headers 请求头集合
75 | * @param params 请求参数集合
76 | * @return
77 | * @throws Exception
78 | */
79 | public static HttpClientResult doGet(String url, Map headers, Map params) throws Exception {
80 | // 创建httpClient对象
81 | CloseableHttpClient httpClient = HttpClients.createDefault();
82 |
83 | // 创建访问的地址
84 | URIBuilder uriBuilder = new URIBuilder(url);
85 | if (params != null) {
86 | Set> entrySet = params.entrySet();
87 | for (Entry entry : entrySet) {
88 | uriBuilder.setParameter(entry.getKey(), entry.getValue());
89 | }
90 | }
91 |
92 | // 创建http对象
93 | HttpGet httpGet = new HttpGet(uriBuilder.build());
94 | /**
95 | * setConnectTimeout:设置连接超时时间,单位毫秒。
96 | * setConnectionRequestTimeout:设置从connect Manager(连接池)获取Connection
97 | * 超时时间,单位毫秒。这个属性是新加的属性,因为目前版本是可以共享连接池的。
98 | * setSocketTimeout:请求获取数据的超时时间(即响应时间),单位毫秒。 如果访问一个接口,多少时间内无法返回数据,就直接放弃此次调用。
99 | */
100 | RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();
101 | httpGet.setConfig(requestConfig);
102 |
103 | // 设置请求头
104 | packageHeader(headers, httpGet);
105 |
106 | // 创建httpResponse对象
107 | CloseableHttpResponse httpResponse = null;
108 |
109 | try {
110 | // 执行请求并获得响应结果
111 | return getHttpClientResult(httpResponse, httpClient, httpGet);
112 | } finally {
113 | // 释放资源
114 | release(httpResponse, httpClient);
115 | }
116 | }
117 |
118 | /**
119 | * 发送post请求;不带请求头和请求参数
120 | *
121 | * @param url 请求地址
122 | * @return
123 | * @throws Exception
124 | */
125 | public static HttpClientResult doPost(String url) throws Exception {
126 | return doPost(url, null, null);
127 | }
128 |
129 | /**
130 | * 发送post请求;带请求参数
131 | *
132 | * @param url 请求地址
133 | * @param params 参数集合
134 | * @return
135 | * @throws Exception
136 | */
137 | public static HttpClientResult doPost(String url, Map params) throws Exception {
138 | return doPost(url, null, params);
139 | }
140 |
141 |
142 | /**
143 | * @Author: 公众号:Newtol
144 | * @Description: url无参数,JSON数据的POST方法
145 | * @Date: Created in 20:01
146 | * @param:
147 | */
148 | public static HttpClientResult doPostForJson(String url,String content){
149 | return doPostForJson(url, content);
150 | }
151 |
152 | /**
153 | * @Author: 公众号:Newtol
154 | * @Description: url有参数,有Json数据的POST方法
155 | * @Date: Created in 20:01
156 | * @param:
157 | */
158 |
159 | public static HttpClientResult doPostForJson(String url, Map params, String content) throws Exception {
160 | // 创建httpClient对象
161 | CloseableHttpClient httpClient = HttpClients.createDefault();
162 |
163 | // 创建访问的地址
164 | URIBuilder uriBuilder = new URIBuilder(url);
165 | if (params != null) {
166 | Set> entrySet = params.entrySet();
167 | for (Entry entry : entrySet) {
168 | uriBuilder.setParameter(entry.getKey(), entry.getValue());
169 | }
170 | }
171 |
172 | // 创建http对象
173 | HttpPost httpPost = new HttpPost(uriBuilder.build());
174 | RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();
175 | httpPost.setConfig(requestConfig);
176 |
177 | StringEntity se = new StringEntity(content.toString(),"UTF-8");
178 | se.setContentType("application/json;charset=UTf-8");
179 |
180 |
181 | httpPost.setEntity(se);
182 |
183 |
184 | // 创建httpResponse对象
185 | CloseableHttpResponse httpResponse = null;
186 |
187 | try {
188 | // 执行请求并获得响应结果
189 | return getHttpClientResult(httpResponse, httpClient, httpPost);
190 | } finally {
191 | // 释放资源
192 | release(httpResponse, httpClient);
193 | }
194 | }
195 |
196 |
197 |
198 | /**
199 | * 发送post请求;带请求头和请求参数
200 | *
201 | * @param url 请求地址
202 | * @param headers 请求头集合
203 | * @param params 请求参数集合
204 | * @return
205 | * @throws Exception
206 | */
207 | public static HttpClientResult doPost(String url, Map headers, Map params) throws Exception {
208 | // 创建httpClient对象
209 | CloseableHttpClient httpClient = HttpClients.createDefault();
210 |
211 | // 创建http对象
212 | HttpPost httpPost = new HttpPost(url);
213 | /**
214 | * setConnectTimeout:设置连接超时时间,单位毫秒。
215 | * setConnectionRequestTimeout:设置从connect Manager(连接池)获取Connection
216 | * 超时时间,单位毫秒。这个属性是新加的属性,因为目前版本是可以共享连接池的。
217 | * setSocketTimeout:请求获取数据的超时时间(即响应时间),单位毫秒。 如果访问一个接口,多少时间内无法返回数据,就直接放弃此次调用。
218 | */
219 | RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();
220 | httpPost.setConfig(requestConfig);
221 | packageHeader(headers, httpPost);
222 |
223 | // 封装请求参数
224 | packageParam(params, httpPost);
225 |
226 | // 创建httpResponse对象
227 | CloseableHttpResponse httpResponse = null;
228 |
229 | try {
230 | // 执行请求并获得响应结果
231 | return getHttpClientResult(httpResponse, httpClient, httpPost);
232 | } finally {
233 | // 释放资源
234 | release(httpResponse, httpClient);
235 | }
236 | }
237 |
238 |
239 |
240 |
241 | /**
242 | * Description: 封装请求头
243 | * @param params
244 | * @param httpMethod
245 | */
246 | public static void packageHeader(Map params, HttpRequestBase httpMethod) {
247 | // 封装请求头
248 | if (params != null) {
249 | Set> entrySet = params.entrySet();
250 | for (Entry entry : entrySet) {
251 | // 设置到请求头到HttpRequestBase对象中
252 | httpMethod.setHeader(entry.getKey(), entry.getValue());
253 | }
254 | }
255 | }
256 |
257 | /**
258 | * Description: 封装请求参数
259 | *
260 | * @param params
261 | * @param httpMethod
262 | * @throws UnsupportedEncodingException
263 | */
264 | public static void packageParam(Map params, HttpEntityEnclosingRequestBase httpMethod)
265 | throws UnsupportedEncodingException {
266 | // 封装请求参数
267 | if (params != null) {
268 | List nvps = new ArrayList();
269 | Set> entrySet = params.entrySet();
270 | for (Entry entry : entrySet) {
271 | nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
272 | }
273 |
274 | // 设置到请求的http对象中
275 | httpMethod.setEntity(new UrlEncodedFormEntity(nvps, ENCODING));
276 | }
277 | }
278 |
279 | /**
280 | * Description: 获得响应结果
281 | *
282 | * @param httpResponse
283 | * @param httpClient
284 | * @param httpMethod
285 | * @return
286 | * @throws Exception
287 | */
288 | public static HttpClientResult getHttpClientResult(CloseableHttpResponse httpResponse,
289 | CloseableHttpClient httpClient, HttpRequestBase httpMethod) throws Exception {
290 | // 执行请求
291 | httpResponse = httpClient.execute(httpMethod);
292 |
293 | // 获取返回结果
294 | if (httpResponse != null && httpResponse.getStatusLine() != null) {
295 | String content = "";
296 | if (httpResponse.getEntity() != null) {
297 | content = EntityUtils.toString(httpResponse.getEntity(), ENCODING);
298 | }
299 | return new HttpClientResult(httpResponse.getStatusLine().getStatusCode(), content);
300 | }
301 | return new HttpClientResult(HttpStatus.SC_INTERNAL_SERVER_ERROR);
302 | }
303 |
304 | /**
305 | * Description: 释放资源
306 | *
307 | * @param httpResponse
308 | * @param httpClient
309 | * @throws IOException
310 | */
311 | public static void release(CloseableHttpResponse httpResponse, CloseableHttpClient httpClient) throws IOException {
312 | // 释放资源
313 | if (httpResponse != null) {
314 | httpResponse.close();
315 | }
316 | if (httpClient != null) {
317 | httpClient.close();
318 | }
319 | }
320 | }
321 |
--------------------------------------------------------------------------------
/src/main/java/cn/newtol/weixin/utils/HttpServletUtil.java:
--------------------------------------------------------------------------------
1 | package cn.newtol.weixin.utils;
2 |
3 | import org.springframework.stereotype.Service;
4 |
5 | import javax.servlet.http.HttpServletRequest;
6 | import javax.servlet.http.HttpServletResponse;
7 | import java.util.Map;
8 | import java.util.Set;
9 |
10 | /**
11 | * @Author: 公众号:Newtol
12 | * @Description: httpServlet工具类
13 | * @Date: Created in 19:26 2018/11/25
14 | */
15 | @Service
16 | public class HttpServletUtil {
17 |
18 | /**
19 | * @Author: 公众号:Newtol
20 | * @Description: 设置session
21 | * @Date: Created in 19:34
22 | * @param:
23 | */
24 | public void setSession(Map map, HttpServletRequest request){
25 | if (map != null) {
26 | Set> entrySet = map.entrySet();
27 | for (Map.Entry entry : entrySet) {
28 | request.getSession().setAttribute(entry.getKey(), entry.getValue());
29 | }
30 | }
31 | }
32 |
33 | public void redirectToUrl(HttpServletResponse response,String url){
34 | response.setContentType("text/html;charset=UTF-8");
35 | response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
36 | response.setHeader("Location", url);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/cn/newtol/weixin/utils/JacksonUtil.java:
--------------------------------------------------------------------------------
1 | package cn.newtol.weixin.utils;
2 |
3 | import com.fasterxml.jackson.annotation.JsonInclude;
4 | import com.fasterxml.jackson.core.JsonProcessingException;
5 | import com.fasterxml.jackson.dataformat.xml.XmlMapper;
6 | import org.springframework.stereotype.Service;
7 |
8 | /**
9 | * @Author: 公众号:Newtol
10 | * @Description: Jackson 转换工具类
11 | * @Date: Created in 23:14 2018/11/28
12 | */
13 | @Service
14 | public class JacksonUtil {
15 | public String getXmlFromBean(Object object) throws JsonProcessingException {
16 | XmlMapper xmlMapper = new XmlMapper();
17 | xmlMapper.setDefaultUseWrapper(false);
18 | //字段为null,自动忽略,不再序列化
19 | xmlMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
20 | String result = xmlMapper.writeValueAsString(object);
21 | return result;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/cn/newtol/weixin/utils/RedisUtil.java:
--------------------------------------------------------------------------------
1 | package cn.newtol.weixin.utils;
2 |
3 | import org.springframework.data.redis.core.HashOperations;
4 | import org.springframework.data.redis.core.StringRedisTemplate;
5 | import org.springframework.data.redis.core.ValueOperations;
6 | import org.springframework.stereotype.Service;
7 |
8 | import javax.annotation.Resource;
9 | import java.util.Map;
10 | import java.util.concurrent.TimeUnit;
11 |
12 | /**
13 | * @Author: 公众号:Newtol
14 | * @Description: Redis工具类
15 | * @Date: Created in 18:55 2018/11/9
16 | */
17 | @Service
18 | public class RedisUtil {
19 | @Resource
20 | private StringRedisTemplate stringRedisTemplate;
21 | /**
22 | * @Author: REN
23 | * @Description: string有过期时间
24 | * @Date: Created in 20:01
25 | * @param key 键
26 | * @param value 值
27 | * @param timeout 过期时间
28 | * @param timeUnit 计时方式
29 | */
30 | public void setString(String key, String value,long timeout,TimeUnit timeUnit){
31 | ValueOperations valueOperations = stringRedisTemplate.opsForValue();
32 | valueOperations.set(key, value,timeout,timeUnit);
33 | }
34 |
35 | /**
36 | * @Author: REN
37 | * @Description: string无过期时间
38 | * @Date: Created in 20:00
39 | * @param: key
40 | */
41 | public void setString(String key,String value){
42 | ValueOperations valueOperations = stringRedisTemplate.opsForValue();
43 | valueOperations.set(key, value);
44 | }
45 |
46 | /**
47 | * get redis: string类型
48 | * @param key key
49 | * @return
50 | */
51 | public String getString(String key){
52 | return stringRedisTemplate.opsForValue().get(key);
53 | }
54 |
55 | /**
56 | * set redis: hash类型
57 | * @param key key
58 | * @param filedKey filedkey
59 | * @param value value
60 | */
61 | public void setHash(String key, String filedKey, String value){
62 | HashOperations hashOperations = stringRedisTemplate.opsForHash();
63 | hashOperations.put(key,filedKey, value);
64 | }
65 |
66 | public void setHash(String key , Map map){
67 | HashOperations hashOperations = stringRedisTemplate.opsForHash();
68 | hashOperations.putAll(key,map);
69 | }
70 |
71 | /**
72 | * get redis: hash类型
73 | * @param key key
74 | * @param filedkey filedkey
75 | * @return
76 | */
77 | public String getHash(String key, String filedkey){
78 | return (String) stringRedisTemplate.opsForHash().get(key, filedkey);
79 | }
80 |
81 | }
82 |
83 |
--------------------------------------------------------------------------------
/src/main/java/cn/newtol/weixin/utils/ResultUtil.java:
--------------------------------------------------------------------------------
1 | package cn.newtol.weixin.utils;
2 |
3 | import cn.newtol.weixin.Enum.ResultEnum;
4 | import cn.newtol.weixin.domain.Result;
5 |
6 | /**
7 | * @Author: 公众号:Newtol
8 | * @Description:
9 | * @Date: Created in 13:14 2018/11/10
10 | */
11 | public class ResultUtil {
12 | /**
13 | * @Author: 公众号:Newtol
14 | * @Description: 请求成功,返回带参数结果
15 | * @Date: Created in 21:16
16 | * @param:
17 | */
18 | public static Result success(Object object){
19 | Result result = new Result();
20 | result.setErrorCode(ResultEnum.SUCCESS.getErrorCode());
21 | result.setMessage(ResultEnum.SUCCESS.getMessage());
22 | result.setData(object);
23 | return result;
24 | }
25 | /**
26 | * @Author: 公众号:Newtol
27 | * @Description: 请求成功,返回不带参数结果
28 | * @Date: Created in 21:16
29 | * @param:
30 | */
31 | public static Result success(){
32 | return success(null);
33 | }
34 |
35 | /**
36 | * @Author: 公众号:Newtol
37 | * @Description: 访问错误,自定义返回参数
38 | * @Date: Created in 21:17
39 | * @param:
40 | */
41 | public static Result error(Integer code ,String msg){
42 | Result result = new Result();
43 | result.setErrorCode(code);
44 | result.setMessage(msg);
45 | return result;
46 | }
47 |
48 | /**
49 | * @Author: 公众号:Newtol
50 | * @Description: 访问错误,使用预定义返回参数
51 | * @Date: Created in 21:17
52 | * @param:
53 | */
54 | public static Result error(ResultEnum resultEnum){
55 | Result result = new Result();
56 | result.setErrorCode(resultEnum.getErrorCode());
57 | result.setMessage(resultEnum.getMessage());
58 | return result;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/cn/newtol/weixin/utils/WeiXinUtil.java:
--------------------------------------------------------------------------------
1 | package cn.newtol.weixin.utils;
2 |
3 | import cn.newtol.weixin.Enum.ResultEnum;
4 | import cn.newtol.weixin.domain.WeiXinUserInfo;
5 | import cn.newtol.weixin.domain.HttpClientResult;
6 | import cn.newtol.weixin.domain.dto.WeiXinVerify;
7 | import cn.newtol.weixin.exceptions.TestException;
8 | import com.alibaba.fastjson.JSONObject;
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 | import org.springframework.stereotype.Component;
12 | import org.springframework.stereotype.Service;
13 | import javax.annotation.Resource;
14 | import java.sql.SQLException;
15 | import java.util.Arrays;
16 | import java.util.HashMap;
17 | import java.util.Map;
18 | import java.util.concurrent.TimeUnit;
19 |
20 | /**
21 | * @Author: 公众号:Newtol
22 | * @Description: 微信后台工具类
23 | * @Date: Created in 17:46 2018/11/24
24 | */
25 | @Component
26 | @Service
27 | public class WeiXinUtil {
28 | private static final Logger logger = LoggerFactory.getLogger(WeiXinUtil.class);
29 | @Resource
30 | private RedisUtil redisUtil;
31 |
32 | /**
33 | * @Author: 公众号:Newtol
34 | * @Description: 获取access_token
35 | * @Date: Created in 19:00
36 | * @param:
37 | */
38 | public synchronized String getAccessToken(String appId,String appSecret,String key) throws Exception {
39 | String accessToken = getAccessTokenFromDatabase(appId,key);
40 | if (accessToken == null || "".equals(accessToken)) {
41 | accessToken = getAccessTokenFromWeiXin(appId,appSecret,key);
42 | }
43 | return accessToken;
44 | }
45 |
46 | /**
47 | * @Author: 公众号:Newtol
48 | * @Description: 从Redis获取AccessToken
49 | * @Date: Created in 22:40
50 | * @param:
51 | */
52 | private String getAccessTokenFromDatabase(String appId,String key) throws SQLException, ClassNotFoundException {
53 | String accessToken = redisUtil.getString(key+appId);
54 | return accessToken;
55 | }
56 |
57 | /**
58 | * @Author: 公众号:Newtol
59 | * @Description: 从微信服务器重新获取AccessToken并存入Redis
60 | * @Date: Created in 22:39
61 | * @param:
62 | */
63 | private String getAccessTokenFromWeiXin(String appId,String appSecret,String key) throws Exception,TestException {
64 | String accessToken = null;
65 |
66 | /*准备发送GET请求*/
67 | String url = "https://api.weixin.qq.com/cgi-bin/token";
68 | HashMap hashMap = new HashMap<>();
69 | hashMap.put("grant_type","client_credential");
70 | hashMap.put("appid",appId);
71 | hashMap.put("secret",appSecret);
72 |
73 | /*获取返回结果*/
74 | HttpClientResult httpClientResult = HttpClientUtil.doGet(url,hashMap);
75 | JSONObject jsonObject = JSONObject.parseObject(httpClientResult.getContent());
76 | accessToken = jsonObject.getString("access_token");
77 |
78 | /*获取失败就抛出异常*/
79 | if (accessToken == null || "".equals(accessToken)){
80 | logger.info("请求AccessToken错误:"+"===appid:"+appId+"===appSecret:"+appSecret);
81 | throw new TestException(ResultEnum.ERROR_WEIXINBASEINFO);
82 | }
83 |
84 | /*因为Access_Token过期时间为7200所以将Redis中的AccessToken过期时间提前50s过期,将key设置为配置文件中的key+appId的形式*/
85 | redisUtil.setString(key+appId,accessToken,7150, TimeUnit.SECONDS);
86 | return accessToken;
87 | }
88 | /**
89 | * @Author: 公众号:Newtol
90 | * @Description: 接入微信服务器
91 | * @Date: Created in 18:22
92 | * @param: weiXinVerify
93 | */
94 | public String joinWeiXin(WeiXinVerify weiXinVerify,String token) {
95 | String list[] = {token,weiXinVerify.getTimestamp(),weiXinVerify.getNonce()};
96 | //字典排序
97 | Arrays.sort(list);
98 | //拼接字符串
99 | StringBuilder builder = new StringBuilder();
100 | for (String str : list) {
101 | builder.append(str);
102 | }
103 | //sha1加密
104 | String hashcode = EncryptUtil.sha1(builder.toString());
105 | //不区分大小写差异情况下比较是否相同
106 | if (hashcode.equalsIgnoreCase(weiXinVerify.getSignature())) {
107 | //响应输出
108 | return weiXinVerify.getEchostr();
109 | }
110 | logger.error("微信接入失败");
111 | return null;
112 | }
113 |
114 | /**
115 | * @Author: 公众号:Newtol
116 | * @Description: 获取微信用户信息
117 | * @Date: Created in 19:19
118 | * @param:
119 | */
120 |
121 | public WeiXinUserInfo getUserInfo(String access_token, String openId) throws Exception {
122 | String url = "https://api.weixin.qq.com/cgi-bin/user/info";
123 | Map map = new HashMap<>();
124 | map.put("access_token",access_token);
125 | map.put("openid",openId);
126 | map.put("lang","zh_CN");
127 | HttpClientResult httpClientResult = HttpClientUtil.doGet(url,map);
128 | System.out.println(httpClientResult.getContent());
129 | WeiXinUserInfo weiXinUserInfo = JSONObject.parseObject(httpClientResult.getContent(), WeiXinUserInfo.class);
130 | if (weiXinUserInfo ==null || "".equals(weiXinUserInfo)){
131 | logger.info("获取用户信息失败:"+httpClientResult.getContent()+"==="+"openid"+openId+"==="+"access_token"+access_token);
132 | }
133 | return weiXinUserInfo;
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | server:
2 | port: 9090
3 | spring:
4 | redis:
5 | database: 0
6 | host: localhost
7 | password:
8 | port: 6379
9 | jedis:
10 | pool:
11 | max-active: 8
12 | max-wait: -1
13 | max-idle: 8
14 | min-idle: 0
15 |
16 | datasource:
17 | driver-class-name: com.mysql.cj.jdbc.Driver
18 | url: jdbc:mysql://localhost:3306/weixin?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
19 | username: root
20 | password:
21 | jpa:
22 | hibernate:
23 | ddl-auto: update
24 | show-sql: true
25 | logging:
26 | level:
27 | cn:
28 | newtol: debug
29 | path: weiXin
30 | pattern:
31 | file: "%d{yyyy-MM-dd} === [%thread] === %-5level === %logger{50} ==== %msg%n"
32 | console: "%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n"
33 |
34 | ---
35 | # 微信接入部分配置
36 | weiXin:
37 | # 微信服务器配置的token
38 | token: bbbbbbbbbb
39 | # Redis中access_token存储的key
40 | accessTokenKey: accessToken_
41 | # 跳转链接的key
42 | redirectUrlKey: redirect_Url
43 | # 获取网页授权时,使用的链接地址
44 | getWebAuthorize: http://newtol.wezoz.com/getWebAuthorize
45 |
46 |
47 |
48 | # appId: wxc5e0d5b55b141e56
49 | # appSecret: 782fc50a471b88e87e544885b2dcc782
50 | # getWebAuthorize: http://ys5p5e.natappfree.cc/getOpenId
51 |
52 | # {"url":"http:\/\/mmbiz.qpic.cn\/mmbiz_jpg\/icor8DRGziah4INEIVibGtiapicKQzvibVicNeqwbkUEHefqlNYHeLc94yibH7ZV3FJqdviaZXL0HPUlUvtldfRwnUc2npA\/0"}
53 |
54 | # {"media_id":"tP0y34JmbzKUFw-m4Zy6U3VZcoCjUvql9yZfVTa9mwM","url":"http:\/\/mmbiz.qpic.cn\/mmbiz_jpg\/icor8DRGziah4INEIVibGtiapicKQzvibVicNeqwbkUEHefqlNYHeLc94yibH7ZV3FJqdviaZXL0HPUlUvtldfRwnUc2npA\/0?wx_fmt=jpeg"}
55 | # {"media_id":"tP0y34JmbzKUFw-m4Zy6U16PpUXJjo0OP5hxBce3P5s","url":"http:\/\/mmbiz.qpic.cn\/mmbiz_jpg\/icor8DRGziah4INEIVibGtiapicKQzvibVicNeqnvMSEPvK3csvlvKPKaBmwAKMZ9ibUVc9icJXJicNPNM9ViaLrs04YUmJPg\/0?wx_fmt=jpeg"}
56 | menu:
57 | button:
58 | - sub_button:
59 | - clickButton:
60 | type: click
61 | name: 点击
62 | key: hi
63 | - clickButton:
64 | type: click
65 | name: 点击2
66 | key: hi1
67 | name: 视图
68 | - sub_button:
69 | - clickButton:
70 | type: view
71 | name: 视图2
72 | url: http://newtol.wezoz.com/redirectUrl?state=010e635c254a61fa5af79b246e7b2697
73 | name: 菜单
74 |
75 |
--------------------------------------------------------------------------------
/src/test/java/cn/newtol/weixin/WeixinApplicationTests.java:
--------------------------------------------------------------------------------
1 | package cn.newtol.weixin;
2 |
3 | import org.junit.Test;
4 | import org.junit.runner.RunWith;
5 | import org.springframework.boot.test.context.SpringBootTest;
6 | import org.springframework.test.context.junit4.SpringRunner;
7 |
8 | @RunWith(SpringRunner.class)
9 | @SpringBootTest
10 | public class WeixinApplicationTests {
11 |
12 | @Test
13 | public void contextLoads() {
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------