提交 6eaa4d62 编写于 作者: 如梦技术's avatar 如梦技术 🐛

🔖 v1.3.2

上级 b0ef7b15
......@@ -3,14 +3,17 @@ jfinal weixin 的 spring boot starter,这个starter是为了方便boot用户
具体demo请查看:`spring-boot-weixin-demo`[JFinal-weixin文档](https://gitee.com/jfinal/jfinal-weixin/wikis/pages?title=Home)
Spring 全家桶 `QQ交流群`:479710041。
推荐
Spring boot 高效开发之 Mica 工具集:https://gitee.com/596392912/mica
## Jar包依赖
加入【如梦技术】Spring QQ群:479710041,了解更多。
## Jar包依赖(最新)
```xml
<dependency>
<groupId>net.dreamlu</groupId>
<artifactId>spring-boot-starter-weixin</artifactId>
<version>1.3.2</version>
<version>1.3.3</version>
</dependency>
```
......@@ -60,6 +63,13 @@ dream:
- `access-token-cache`建议配置有效时间7100秒。
## 更新说明
>## 2019-03-07 v1.3.3
> 升级到 `gradle 5.2.1`。
> 升级 `JFinal` 到 `3.6`。
> 升级 `JFinal Weixin` 到 `2.3`。
> 使用 `mica-auto` 生成 `spring.factories`、`devtools` 配置。
> InMsg 消息对象采用 request 存储,去掉 `@WxMsgController` 中的 Scope 配置,将消息控制器还原为单例。
>## 2018-12-23 v1.3.2
> 修复 `SpringAccessTokenCache` 没有配置的问题,感谢 qq:`A梦的小C` 反馈。
......@@ -73,20 +83,3 @@ dream:
## 捐助共勉
<img src="https://gitee.com/uploads/images/2018/0311/153544_5afb12b1_372.jpeg" width="250px"/>
<img src="https://gitee.com/uploads/images/2018/0311/153556_679db579_372.jpeg" width="250px"/>
## VIP群
捐助~~¥199~~限时¥99,即可加入如梦技术VIP,捐助后联系QQ: 596392912
#### VIP权益
1. spring boot版安全框架(maven + spring boot + spring security + thymeleaf)
2. 技术资料共享,技术指导和技术路线规划。
3. spring cloud脚手架(改造中)
4. 更多私有Git资源,
### spring boot版安全框架 Demo
地址:http://demo.dreamlu.net
账号vs密码:test、test
# http://editorconfig.org
root = true
# 空格替代Tab缩进在各种编辑工具下效果一致
[*]
indent_style = space
indent_size = 4
charset = utf-8
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
[*.java]
indent_style = tab
[*.{json,yml}]
indent_size = 2
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false
buildscript {
repositories {
maven { url 'http://repo.spring.io/plugins-release' }
}
dependencies {
classpath("io.spring.gradle:propdeps-plugin:0.0.10.RELEASE")
classpath("io.spring.gradle:dependency-management-plugin:1.0.4.RELEASE")
}
}
apply plugin: 'maven'
apply plugin: 'signing'
apply plugin: 'propdeps'
apply plugin: 'propdeps-maven'
apply plugin: 'maven-publish'
apply plugin: 'io.spring.dependency-management'
group = GROUPID
version = VERSION
ext {
javaVersion = JavaVersion.VERSION_1_8
springVersion = "4.3.18.RELEASE"
springBootVersion = "1.5.15.RELEASE"
jfinalVersion = "3.4"
jfinalWeixinVersion = "2.1"
lombokVersion = '1.16.20'
springBootVersion = "1.5.19.RELEASE"
jfinalVersion = "3.6"
jfinalWeixinVersion = "2.3"
micaAutoVersion = "1.0.2"
lombokVersion = "1.18.6"
}
repositories {
mavenLocal()
maven { url "http://maven.aliyun.com/nexus/content/groups/public" }
maven { url "https://repo.spring.io/libs-release" }
maven { url "https://repo.spring.io/milestone" }
maven { url "http://maven.aliyun.com/nexus/content/groups/public" }
mavenCentral()
}
dependencies {
compile "com.jfinal:jfinal:${jfinalVersion}"
compile "com.jfinal:jfinal-weixin:${jfinalWeixinVersion}"
provided "org.springframework.boot:spring-boot-starter-web"
optional "org.springframework.boot:spring-boot-configuration-processor"
provided "org.projectlombok:lombok:${lombokVersion}"
testCompile "org.springframework.boot:spring-boot-starter-test"
}
apply plugin: "java-library"
apply from: "${rootProject.projectDir}/gradle/publish-maven.gradle"
dependencyManagement {
imports {
mavenBom "org.springframework.boot:spring-boot-dependencies:${springBootVersion}"
}
}
task sourcesJar(type: Jar, dependsOn: classes) {
classifier = 'sources'
from sourceSets.main.allSource
}
jar {
into("META-INF/maven/$project.group/$project.name") {
from { generatePomFileForMavenJavaPublication }
rename ".*", "pom.xml"
}
}
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
}
}
}
javadoc {
options {
encoding "UTF-8"
charSet 'UTF-8'
author true
version true
failOnError false
links "http://docs.oracle.com/javase/8/docs/api"
}
}
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from 'build/docs/javadoc'
}
artifacts {
archives sourcesJar
archives javadocJar
}
group = GROUPID
version = VERSION
uploadArchives {
repositories {
mavenDeployer {
pom.version = "$project.version"
pom.artifactId = "$project.name"
pom.groupId = "$project.group"
signing {
sign configurations.archives
}
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
repository(url: pom.version.endsWith('SNAPSHOT')
? 'https://oss.sonatype.org/content/repositories/snapshots/'
: 'https://oss.sonatype.org/service/local/staging/deploy/maven2/'
) {
authentication(userName: userName, password: passWord)
}
pom.project {
name 'spring-boot-starter-weixin'
packaging 'jar'
description 'A spring boot starter for JFinal weixin.'
url 'https://gitee.com/596392912/spring-boot-starter-weixin'
scm {
connection 'scm:git@gitee.com:/596392912/spring-boot-starter-weixin.git'
developerConnection 'scm:git@gitee.com/596392912/spring-boot-starter-weixin.git'
url 'https://gitee.com/596392912/spring-boot-starter-weixin'
}
licenses {
license {
name 'The MIT License (MIT)'
url 'http://opensource.org/licenses/MIT'
}
}
developers {
developer {
name 'Dreamlu'
email 'qq596392912@gmail.com'
}
}
}
}
}
dependencies {
api "com.jfinal:jfinal:$jfinalVersion"
api "com.jfinal:jfinal-weixin:$jfinalWeixinVersion"
api "com.squareup.okhttp3:okhttp:3.13.1"
compileOnly "org.projectlombok:lombok:$lombokVersion"
annotationProcessor "org.projectlombok:lombok:$lombokVersion"
annotationProcessor "net.dreamlu:mica-auto:$micaAutoVersion"
compileOnly "org.springframework.boot:spring-boot-starter-web:${springBootVersion}"
testCompile "org.springframework.boot:spring-boot-starter-test:${springBootVersion}"
}
VERSION=1.3.2
VERSION=1.3.3
GROUPID=net.dreamlu
userName=chunmeng
passWord=sonatype
NEXUS_OSS_USER_NAME=***
NEXUS_OSS_PASS_WORD=***
REPOSITORY_URL_SNAPSHOT=https://oss.sonatype.org/content/repositories/snapshots/
REPOSITORY_URL_RELEASE=https://oss.sonatype.org/service/local/staging/deploy/maven2/
signing.keyId=D73A4CFE
signing.password=sonatype
signing.secretKeyRingFile=/Users/lcm/.gnupg/secring.gpg
apply plugin: 'signing'
apply plugin: 'maven-publish'
jar {
afterEvaluate {
manifest {
attributes 'Implementation-Version': version
}
}
}
task sourcesJar(type: Jar) {
archiveClassifier.set('sources')
from sourceSets.main.allSource
}
javadoc {
options {
encoding "UTF-8"
charSet 'UTF-8'
author true
version true
failOnError false
links "http://docs.oracle.com/javase/8/docs/api"
}
}
task javadocJar(type: Jar) {
archiveClassifier.set('javadoc')
from 'build/docs/javadoc'
}
artifacts {
archives sourcesJar
archives javadocJar
}
tasks.whenTaskAdded { task ->
if (task.name.contains('signMavenJavaPublication')) {
task.enabled = new File(project.property('signing.secretKeyRingFile') as String).isFile()
}
}
publishing {
repositories {
maven {
url = version.endsWith('SNAPSHOT') ? REPOSITORY_URL_SNAPSHOT : REPOSITORY_URL_RELEASE
credentials {
username getRepositoryUsername()
password getRepositoryPassword()
}
}
}
publications {
mavenJava(MavenPublication) {
from components.java
artifact sourcesJar
artifact javadocJar
pom {
name = 'spring-boot-starter-weixin'
packaging = 'jar'
description = 'A spring boot starter for JFinal weixin.'
url = 'https://gitee.com/596392912/spring-boot-starter-weixin'
scm {
connection = 'scm:git@gitee.com:/596392912/spring-boot-starter-weixin.git'
developerConnection = 'scm:git@gitee.com/596392912/spring-boot-starter-weixin.git'
url = 'https://gitee.com/596392912/spring-boot-starter-weixin'
}
licenses {
license {
name = 'The MIT License (MIT)'
url = 'http://opensource.org/licenses/MIT'
}
}
developers {
developer {
name = 'Dreamlu'
email = 'qq596392912@gmail.com'
}
}
}
}
}
signing {
sign publishing.publications.mavenJava
}
}
def getRepositoryUsername() {
return hasProperty('NEXUS_OSS_USER_NAME') ? NEXUS_OSS_USER_NAME : ""
}
def getRepositoryPassword() {
return hasProperty('NEXUS_OSS_PASS_WORD') ? NEXUS_OSS_PASS_WORD : ""
}
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
......@@ -28,7 +28,7 @@ APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
DEFAULT_JVM_OPTS='"-Xmx64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
......
......@@ -14,7 +14,7 @@ set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DEFAULT_JVM_OPTS="-Xmx64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
......
......@@ -18,12 +18,13 @@ import java.lang.annotation.*;
@Controller
public @interface WxApi {
/**
* Alias for {@link RequestMapping#value}.
* @return {String[]}
*/
@AliasFor(annotation = RequestMapping.class)
String[] value() default {};
/**
* Alias for {@link RequestMapping#value}.
*
* @return {String[]}
*/
@AliasFor(annotation = RequestMapping.class)
String[] value() default {};
// /**
// * 目前不支持多小程序
......
package net.dreamlu.weixin.annotation;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.lang.annotation.*;
......@@ -22,11 +18,11 @@ import java.lang.annotation.*;
@Component
@RequestMapping
@Controller
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public @interface WxMsgController {
/**
* Alias for {@link RequestMapping#value}.
*
* @return {String[]}
*/
@AliasFor(annotation = RequestMapping.class)
......
......@@ -4,7 +4,7 @@ import lombok.AllArgsConstructor;
import net.dreamlu.weixin.cache.SpringAccessTokenCache;
import net.dreamlu.weixin.properties.DreamWeixinProperties;
import net.dreamlu.weixin.spring.MsgInterceptor;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
......@@ -12,9 +12,14 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* 微信自动配置
*
* @author L.cm
*/
@Configuration
@AutoConfigureAfter(DreamWeixinProperties.class)
@AllArgsConstructor
@EnableConfigurationProperties(DreamWeixinProperties.class)
public class DreamWeixinAutoConfiguration {
private final CacheManager cacheManager;
......@@ -37,7 +42,7 @@ public class DreamWeixinAutoConfiguration {
String urlPattern = properties.getUrlPatterns();
MsgInterceptor httpCacheInterceptor = new MsgInterceptor(properties);
registry.addInterceptor(httpCacheInterceptor)
.addPathPatterns(urlPattern);
.addPathPatterns(urlPattern);
}
}
}
......@@ -15,58 +15,63 @@ import org.springframework.context.annotation.Configuration;
import java.util.List;
/**
* 微信应用配置
*
* @author L.cm
*/
@Configuration
@AllArgsConstructor
public class WeixinAppConfig implements SmartInitializingSingleton {
private final DreamWeixinProperties weixinProperties;
private final SpringAccessTokenCache accessTokenCache;
private final DreamWeixinProperties weixinProperties;
private final SpringAccessTokenCache accessTokenCache;
@Override
public void afterSingletonsInstantiated() {
boolean isdev = weixinProperties.isDevMode();
ApiConfigKit.setDevMode(isdev);
ApiConfigKit.setAccessTokenCache(accessTokenCache);
List<DreamWeixinProperties.ApiConfig> list = weixinProperties.getWxConfigs();
for (DreamWeixinProperties.ApiConfig apiConfig : list) {
ApiConfig config = new ApiConfig();
if (StrKit.notBlank(apiConfig.getAppId())) {
config.setAppId(apiConfig.getAppId());
}
if (StrKit.notBlank(apiConfig.getAppSecret())) {
config.setAppSecret(apiConfig.getAppSecret());
}
if (StrKit.notBlank(apiConfig.getToken())) {
config.setToken(apiConfig.getToken());
}
if (StrKit.notBlank(apiConfig.getEncodingAesKey())) {
config.setEncodingAesKey(apiConfig.getEncodingAesKey());
}
config.setEncryptMessage(apiConfig.isMessageEncrypt());
ApiConfigKit.putApiConfig(config);
}
DreamWeixinProperties.WxaConfig apiConfig = weixinProperties.getWxaConfig();
WxaConfig config = new WxaConfig();
if (StrKit.notBlank(apiConfig.getAppId())) {
config.setAppId(apiConfig.getAppId());
}
if (StrKit.notBlank(apiConfig.getAppSecret())) {
config.setAppSecret(apiConfig.getAppSecret());
}
if (StrKit.notBlank(apiConfig.getToken())) {
config.setToken(apiConfig.getToken());
}
if (StrKit.notBlank(apiConfig.getEncodingAesKey())) {
config.setEncodingAesKey(apiConfig.getEncodingAesKey());
}
config.setMessageEncrypt(apiConfig.isMessageEncrypt());
WxaConfigKit.setDevMode(isdev);
WxaConfigKit.setWxaConfig(config);
if (WxaMsgParser.JSON == weixinProperties.getWxaMsgParser()) {
WxaConfigKit.useJsonMsgParser();
}
if ("jackson".equals(weixinProperties.getJsonType())) {
JsonUtils.setJsonFactory(JacksonFactory.me());
}
}
@Override
public void afterSingletonsInstantiated() {
boolean isdev = weixinProperties.isDevMode();
ApiConfigKit.setDevMode(isdev);
ApiConfigKit.setAccessTokenCache(accessTokenCache);
List<DreamWeixinProperties.ApiConfig> list = weixinProperties.getWxConfigs();
for (DreamWeixinProperties.ApiConfig apiConfig : list) {
ApiConfig config = new ApiConfig();
if (StrKit.notBlank(apiConfig.getAppId())) {
config.setAppId(apiConfig.getAppId());
}
if (StrKit.notBlank(apiConfig.getAppSecret())) {
config.setAppSecret(apiConfig.getAppSecret());
}
if (StrKit.notBlank(apiConfig.getToken())) {
config.setToken(apiConfig.getToken());
}
if (StrKit.notBlank(apiConfig.getEncodingAesKey())) {
config.setEncodingAesKey(apiConfig.getEncodingAesKey());
}
config.setEncryptMessage(apiConfig.isMessageEncrypt());
ApiConfigKit.putApiConfig(config);
}
DreamWeixinProperties.WxaConfig apiConfig = weixinProperties.getWxaConfig();
WxaConfig config = new WxaConfig();
if (StrKit.notBlank(apiConfig.getAppId())) {
config.setAppId(apiConfig.getAppId());
}
if (StrKit.notBlank(apiConfig.getAppSecret())) {
config.setAppSecret(apiConfig.getAppSecret());
}
if (StrKit.notBlank(apiConfig.getToken())) {
config.setToken(apiConfig.getToken());
}
if (StrKit.notBlank(apiConfig.getEncodingAesKey())) {
config.setEncodingAesKey(apiConfig.getEncodingAesKey());
}
config.setMessageEncrypt(apiConfig.isMessageEncrypt());
WxaConfigKit.setDevMode(isdev);
WxaConfigKit.setWxaConfig(config);
if (WxaMsgParser.JSON == weixinProperties.getWxaMsgParser()) {
WxaConfigKit.useJsonMsgParser();
}
if ("jackson".equals(weixinProperties.getJsonType())) {
JsonUtils.setJsonFactory(JacksonFactory.me());
}
}
}
......@@ -13,56 +13,58 @@ import java.util.List;
@ConfigurationProperties("dream.weixin")
public class DreamWeixinProperties {
/**
* 拦截的路由,默认:/weixin/*
*/
private String urlPatterns = "/weixin/*";
/**
* 是否开发模式,默认:false
*/
private boolean devMode = false;
/**
* Spring cache name,需要开启spring cache,默认:dreamWeixinCache
*/
private String accessTokenCache = "dreamWeixinCache";
/**
* 多公众号url挂参,默认:appId
*/
private String appIdKey = "appId";
/**
* 多公众号配置
*/
private List<ApiConfig> wxConfigs = new ArrayList<>();
/**
* 小程序配置
*/
private WxaConfig wxaConfig = new WxaConfig();
/**
* 小程序消息解析,默认xml,支持json和xml
*/
private WxaMsgParser wxaMsgParser = WxaMsgParser.XML;
/**
* json 类型,默认为 boot 的 jackson,可配置成 jfinal,使用jfinal默认规则
*/
private String jsonType = "jackson";
/**
* 拦截的路由,默认:/weixin/*
*/
private String urlPatterns = "/weixin/*";
/**
* 是否开发模式,默认:false
*/
private boolean devMode = false;
/**
* Spring cache name,需要开启spring cache,默认:dreamWeixinCache
*/
private String accessTokenCache = "dreamWeixinCache";
/**
* 多公众号url挂参,默认:appId
*/
private String appIdKey = "appId";
/**
* 多公众号配置
*/
private List<ApiConfig> wxConfigs = new ArrayList<>();
/**
* 小程序配置
*/
private WxaConfig wxaConfig = new WxaConfig();
/**
* 小程序消息解析,默认xml,支持json和xml
*/
private WxaMsgParser wxaMsgParser = WxaMsgParser.XML;
/**
* json 类型,默认为 boot 的 jackson,可配置成 jfinal,使用jfinal默认规则
*/
private String jsonType = "jackson";
@Getter
@Setter
public static class ApiConfig {
private String token;
private String appId;
private String appSecret;
private String encodingAesKey;
private boolean messageEncrypt = false; // 消息加密与否
}
@Getter
@Setter
public static class ApiConfig {
private String token;
private String appId;
private String appSecret;
private String encodingAesKey;
// 消息加密与否
private boolean messageEncrypt = false;
}
@Getter
@Setter
public static class WxaConfig {
private String appId;
private String appSecret;
private String token;
private String encodingAesKey;
private boolean messageEncrypt = false; // 消息加密与否
}
@Getter
@Setter
public static class WxaConfig {
private String appId;
private String appSecret;
private String token;
private String encodingAesKey;
// 消息加密与否
private boolean messageEncrypt = false;
}
}
/**
* Copyright (c) 2011-2014, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
*/
package net.dreamlu.weixin.spring;
import com.jfinal.weixin.iot.msg.InEquDataMsg;
......@@ -16,212 +10,215 @@ import com.jfinal.weixin.sdk.msg.in.speech_recognition.InSpeechRecognitionResult
/**
* MsgControllerAdapter 对 MsgController 部分方法提供了默认实现,
* 以便开发者不去关注 MsgController 中不需要处理的抽象方法,节省代码量
*
* @author L.cm
*/
public abstract class DreamMsgControllerAdapter extends MsgController {
// 关注/取消关注事件
@Override
protected abstract void processInFollowEvent(InFollowEvent inFollowEvent);
// 接收文本消息事件
@Override
protected abstract void processInTextMsg(InTextMsg inTextMsg);
// 自定义菜单事件
@Override
protected abstract void processInMenuEvent(InMenuEvent inMenuEvent);
// 接收图片消息事件
@Override
protected void processInImageMsg(InImageMsg inImageMsg) {
renderDefault();
}
// 接收语音消息事件
@Override
protected void processInVoiceMsg(InVoiceMsg inVoiceMsg) {
renderDefault();
}
// 接收视频消息事件
@Override
protected void processInVideoMsg(InVideoMsg inVideoMsg) {
renderDefault();
}
// 接收地理位置消息事件
@Override
protected void processInLocationMsg(InLocationMsg inLocationMsg) {
renderDefault();
}
// 接收链接消息事件
@Override
protected void processInLinkMsg(InLinkMsg inLinkMsg) {
renderDefault();
}
// 扫描带参数二维码事件
@Override
protected void processInQrCodeEvent(InQrCodeEvent inQrCodeEvent) {
renderDefault();
}
// 上报地理位置事件
@Override
protected void processInLocationEvent(InLocationEvent inLocationEvent) {
renderDefault();
}
// 接收语音识别结果,与 InVoiceMsg 唯一的不同是多了一个 Recognition 标记
@Override
protected void processInSpeechRecognitionResults(InSpeechRecognitionResults inSpeechRecognitionResults) {
renderDefault();
}
// 在模版消息发送任务完成后事件
@Override
protected void processInTemplateMsgEvent(InTemplateMsgEvent inTemplateMsgEvent) {
renderDefault();
}
// 群发完成事件
@Override
protected void processInMassEvent(InMassEvent inMassEvent) {
renderDefault();
}
// 接收小视频消息
@Override
protected void processInShortVideoMsg(InShortVideoMsg inShortVideoMsg) {
renderDefault();
}
// 接客服入会话事件
@Override
protected void processInCustomEvent(InCustomEvent inCustomEvent) {
renderDefault();
}
// 用户进入摇一摇界面,在“周边”页卡下摇一摇时事件
@Override
protected void processInShakearoundUserShakeEvent(InShakearoundUserShakeEvent inShakearoundUserShakeEvent) {
renderDefault();
}
// 资质认证事件
@Override
protected void processInVerifySuccessEvent(InVerifySuccessEvent inVerifySuccessEvent) {
renderDefault();
}
// 资质认证失败事件
@Override
protected void processInVerifyFailEvent(InVerifyFailEvent inVerifyFailEvent){
renderDefault();
}
// 门店在审核通过后下发消息事件
@Override
protected void processInPoiCheckNotifyEvent(InPoiCheckNotifyEvent inPoiCheckNotifyEvent) {
renderDefault();
}
// WIFI连网后下发消息 by unas at 2016-1-29
@Override
protected void processInWifiEvent(InWifiEvent inWifiEvent) {
renderDefault();
}
// 微信会员卡积分变更事件
@Override
protected void processInUpdateMemberCardEvent(InUpdateMemberCardEvent msg) {
renderDefault();
}
// 微信会员卡快速买单事件
@Override
protected void processInUserPayFromCardEvent(InUserPayFromCardEvent msg) {
renderDefault();
}
// 微信小店订单支付成功接口事件
@Override
protected void processInMerChantOrderEvent(InMerChantOrderEvent inMerChantOrderEvent) {
renderDefault();
}
// 没有找到对应的事件消息
@Override
protected void processIsNotDefinedEvent(InNotDefinedEvent inNotDefinedEvent) {
renderDefault();
}
// 没有找到对应的消息
@Override
protected void processIsNotDefinedMsg(InNotDefinedMsg inNotDefinedMsg) {
renderDefault();
}
@Override
protected void processInUserGiftingCardEvent(InUserGiftingCardEvent msg) {
renderDefault();
}
@Override
protected void processInUserGetCardEvent(InUserGetCardEvent msg) {
renderDefault();
}
@Override
protected void processInUserConsumeCardEvent(InUserConsumeCardEvent msg) {
renderDefault();
}
@Override
protected void processInCardSkuRemindEvent(InCardSkuRemindEvent msg) {
renderDefault();
}
@Override
protected void processInCardPayOrderEvent(InCardPayOrderEvent msg) {
renderDefault();
}
@Override
protected void processInCardPassCheckEvent(InCardPassCheckEvent msg) {
renderDefault();
}
@Override
protected void processInUserCardEvent(InUserCardEvent inUserCardEvent) {
renderDefault();
}
/**
* 处理微信硬件绑定和解绑事件
* @param msg 处理微信硬件绑定和解绑事件
*/
@Override
protected void processInEqubindEvent(InEqubindEvent msg) {
renderDefault();
}
/**
* 处理微信硬件发来数据
* @param msg 处理微信硬件发来数据
*/
@Override
protected void processInEquDataMsg(InEquDataMsg msg) {
renderDefault();
}
/**
* 方便没有使用的api返回“”避免出现,该公众号暂时不能提供服务
*
* 可重写该方法
*/
protected void renderDefault() {
WebUtils.renderText(response, "");
}
// 关注/取消关注事件
@Override
protected abstract void processInFollowEvent(InFollowEvent inFollowEvent);
// 接收文本消息事件
@Override
protected abstract void processInTextMsg(InTextMsg inTextMsg);
// 自定义菜单事件
@Override
protected abstract void processInMenuEvent(InMenuEvent inMenuEvent);
// 接收图片消息事件
@Override
protected void processInImageMsg(InImageMsg inImageMsg) {
renderDefault();
}
// 接收语音消息事件
@Override
protected void processInVoiceMsg(InVoiceMsg inVoiceMsg) {
renderDefault();
}
// 接收视频消息事件
@Override
protected void processInVideoMsg(InVideoMsg inVideoMsg) {
renderDefault();
}
// 接收地理位置消息事件
@Override
protected void processInLocationMsg(InLocationMsg inLocationMsg) {
renderDefault();
}
// 接收链接消息事件
@Override
protected void processInLinkMsg(InLinkMsg inLinkMsg) {
renderDefault();
}
// 扫描带参数二维码事件
@Override
protected void processInQrCodeEvent(InQrCodeEvent inQrCodeEvent) {
renderDefault();
}
// 上报地理位置事件
@Override
protected void processInLocationEvent(InLocationEvent inLocationEvent) {
renderDefault();
}
// 接收语音识别结果,与 InVoiceMsg 唯一的不同是多了一个 Recognition 标记
@Override
protected void processInSpeechRecognitionResults(InSpeechRecognitionResults inSpeechRecognitionResults) {
renderDefault();
}
// 在模版消息发送任务完成后事件
@Override
protected void processInTemplateMsgEvent(InTemplateMsgEvent inTemplateMsgEvent) {
renderDefault();
}
// 群发完成事件
@Override
protected void processInMassEvent(InMassEvent inMassEvent) {
renderDefault();
}
// 接收小视频消息
@Override
protected void processInShortVideoMsg(InShortVideoMsg inShortVideoMsg) {
renderDefault();
}
// 接客服入会话事件
@Override
protected void processInCustomEvent(InCustomEvent inCustomEvent) {
renderDefault();
}
// 用户进入摇一摇界面,在“周边”页卡下摇一摇时事件
@Override
protected void processInShakearoundUserShakeEvent(InShakearoundUserShakeEvent inShakearoundUserShakeEvent) {
renderDefault();
}
// 资质认证事件
@Override
protected void processInVerifySuccessEvent(InVerifySuccessEvent inVerifySuccessEvent) {
renderDefault();
}
// 资质认证失败事件
@Override
protected void processInVerifyFailEvent(InVerifyFailEvent inVerifyFailEvent) {
renderDefault();
}
// 门店在审核通过后下发消息事件
@Override
protected void processInPoiCheckNotifyEvent(InPoiCheckNotifyEvent inPoiCheckNotifyEvent) {
renderDefault();
}
// WIFI连网后下发消息 by unas at 2016-1-29
@Override
protected void processInWifiEvent(InWifiEvent inWifiEvent) {
renderDefault();
}
// 微信会员卡积分变更事件
@Override
protected void processInUpdateMemberCardEvent(InUpdateMemberCardEvent msg) {
renderDefault();
}
// 微信会员卡快速买单事件
@Override
protected void processInUserPayFromCardEvent(InUserPayFromCardEvent msg) {
renderDefault();
}
// 微信小店订单支付成功接口事件
@Override
protected void processInMerChantOrderEvent(InMerChantOrderEvent inMerChantOrderEvent) {
renderDefault();
}
// 没有找到对应的事件消息
@Override
protected void processIsNotDefinedEvent(InNotDefinedEvent inNotDefinedEvent) {
renderDefault();
}
// 没有找到对应的消息
@Override
protected void processIsNotDefinedMsg(InNotDefinedMsg inNotDefinedMsg) {
renderDefault();
}
@Override
protected void processInUserGiftingCardEvent(InUserGiftingCardEvent msg) {
renderDefault();
}
@Override
protected void processInUserGetCardEvent(InUserGetCardEvent msg) {
renderDefault();
}
@Override
protected void processInUserConsumeCardEvent(InUserConsumeCardEvent msg) {
renderDefault();
}
@Override
protected void processInCardSkuRemindEvent(InCardSkuRemindEvent msg) {
renderDefault();
}
@Override
protected void processInCardPayOrderEvent(InCardPayOrderEvent msg) {
renderDefault();
}
@Override
protected void processInCardPassCheckEvent(InCardPassCheckEvent msg) {
renderDefault();
}
@Override
protected void processInUserCardEvent(InUserCardEvent inUserCardEvent) {
renderDefault();
}
/**
* 处理微信硬件绑定和解绑事件
*
* @param msg 处理微信硬件绑定和解绑事件
*/
@Override
protected void processInEqubindEvent(InEqubindEvent msg) {
renderDefault();
}
/**
* 处理微信硬件发来数据
*
* @param msg 处理微信硬件发来数据
*/
@Override
protected void processInEquDataMsg(InEquDataMsg msg) {
renderDefault();
}
/**
* 方便没有使用的api返回“”避免出现,该公众号暂时不能提供服务
* <p>
* 可重写该方法
*/
protected void renderDefault() {
WebUtils.renderText(response, "");
}
}
package net.dreamlu.weixin.spring;
import com.jfinal.kit.StrKit;
import com.jfinal.weixin.sdk.api.ApiConfigKit;
import com.jfinal.weixin.sdk.kit.MsgEncryptKit;
import com.jfinal.wxaapp.WxaConfigKit;
import com.jfinal.wxaapp.msg.IMsgParser;
......@@ -20,70 +19,73 @@ import javax.servlet.http.HttpServletResponse;
/**
* 小程序消息控制器
* @author L.cm
*
* @author L.cm
*/
public abstract class DreamWxaMsgController {
private static final Log logger = LogFactory.getLog(DreamWxaMsgController.class);
private WxaMsg wxaMsg = null; // 本次请求 xml 解析后的 wxaMsg 对象
@Autowired
protected HttpServletRequest request;
@Autowired
protected HttpServletResponse response;
/**
* 小程序消息
* @param imXmlMsg imXmlMsg
*/
@RequestMapping("")
public void index(@RequestBody String imXmlMsg) {
// 开发模式输出微信服务发送过来的 xml、json 消息
if (WxaConfigKit.isDevMode()) {
System.out.println("接收消息:");
System.out.println(imXmlMsg);
}
if (StrKit.isBlank(imXmlMsg)) {
throw new RuntimeException("请不要在浏览器中请求该连接,调试请查看WIKI:http://git.oschina.net/jfinal/jfinal-weixin/wikis/JFinal-weixin-demo%E5%92%8C%E8%B0%83%E8%AF%95");
}
// 是否需要解密消息
if (WxaConfigKit.getWxaConfig().isMessageEncrypt()) {
imXmlMsg = MsgEncryptKit.decrypt(imXmlMsg,
request.getParameter("timestamp"),
request.getParameter("nonce"),
request.getParameter("msg_signature"));
}
private static final Log logger = LogFactory.getLog(DreamWxaMsgController.class);
@Autowired
protected HttpServletRequest request;
@Autowired
protected HttpServletResponse response;
/**
* 小程序消息
*
* @param imXmlMsg imXmlMsg
*/
@RequestMapping("")
public void index(@RequestBody String imXmlMsg) {
// 开发模式输出微信服务发送过来的 xml、json 消息
if (WxaConfigKit.isDevMode()) {
System.out.println("接收消息:");
System.out.println(imXmlMsg);
}
if (StrKit.isBlank(imXmlMsg)) {
throw new RuntimeException("请不要在浏览器中请求该连接,调试请查看WIKI:http://git.oschina.net/jfinal/jfinal-weixin/wikis/JFinal-weixin-demo%E5%92%8C%E8%B0%83%E8%AF%95");
}
// 是否需要解密消息
if (WxaConfigKit.getWxaConfig().isMessageEncrypt()) {
imXmlMsg = MsgEncryptKit.decrypt(imXmlMsg,
request.getParameter("timestamp"),
request.getParameter("nonce"),
request.getParameter("msg_signature"));
}
IMsgParser msgParser = WxaConfigKit.getMsgParser();
WxaMsg wxaMsg = msgParser.parser(imXmlMsg);
if (wxaMsg instanceof WxaTextMsg) {
processTextMsg((WxaTextMsg) wxaMsg);
} else if (wxaMsg instanceof WxaImageMsg) {
processImageMsg((WxaImageMsg) wxaMsg);
} else if (wxaMsg instanceof WxaUserEnterSessionMsg) {
processUserEnterSessionMsg((WxaUserEnterSessionMsg) wxaMsg);
} else {
logger.error("未能识别的小程序消息类型。 消息内容为:\n" + imXmlMsg);
}
// 直接回复success(推荐方式)
WebUtils.renderText(response, "success");
}
/**
* 处理接收到的文本消息
*
* @param textMsg 处理接收到的文本消息
*/
protected abstract void processTextMsg(WxaTextMsg textMsg);
/**
* 处理接收到的图片消息
*
* @param imageMsg 处理接收到的图片消息
*/
protected abstract void processImageMsg(WxaImageMsg imageMsg);
IMsgParser msgParser = WxaConfigKit.getMsgParser();
wxaMsg = msgParser.parser(imXmlMsg);
if (wxaMsg instanceof WxaTextMsg) {
processTextMsg((WxaTextMsg) wxaMsg);
} else if (wxaMsg instanceof WxaImageMsg) {
processImageMsg((WxaImageMsg) wxaMsg);
} else if (wxaMsg instanceof WxaUserEnterSessionMsg) {
processUserEnterSessionMsg((WxaUserEnterSessionMsg) wxaMsg);
} else {
logger.error("未能识别的小程序消息类型。 消息内容为:\n" + imXmlMsg);
}
// 直接回复success(推荐方式)
WebUtils.renderText(response,"success");
}
/**
* 处理接收到的文本消息
* @param textMsg 处理接收到的文本消息
*/
protected abstract void processTextMsg(WxaTextMsg textMsg);
/**
* 处理接收到的图片消息
* @param imageMsg 处理接收到的图片消息
*/
protected abstract void processImageMsg(WxaImageMsg imageMsg);
/**
* 处理接收到的进入会话事件
* @param userEnterSessionMsg 处理接收到的进入会话事件
*/
protected abstract void processUserEnterSessionMsg(WxaUserEnterSessionMsg userEnterSessionMsg);
/**
* 处理接收到的进入会话事件
*
* @param userEnterSessionMsg 处理接收到的进入会话事件
*/
protected abstract void processUserEnterSessionMsg(WxaUserEnterSessionMsg userEnterSessionMsg);
}
\ No newline at end of file
}
......@@ -3,127 +3,126 @@ package net.dreamlu.weixin.spring;
import com.jfinal.kit.StrKit;
import com.jfinal.weixin.sdk.api.ApiConfigKit;
import com.jfinal.weixin.sdk.kit.SignatureCheckKit;
import com.jfinal.wxaapp.WxaConfigKit;
import net.dreamlu.weixin.annotation.WxApi;
import net.dreamlu.weixin.properties.DreamWeixinProperties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MsgInterceptor extends HandlerInterceptorAdapter {
private static final Log logger = LogFactory.getLog(MsgInterceptor.class);
private static final Log logger = LogFactory.getLog(MsgInterceptor.class);
private final DreamWeixinProperties weixinProperties;
private final DreamWeixinProperties weixinProperties;
public MsgInterceptor(DreamWeixinProperties weixinProperties) {
this.weixinProperties = weixinProperties;
}
public MsgInterceptor(DreamWeixinProperties weixinProperties) {
this.weixinProperties = weixinProperties;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 非控制器请求直接跳出
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 如果是 api 接口
Class<?> beanType = handlerMethod.getBeanType();
// 处理接口业务
WxApi wxApi = AnnotationUtils.getAnnotation(beanType, WxApi.class);
String appId = request.getParameter(weixinProperties.getAppIdKey());
if (wxApi != null) {
ApiConfigKit.setThreadLocalAppId(appId);
return true;
}
Object bean = handlerMethod.getBean();
// 小程序直接跳出
if (bean instanceof DreamWxaMsgController) {
return true;
}
/**
* 将 appId 与当前线程绑定,以便在后续操作中方便获取ApiConfig对象:
* <pre>
* ApiConfigKit.getApiConfig();
* </pre>
*/
ApiConfigKit.setThreadLocalAppId(appId);
// 如果是服务器配置请求,则配置服务器并返回
if (isConfigServerRequest(request)) {
configServer(request, response);
return false;
}
// 对开发测试更加友好
if (ApiConfigKit.isDevMode()) {
return true;
} else {
// 签名检测
if (checkSignature(request, response)) {
return true;
} else {
WebUtils.renderText(response, "签名验证失败,请确定是微信服务器在发送消息过来");
return false;
}
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 非控制器请求直接跳出
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 如果是 api 接口
Class<?> beanType = handlerMethod.getBeanType();
// 处理接口业务
WxApi wxApi = AnnotationUtils.getAnnotation(beanType, WxApi.class);
String appId = request.getParameter(weixinProperties.getAppIdKey());
if (wxApi != null) {
ApiConfigKit.setThreadLocalAppId(appId);
return true;
}
Object bean = handlerMethod.getBean();
// 小程序直接跳出
if (bean instanceof DreamWxaMsgController) {
return true;
}
/**
* 将 appId 与当前线程绑定,以便在后续操作中方便获取ApiConfig对象:
* <pre>
* ApiConfigKit.getApiConfig();
* </pre>
*/
ApiConfigKit.setThreadLocalAppId(appId);
// 如果是服务器配置请求,则配置服务器并返回
if (isConfigServerRequest(request)) {
configServer(request, response);
return false;
}
// 对开发测试更加友好
if (ApiConfigKit.isDevMode()) {
return true;
} else {
// 签名检测
if (checkSignature(request, response)) {
return true;
} else {
WebUtils.renderText(response, "签名验证失败,请确定是微信服务器在发送消息过来");
return false;
}
}
}
}
/**
* 检测签名
*/
private boolean checkSignature(HttpServletRequest request, HttpServletResponse response) {
String signature = request.getParameter("signature");
String timestamp = request.getParameter("timestamp");
String nonce = request.getParameter("nonce");
if (StrKit.isBlank(signature) || StrKit.isBlank(timestamp) || StrKit.isBlank(nonce)) {
logger.error("check signature failure");
return false;
}
if (SignatureCheckKit.me.checkSignature(signature, timestamp, nonce)) {
return true;
} else {
logger.error("check signature failure: " +
" signature = " + signature +
" timestamp = " + timestamp +
" nonce = " + nonce);
return false;
}
}
/**
* 检测签名
*/
private boolean checkSignature(HttpServletRequest request, HttpServletResponse response) {
String signature = request.getParameter("signature");
String timestamp = request.getParameter("timestamp");
String nonce = request.getParameter("nonce");
if (StrKit.isBlank(signature) || StrKit.isBlank(timestamp) || StrKit.isBlank(nonce)) {
logger.error("check signature failure");
return false;
}
if (SignatureCheckKit.me.checkSignature(signature, timestamp, nonce)) {
return true;
} else {
logger.error("check signature failure: " +
" signature = " + signature +
" timestamp = " + timestamp +
" nonce = " + nonce);
return false;
}
}
/**
* 是否为开发者中心保存服务器配置的请求
*/
private boolean isConfigServerRequest(HttpServletRequest request) {
return StrKit.notBlank(request.getParameter("echostr"));
}
/**
* 是否为开发者中心保存服务器配置的请求
*/
private boolean isConfigServerRequest(HttpServletRequest request) {
return StrKit.notBlank(request.getParameter("echostr"));
}
/**
* 配置开发者中心微信服务器所需的 url 与 token
*
* @param request HttpServletRequest
* @param response HttpServletResponse
*/
private void configServer(HttpServletRequest request, HttpServletResponse response) {
// 通过 echostr 判断请求是否为配置微信服务器回调所需的 url 与 token
String echostr = request.getParameter("echostr");
String signature = request.getParameter("signature");
String timestamp = request.getParameter("timestamp");
String nonce = request.getParameter("nonce");
boolean isOk = SignatureCheckKit.me.checkSignature(signature, timestamp, nonce);
if (isOk && !response.isCommitted()) {
WebUtils.renderText(response, echostr);
} else {
logger.error("验证失败:configServer");
}
}
/**
* 配置开发者中心微信服务器所需的 url 与 token
*
* @param request HttpServletRequest
* @param response HttpServletResponse
*/
private void configServer(HttpServletRequest request, HttpServletResponse response) {
// 通过 echostr 判断请求是否为配置微信服务器回调所需的 url 与 token
String echostr = request.getParameter("echostr");
String signature = request.getParameter("signature");
String timestamp = request.getParameter("timestamp");
String nonce = request.getParameter("nonce");
boolean isOk = SignatureCheckKit.me.checkSignature(signature, timestamp, nonce);
if (isOk && !response.isCommitted()) {
WebUtils.renderText(response, echostr);
} else {
logger.error("验证失败:configServer");
}
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
super.afterCompletion(request, response, handler, ex);
ApiConfigKit.removeThreadLocalAppId();
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
super.afterCompletion(request, response, handler, ex);
ApiConfigKit.removeThreadLocalAppId();
}
}
......@@ -8,20 +8,21 @@ import java.io.IOException;
import java.io.PrintWriter;
class WebUtils {
private static final Log logger = LogFactory.getLog(WebUtils.class);
/**
* 返回json
*
* @param response HttpServletResponse
* @param text 文本
*/
public static void renderText(HttpServletResponse response, String text) {
response.setCharacterEncoding("UTF-8");
response.setContentType("text/plain;charset:utf-8;");
try (PrintWriter out = response.getWriter()) {
out.append(text);
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
}
private static final Log logger = LogFactory.getLog(WebUtils.class);
/**
* 返回json
*
* @param response HttpServletResponse
* @param text 文本
*/
public static void renderText(HttpServletResponse response, String text) {
response.setCharacterEncoding("UTF-8");
response.setContentType("text/plain;charset:utf-8;");
try (PrintWriter out = response.getWriter()) {
out.append(text);
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
}
}
restart.include.weixin=/spring-boot-starter-weixin-[\\w-]+\.jar
\ No newline at end of file
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
net.dreamlu.weixin.properties.DreamWeixinProperties,\
net.dreamlu.weixin.config.WeixinAppConfig,\
net.dreamlu.weixin.config.DreamWeixinAutoConfiguration
......@@ -14,8 +14,8 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<!--<version>1.5.12.RELEASE</version>-->
<version>2.0.4.RELEASE</version>
<version>1.5.12.RELEASE</version>
<!--<version>2.0.4.RELEASE</version>-->
<relativePath /> <!-- lookup parent from repository -->
</parent>
......@@ -33,7 +33,7 @@
<dependency>
<groupId>net.dreamlu</groupId>
<artifactId>spring-boot-starter-weixin</artifactId>
<version>1.3.0</version>
<version>1.3.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册