tanghc 6 years ago
parent 85d61d2f1e
commit 46a546f1da
  1. 42
      .gitignore
  2. 58
      README.md
  3. 286
      mvnw
  4. 161
      mvnw.cmd
  5. 20
      pom.xml
  6. 15
      sop-book/pom.xml
  7. 25
      sop-book/sop-book-api/.gitignore
  8. 46
      sop-book/sop-book-api/pom.xml
  9. 12
      sop-book/sop-book-api/src/main/java/com/hhdd/book/api/domain/Book.java
  10. 14
      sop-book/sop-book-api/src/main/java/com/hhdd/book/api/service/BookService.java
  11. 25
      sop-book/sop-book-web/.gitignore
  12. BIN
      sop-book/sop-book-web/.mvn/wrapper/maven-wrapper.jar
  13. 1
      sop-book/sop-book-web/.mvn/wrapper/maven-wrapper.properties
  14. 286
      sop-book/sop-book-web/mvnw
  15. 161
      sop-book/sop-book-web/mvnw.cmd
  16. 84
      sop-book/sop-book-web/pom.xml
  17. 20
      sop-book/sop-book-web/src/main/java/com/gitee/sop/bookweb/SopBookApplication.java
  18. 14
      sop-book/sop-book-web/src/main/java/com/gitee/sop/bookweb/consumer/StoryServiceConsumer.java
  19. 74
      sop-book/sop-book-web/src/main/java/com/gitee/sop/bookweb/controller/ApiResult.java
  20. 73
      sop-book/sop-book-web/src/main/java/com/gitee/sop/bookweb/controller/BookController.java
  21. 11
      sop-book/sop-book-web/src/main/java/com/gitee/sop/bookweb/param/BookParam.java
  22. 6
      sop-book/sop-book-web/src/main/resources/application.properties
  23. 17
      sop-book/sop-book-web/src/test/java/com/gitee/sop/bookweb/SopBookApplicationTests.java
  24. 25
      sop-gateway-common/.gitignore
  25. 131
      sop-gateway-common/pom.xml
  26. 3
      sop-gateway-common/readme.md
  27. 116
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiConfig.java
  28. 186
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiContext.java
  29. 17
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/RequestMode.java
  30. 29
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ServiceApiInfo.java
  31. 33
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/SopConstants.java
  32. 16
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/configuration/AlipayZuulConfiguration.java
  33. 112
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/configuration/BaseZuulConfiguration.java
  34. 44
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/configuration/BaseZuulController.java
  35. 23
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/configuration/TaobaoZuulConfiguration.java
  36. 20
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/easyopen/EasyopenRoute.java
  37. 74
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/easyopen/EasyopenZuulConfig.java
  38. 46
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/easyopen/filter/PostEasyopenResultFilter.java
  39. 26
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/exception/ApiException.java
  40. 96
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/filter/BaseZuulFilter.java
  41. 59
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/filter/ErrorFilter.java
  42. 49
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/filter/PostResultFilter.java
  43. 41
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/filter/PreTokenFilter.java
  44. 48
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/filter/PreValidateFilter.java
  45. 25
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/ApiMetaChangeListener.java
  46. 20
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/ApiMetaConfig.java
  47. 13
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/ApiMetaContext.java
  48. 22
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/ApiMetaManager.java
  49. 62
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultApiMetaContext.java
  50. 51
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultApiMetaManager.java
  51. 45
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/SopRouteLocator.java
  52. 48
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/message/Error.java
  53. 66
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/message/ErrorEnum.java
  54. 120
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/message/ErrorFactory.java
  55. 23
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/message/ErrorImpl.java
  56. 47
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/message/ErrorMeta.java
  57. 197
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ApiParam.java
  58. 104
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ApiParamParser.java
  59. 42
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ApiUploadContext.java
  60. 70
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/Param.java
  61. 46
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ParamNames.java
  62. 19
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ParamParser.java
  63. 36
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/UploadContext.java
  64. 77
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/ApiResult.java
  65. 125
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/ApiResultExecutor.java
  66. 16
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/JsonResultSerializer.java
  67. 12
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/Result.java
  68. 27
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/ResultBuilder.java
  69. 10
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/ResultExecutor.java
  70. 16
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/ResultSerializer.java
  71. 16
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/XmlResultSerializer.java
  72. 32
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/secret/AppSecretManager.java
  73. 33
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/secret/CacheAppSecretManager.java
  74. 56
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/secret/FileAppSecretManager.java
  75. 242
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/session/ApiHttpSession.java
  76. 30
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/session/ApiRedisTemplate.java
  77. 100
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/session/ApiSessionManager.java
  78. 264
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/session/RedisHttpSession.java
  79. 100
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/session/RedisSessionManager.java
  80. 20
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/session/SessionManager.java
  81. 146
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/AESUtil.java
  82. 25
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/KeyStore.java
  83. 208
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RSANewUtil.java
  84. 312
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RSAUtil.java
  85. 139
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RedisLockUtil.java
  86. 137
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RequestUtil.java
  87. 119
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/ResponseUtil.java
  88. 32
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/XmlUtil.java
  89. 36
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/AbstractSigner.java
  90. 60
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/ApiEncrypter.java
  91. 56
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/ApiSigner.java
  92. 150
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/ApiValidator.java
  93. 95
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/Encrypter.java
  94. 13
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/SignEncipher.java
  95. 37
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/SignEncipherHMAC_MD5.java
  96. 16
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/SignEncipherMD5.java
  97. 20
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/Signer.java
  98. 18
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/Validator.java
  99. 97
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/alipay/AlipayConstants.java
  100. 616
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/alipay/AlipaySignature.java
  101. Some files were not shown because too many files have changed in this diff Show More

42
.gitignore vendored

@ -1,23 +1,25 @@
# Compiled class file
*.class
/target/
!.mvn/wrapper/maven-wrapper.jar
# Log file
*.log
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
# BlueJ files
*.ctxt
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
/build/

@ -1,37 +1,45 @@
# SOP
# SOP(Simple Open Platform)
#### 介绍
一个简单易用的接口开放平台(Simple Open Platform),可实现类似于淘宝开放平台的调用方式。
一个开放平台解决方案项目,基于Spring Cloud实现,目标是能够让用户快速得搭建起自己的开放平台。
#### 软件架构
软件架构说明
SOP提供了两种接口调用方式,分别是:[支付宝开放平台](https://docs.open.alipay.com/api)的调用方式和[淘宝开放平台](http://open.taobao.com/api.htm?docId=285&docType=2)的调用方式。
通过简单的配置后,你的项目就具备了和支付宝开放平台的一样的接口提供能力。
SOP封装了开放平台大部分功能包括:签名验证、统一异常处理、统一返回内容 、业务参数验证(JSR-303)、秘钥管理等,未来还会实现更多功能。
#### 安装教程
## 项目特点
1. xxxx
2. xxxx
3. xxxx
- 接入方式简单,与老项目不冲突,老项目注册到注册中心,然后的方法上加上注解即可。
- 架构松耦合,业务代码实现在各自微服务上,SOP不参与业务实现,这也是Spring Cloud微服务体系带来的好处。
- 扩展简单,开放平台对应的功能各自独立,可以自定义实现自己的需求,如:更改参数,更改签名规则等。
#### 使用说明
## 谁能使用这个项目
1. xxxx
2. xxxx
3. xxxx
- 有现成的项目,想改造成开放平台供他人调用
- 有现成的项目,想暴露其中几个接口并通过开放平台供他人调用
- 想搭一个开放平台新项目,并结合微服务的方式去维护
- 对开放平台感兴趣的朋友
#### 参与贡献
以上情况都可以考虑使用SOP
1. Fork 本仓库
2. 新建 Feat_xxx 分支
3. 提交代码
4. 新建 Pull Request
## 架构图
#### 码云特技
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
2. 码云官方博客 [blog.gitee.com](https://blog.gitee.com)
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解码云上的优秀开源项目
4. [GVP](https://gitee.com/gvp) 全称是码云最有价值开源项目,是码云综合评定出的优秀开源项目
5. 码云官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
6. 码云封面人物是一档用来展示码云会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
## 工程说明
- sop-registry:注册中心,eureka实现
- sop-gateway:网关,zuul实现,统一访问入口
- sop-gateway-common:网关公共模块,封装常用功能,包含签名校验等功能。
- sop-server-common:微服务端公共模块,封装配套功能
- sop-story:示例,story服务,同时作为Provider提供服务
- sop-book:示例,book服务,也是Consumer,调用story提供的服务
- sop-test:测试用例
## 相关文档
## 沟通交流
Q群:328419269

286
mvnw vendored

@ -0,0 +1,286 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Maven2 Start Up Batch script
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# M2_HOME - location of maven2's installed home dir
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ] ; then
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
mingw=false
case "`uname`" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
if [ -z "$JAVA_HOME" ]; then
if [ -x "/usr/libexec/java_home" ]; then
export JAVA_HOME="`/usr/libexec/java_home`"
else
export JAVA_HOME="/Library/Java/Home"
fi
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=`java-config --jre-home`
fi
fi
if [ -z "$M2_HOME" ] ; then
## resolve links - $0 may be a link to maven's home
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG="`dirname "$PRG"`/$link"
fi
done
saveddir=`pwd`
M2_HOME=`dirname "$PRG"`/..
# make it fully qualified
M2_HOME=`cd "$M2_HOME" && pwd`
cd "$saveddir"
# echo Using m2 at $M2_HOME
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --unix "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
fi
# For Mingw, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$M2_HOME" ] &&
M2_HOME="`(cd "$M2_HOME"; pwd)`"
[ -n "$JAVA_HOME" ] &&
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
# TODO classpath?
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="`which javac`"
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=`which readlink`
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
if $darwin ; then
javaHome="`dirname \"$javaExecutable\"`"
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
else
javaExecutable="`readlink -f \"$javaExecutable\"`"
fi
javaHome="`dirname \"$javaExecutable\"`"
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="`which java`"
fi
fi
if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
if [ -z "$1" ]
then
echo "Path not specified to find_maven_basedir"
return 1
fi
basedir="$1"
wdir="$1"
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
wdir=`cd "$wdir/.."; pwd`
fi
# end of workaround
done
echo "${basedir}"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
echo "$(tr -s '\n' ' ' < "$1")"
fi
}
BASE_DIR=`find_maven_basedir "$(pwd)"`
if [ -z "$BASE_DIR" ]; then
exit 1;
fi
##########################################################################################
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
# This allows using the maven wrapper in projects that prohibit checking in binary data.
##########################################################################################
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found .mvn/wrapper/maven-wrapper.jar"
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
fi
jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"
while IFS="=" read key value; do
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
esac
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
if [ "$MVNW_VERBOSE" = true ]; then
echo "Downloading from: $jarUrl"
fi
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
if command -v wget > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found wget ... using wget"
fi
wget "$jarUrl" -O "$wrapperJarPath"
elif command -v curl > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found curl ... using curl"
fi
curl -o "$wrapperJarPath" "$jarUrl"
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Falling back to using Java to download"
fi
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
if [ -e "$javaClass" ]; then
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Compiling MavenWrapperDownloader.java ..."
fi
# Compiling the Java class
("$JAVA_HOME/bin/javac" "$javaClass")
fi
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
# Running the downloader
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Running MavenWrapperDownloader.java ..."
fi
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
if [ "$MVNW_VERBOSE" = true ]; then
echo $MAVEN_PROJECTBASEDIR
fi
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --path --windows "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
fi
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
exec "$JAVACMD" \
$MAVEN_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"

161
mvnw.cmd vendored

@ -0,0 +1,161 @@
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Maven2 Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM set title of command window
title %0
@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"
FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO (
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
)
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
if exist %WRAPPER_JAR% (
echo Found %WRAPPER_JAR%
) else (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %DOWNLOAD_URL%
powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"
echo Finished downloading %WRAPPER_JAR%
)
@REM End of extension
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%" == "on" pause
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
exit /B %ERROR_CODE%

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>sop-registry</module>
<module>sop-gateway</module>
<module>sop-gateway-common</module>
<module>sop-server-common</module>
<module>sop-story</module>
<module>sop-book</module>
<module>sop-test</module>
</modules>
</project>

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-book-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>sop-book-api</module>
<module>sop-book-web</module>
</modules>
</project>

@ -0,0 +1,25 @@
/target/
!.mvn/wrapper/maven-wrapper.jar
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
/build/

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-book-api</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
<scope>provided</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>

@ -0,0 +1,12 @@
package com.gitee.book.api.domain;
import lombok.Data;
/**
* @author tanghc
*/
@Data
public class Book {
private int id;
private String name;
}

@ -0,0 +1,14 @@
package com.hhdd.book.api.service;
import com.gitee.book.api.domain.Book;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* @author tanghc
*/
@RequestMapping("/book")
public interface BookService {
@RequestMapping("/getBook")
Book getBook(@RequestParam("id") int id);
}

@ -0,0 +1,25 @@
/target/
!.mvn/wrapper/maven-wrapper.jar
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
/build/

@ -0,0 +1 @@
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.0/apache-maven-3.6.0-bin.zip

@ -0,0 +1,286 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Maven2 Start Up Batch script
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# M2_HOME - location of maven2's installed home dir
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ] ; then
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
mingw=false
case "`uname`" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
if [ -z "$JAVA_HOME" ]; then
if [ -x "/usr/libexec/java_home" ]; then
export JAVA_HOME="`/usr/libexec/java_home`"
else
export JAVA_HOME="/Library/Java/Home"
fi
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=`java-config --jre-home`
fi
fi
if [ -z "$M2_HOME" ] ; then
## resolve links - $0 may be a link to maven's home
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG="`dirname "$PRG"`/$link"
fi
done
saveddir=`pwd`
M2_HOME=`dirname "$PRG"`/..
# make it fully qualified
M2_HOME=`cd "$M2_HOME" && pwd`
cd "$saveddir"
# echo Using m2 at $M2_HOME
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --unix "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
fi
# For Mingw, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$M2_HOME" ] &&
M2_HOME="`(cd "$M2_HOME"; pwd)`"
[ -n "$JAVA_HOME" ] &&
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
# TODO classpath?
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="`which javac`"
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=`which readlink`
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
if $darwin ; then
javaHome="`dirname \"$javaExecutable\"`"
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
else
javaExecutable="`readlink -f \"$javaExecutable\"`"
fi
javaHome="`dirname \"$javaExecutable\"`"
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="`which java`"
fi
fi
if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
if [ -z "$1" ]
then
echo "Path not specified to find_maven_basedir"
return 1
fi
basedir="$1"
wdir="$1"
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
wdir=`cd "$wdir/.."; pwd`
fi
# end of workaround
done
echo "${basedir}"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
echo "$(tr -s '\n' ' ' < "$1")"
fi
}
BASE_DIR=`find_maven_basedir "$(pwd)"`
if [ -z "$BASE_DIR" ]; then
exit 1;
fi
##########################################################################################
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
# This allows using the maven wrapper in projects that prohibit checking in binary data.
##########################################################################################
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found .mvn/wrapper/maven-wrapper.jar"
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
fi
jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"
while IFS="=" read key value; do
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
esac
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
if [ "$MVNW_VERBOSE" = true ]; then
echo "Downloading from: $jarUrl"
fi
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
if command -v wget > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found wget ... using wget"
fi
wget "$jarUrl" -O "$wrapperJarPath"
elif command -v curl > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found curl ... using curl"
fi
curl -o "$wrapperJarPath" "$jarUrl"
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Falling back to using Java to download"
fi
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
if [ -e "$javaClass" ]; then
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Compiling MavenWrapperDownloader.java ..."
fi
# Compiling the Java class
("$JAVA_HOME/bin/javac" "$javaClass")
fi
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
# Running the downloader
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Running MavenWrapperDownloader.java ..."
fi
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
if [ "$MVNW_VERBOSE" = true ]; then
echo $MAVEN_PROJECTBASEDIR
fi
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --path --windows "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
fi
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
exec "$JAVACMD" \
$MAVEN_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"

@ -0,0 +1,161 @@
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Maven2 Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM set title of command window
title %0
@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"
FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO (
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
)
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
if exist %WRAPPER_JAR% (
echo Found %WRAPPER_JAR%
) else (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %DOWNLOAD_URL%
powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"
echo Finished downloading %WRAPPER_JAR%
)
@REM End of extension
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%" == "on" pause
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
exit /B %ERROR_CODE%

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-book-web</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>sop-story</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-book-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-story-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
<scope>compile</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
</project>

@ -0,0 +1,20 @@
package com.gitee.sop.bookweb;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
// 允许调用其他服务
@EnableFeignClients
// 服务注册
@EnableDiscoveryClient
@SpringBootApplication
public class SopBookApplication {
public static void main(String[] args) {
SpringApplication.run(SopBookApplication.class, args);
}
}

@ -0,0 +1,14 @@
package com.gitee.sop.bookweb.consumer;
import com.gitee.sop.story.api.service.StoryService;
import org.springframework.cloud.openfeign.FeignClient;
/**
* 调用story服务
*
* @author tanghc
*/
// value对应的spring.application.name
@FeignClient("story-service")
public interface StoryServiceConsumer extends StoryService {
}

@ -0,0 +1,74 @@
package com.gitee.sop.bookweb.controller;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.Data;
/**
* 默认的结果封装类.
* <pre>
*
* xml返回结果:
* <response>
* <code>50</code>
* <msg>Remote service error</msg>
* <sub_code>isv.invalid-parameter</sub_code>
* <sub_msg>非法参数</sub_msg>
* </response>
* 成功情况
* <response>
* <code>0</code>
* <msg>成功消息</msg>
* <data>
* ...返回内容
* </data>
* </response>
*
* json返回格式
* {
* "code":"50",
* "msg":"Remote service error",
* "sub_code":"isv.invalid-parameter",
* "sub_msg":"非法参数"
* }
* 成功情况
* {
* "code":"0",
* "msg":"成功消息内容。。。",
* "data":{
* ...返回内容
* }
* }
* </pre>
* <p>
* 字段说明
* code:网关异常码 <br>
* msg:网关异常信息 <br>
* sub_code:业务异常码 <br>
* sub_msg:业务异常信息 <br>
*
* @author tanghc
*/
@XStreamAlias("response")
@Data
public class ApiResult {
/**
* 网关异常信息
*/
private String msg;
/**
* 业务异常码
*/
private String sub_msg;
/**
* 业务异常信息
*/
private String sub_code;
/**
* 返回结果
*/
private Object data;
}

@ -0,0 +1,73 @@
package com.gitee.sop.bookweb.controller;
import com.gitee.book.api.domain.Book;
import com.gitee.sop.bookweb.consumer.StoryServiceConsumer;
import com.gitee.sop.bookweb.param.BookParam;
import com.gitee.sop.story.api.domain.Story;
import com.hhdd.book.api.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Arrays;
/**
* book服务
*
* @author tanghc
*/
@RestController
public class BookController implements BookService {
@Autowired
StoryServiceConsumer storyServiceConsumer;
@Override
public Book getBook(int id) {
Book book = new Book();
book.setId(id);
book.setName("汪汪队");
return book;
}
@RequestMapping("listBookAndStory")
public Object listBookAndStory(int id) {
Book book = new Book();
book.setId(id);
book.setName("汪汪队");
// 调用story服务
Story story = storyServiceConsumer.getStory(id);
return Arrays.asList(book, story);
}
@RequestMapping("getBook2")
public Object getBookError(int id) {
if (id == 0) {
throw new RuntimeException("id不能为空");
}
Book book = new Book();
book.setId(id);
book.setName("汪汪队");
return Arrays.asList(book);
}
@RequestMapping("getBook3")
public Object getBook3(@RequestBody BookParam param) {
if (param.getId() == 0) {
throw new RuntimeException("id不能为空");
}
Book book = new Book();
book.setId(param.getId());
book.setName("小马宝莉");
ApiResult apiResult = new ApiResult();
apiResult.setData(book);
return apiResult;
}
}

@ -0,0 +1,11 @@
package com.gitee.sop.bookweb.param;
import lombok.Data;
/**
* @author tanghc
*/
@Data
public class BookParam {
private int id;
}

@ -0,0 +1,6 @@
server.port=3333
spring.application.name=book-service
eureka.host=localhost
eureka.port=1111
eureka.client.serviceUrl.defaultZone=http://${eureka.host}:${eureka.port}/eureka/

@ -0,0 +1,17 @@
package com.gitee.sop.bookweb;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SopBookApplicationTests {
@Test
public void contextLoads() {
}
}

@ -0,0 +1,25 @@
/target/
!.mvn/wrapper/maven-wrapper.jar
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
/build/

@ -0,0 +1,131 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-gateway-common</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>sop-gateway-common</name>
<description>sop-gateway-common</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
<!-- Logging -->
<logback.version>1.0.13</logback.version>
<slf4j.version>1.7.5</slf4j.version>
<!-- Test -->
<junit.version>4.11</junit.version>
<fastjson.version>1.2.15</fastjson.version>
<commons-io.version>2.5</commons-io.version>
<commons-fileupload.version>1.3.3</commons-fileupload.version>
<commons-collection.version>3.2.2</commons-collection.version>
<xstream.version>1.4.7</xstream.version>
<velocity.version>1.7</velocity.version>
<hibernate-validator.version>6.0.10.Final</hibernate-validator.version>
<oltu.version>0.31</oltu.version>
<jwt.version>3.2.0</jwt.version>
<guava.version>18.0</guava.version>
<spring-data-redis.version>1.8.8.RELEASE</spring-data-redis.version>
<netty.version>4.1.25.Final</netty.version>
<jboss-marshalling-serial.version>2.0.5.Final</jboss-marshalling-serial.version>
<itextpdf.version>5.5.12</itextpdf.version>
<xmlworker.version>5.5.8</xmlworker.version>
<itext-asian.version>5.2.0</itext-asian.version>
<asm.version>6.2</asm.version>
<commons-codec.version>1.11</commons-codec.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>${xstream.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- commons -->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>${commons-collection.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>${commons-fileupload.version}</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>${commons-codec.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
<scope>provided</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
</project>

@ -0,0 +1,3 @@
# sop-gateway-common
zuul通用组件,主要封装网关的一些功能。

@ -0,0 +1,116 @@
package com.gitee.sop.gatewaycommon.bean;
import com.gitee.sop.gatewaycommon.configuration.BaseZuulController;
import com.gitee.sop.gatewaycommon.param.ApiParamParser;
import com.gitee.sop.gatewaycommon.param.ParamParser;
import com.gitee.sop.gatewaycommon.result.ApiResultExecutor;
import com.gitee.sop.gatewaycommon.result.JsonResultSerializer;
import com.gitee.sop.gatewaycommon.result.ResultExecutor;
import com.gitee.sop.gatewaycommon.result.ResultSerializer;
import com.gitee.sop.gatewaycommon.result.XmlResultSerializer;
import com.gitee.sop.gatewaycommon.secret.AppSecretManager;
import com.gitee.sop.gatewaycommon.secret.CacheAppSecretManager;
import com.gitee.sop.gatewaycommon.session.ApiSessionManager;
import com.gitee.sop.gatewaycommon.session.SessionManager;
import com.gitee.sop.gatewaycommon.validate.ApiEncrypter;
import com.gitee.sop.gatewaycommon.validate.ApiSigner;
import com.gitee.sop.gatewaycommon.validate.ApiValidator;
import com.gitee.sop.gatewaycommon.validate.Encrypter;
import com.gitee.sop.gatewaycommon.validate.Signer;
import com.gitee.sop.gatewaycommon.validate.Validator;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author tanghc
*/
@Data
public class ApiConfig {
private static ApiConfig instance = new ApiConfig();
private ApiConfig() {
}
/**
* 合并结果处理
*/
private ResultExecutor resultExecutor = new ApiResultExecutor();
/**
* json序列化
*/
private ResultSerializer jsonResultSerializer = new JsonResultSerializer();
/**
* xml序列化
*/
private ResultSerializer xmlResultSerializer = new XmlResultSerializer();
/**
* app秘钥管理
*/
private AppSecretManager appSecretManager = new CacheAppSecretManager();
/**
* 加密工具
*/
private Encrypter encrypter = new ApiEncrypter();
/**
* 签名工具
*/
private Signer signer = new ApiSigner();
/**
* 参数解析
*/
private ParamParser paramParser = new ApiParamParser();
/**
* 验证
*/
private Validator validator = new ApiValidator();
/**
* session管理
*/
private SessionManager sessionManager = new ApiSessionManager();
/**
* 错误模块
*/
private List<String> i18nModules = new ArrayList<String>();
/**
* 基础Controller
*/
private BaseZuulController baseZuulController = new BaseZuulController();
// -------- fields ---------
/**
* 忽略验证
*/
private boolean ignoreValidate;
/**
* 超时时间
*/
private int timeoutSeconds = 60 * 5;
public void addAppSecret(Map<String, String> appSecretPair) {
this.appSecretManager.addAppSecret(appSecretPair);
}
public static ApiConfig getInstance() {
return instance;
}
public static void setInstance(ApiConfig apiConfig) {
instance = apiConfig;
}
}

@ -0,0 +1,186 @@
package com.gitee.sop.gatewaycommon.bean;
import com.gitee.sop.gatewaycommon.param.UploadContext;
import com.netflix.zuul.context.RequestContext;
import com.gitee.sop.gatewaycommon.param.ApiParam;
import com.gitee.sop.gatewaycommon.session.SessionManager;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Locale;
/**
* 应用上下文,方便获取信息
*
* @author tanghc
*
*/
public class ApiContext {
private static final String ATTR_PARAM = "zuul.common.api.param";
private static final String ATTR_UPLOAD_CONTEXT = "zuul.common.api.upload_context";
private ApiContext(){}
private static void setAttr(String name, Object val) {
HttpServletRequest request = getRequest();
if (request != null) {
request.setAttribute(name, val);
}
}
private static Object getAttr(String name) {
HttpServletRequest request = getRequest();
if (request == null) {
return null;
}
return request.getAttribute(name);
}
/**
* 获取随机码
* @return 返回随机码
*/
public static String getRandomKey() {
HttpSession session = getSession();
if (session == null) {
return null;
}
return (String) session.getAttribute(SopConstants.RANDOM_KEY_NAME);
}
/**
* 获取HttpServletRequest
*
* @return HttpServletRequest
*/
public static HttpServletRequest getRequest() {
return RequestContext.getCurrentContext().getRequest();
}
/**
* 返回默认的HttpServletRequest.getSession();
*
* @return 没有返回null
*/
public static HttpSession getSession() {
HttpServletRequest req = getRequest();
if (req == null) {
return null;
} else {
return req.getSession();
}
}
/**
* 获取session管理器
* @return 返回SessionManager
*/
public static SessionManager getSessionManager() {
return getApiConfig().getSessionManager();
}
/**
* 返回自定义的session,被SessionManager管理
*
* @return 如果sessionId为null则返回null
*/
public static HttpSession getManagedSession() {
String sessionId = getSessionId();
if (sessionId != null) {
return getSessionManager().getSession(sessionId);
} else {
return null;
}
}
/**
* 同getSessionId()
* @return 返回accessToken,没有返回null
*/
public static String getAccessToken() {
return getSessionId();
}
/**
* 获取登陆的token
*
* @return 没有返回null
*/
public static String getSessionId() {
ApiParam apiParam = getApiParam();
if (apiParam == null) {
return null;
}
return apiParam.fetchAccessToken();
}
/**
* 获取本地化从HttpServletRequest中获取没有则返回Locale.SIMPLIFIED_CHINESE
*
* @return Locale
*/
public static Locale getLocale() {
HttpServletRequest req = getRequest();
if (req == null) {
return Locale.SIMPLIFIED_CHINESE;
}
return req.getLocale();
}
public static void setApiParam(ApiParam apiParam) {
setAttr(ATTR_PARAM, apiParam);
}
/**
* 获取系统参数
*
* @return 返回ApiParam
*/
public static ApiParam getApiParam() {
return (ApiParam) getAttr(ATTR_PARAM);
}
public static ApiConfig getApiConfig() {
return ApiConfig.getInstance();
}
public static void setApiConfig(ApiConfig apiConfig) {
ApiConfig.setInstance(apiConfig);
}
public static ServletContext getServletContext() {
ServletContext ctx = null;
HttpSession session = getSession();
if (session != null) {
ctx = session.getServletContext();
}
return ctx;
}
/**
* 获取上传文件如果客户端有文件上传从这里取
* @return 如果没有文件上传返回null
*/
public static UploadContext getUploadContext() {
return (UploadContext) getAttr(ATTR_UPLOAD_CONTEXT);
}
public static void setUploadContext(UploadContext uploadCtx) {
setAttr(ATTR_UPLOAD_CONTEXT, uploadCtx);
}
/**
* 获取response
* @return 返回response
*/
public static HttpServletResponse getResponse() {
return RequestContext.getCurrentContext().getResponse();
}
}

@ -0,0 +1,17 @@
package com.gitee.sop.gatewaycommon.bean;
/**
* @author tanghc
*/
public enum RequestMode {
/**
* 数字签名请求模式
*/
SIGNATURE,
/**
* 公私钥加密模式这样请求和返回的数据都经过加密处理
*/
ENCRYPT
}

@ -0,0 +1,29 @@
package com.gitee.sop.gatewaycommon.bean;
import lombok.Data;
import java.util.List;
/**
* @author tanghc
*/
@Data
public class ServiceApiInfo {
private String md5;
private String appName;
private List<ApiMeta> apis;
@Data
public static class ApiMeta {
/** 接口名 */
private String name;
/** 请求path */
private String path;
/** 版本号 */
private String version;
public String fetchNameVersion() {
return name + version;
}
}
}

@ -0,0 +1,33 @@
package com.gitee.sop.gatewaycommon.bean;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
/**
* @author tanghc
*/
public class SopConstants {
public static final String NULL = "null";
public static final String RANDOM_KEY_NAME = "ssl_randomKey";
public static final Charset CHARSET_UTF8 = StandardCharsets.UTF_8;
public static final String FORMAT_JSON = "json";
public static final String FORMAT_XML = "xml";
public static final String AUTHORIZATION = "Authorization";
public static final String PREFIX_BEARER = "Bearer ";
public static final String YMDHMS = "yyyy-MM-dd HH:mm:ss";
public static final String DEFAULT_SIGN_METHOD = "md5";
public static final String CONTENT_TYPE_NAME = "Content-Type";
public static final String CONTENT_TYPE_JSON = "application/json;charset=UTF-8";
public static final String LINE = "\n";
public static final String EMPTY_JSON = "{}";
public static final String SORT_DESC = "DESC";
public static final String REST_PARAM_NAME = "_REST_PARAM_NAME_";
public static final String REST_PARAM_VERSION = "_REST_PARAM_VERSION_";
}

@ -0,0 +1,16 @@
package com.gitee.sop.gatewaycommon.configuration;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.validate.alipay.AlipaySigner;
/**
* 具备支付宝开放平台能力配置 https://docs.open.alipay.com/api
*
* @author tanghc
*/
public class AlipayZuulConfiguration extends BaseZuulConfiguration {
static {
ApiContext.getApiConfig().setSigner(new AlipaySigner());
}
}

@ -0,0 +1,112 @@
package com.gitee.sop.gatewaycommon.configuration;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.filter.ErrorFilter;
import com.gitee.sop.gatewaycommon.filter.PostResultFilter;
import com.gitee.sop.gatewaycommon.filter.PreValidateFilter;
import com.gitee.sop.gatewaycommon.manager.ApiMetaChangeListener;
import com.gitee.sop.gatewaycommon.manager.ApiMetaContext;
import com.gitee.sop.gatewaycommon.manager.ApiMetaManager;
import com.gitee.sop.gatewaycommon.manager.DefaultApiMetaContext;
import com.gitee.sop.gatewaycommon.manager.DefaultApiMetaManager;
import com.gitee.sop.gatewaycommon.manager.SopRouteLocator;
import com.gitee.sop.gatewaycommon.message.ErrorFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import javax.annotation.PostConstruct;
/**
* @author tanghc
*/
public class BaseZuulConfiguration {
public static final String API_CHANGE_CHANNEL = "channel.sop.api.change";
@Autowired
protected ZuulProperties zuulProperties;
@Autowired
protected ServerProperties server;
@Autowired
protected ApiMetaManager apiMetaManager;
@Bean
public ApiMetaContext apiMetaContext() {
return new DefaultApiMetaContext();
}
@Bean
public ApiMetaManager apiMetaManager(StringRedisTemplate stringRedisTemplate, ApiMetaContext apiMetaContext) {
return new DefaultApiMetaManager(stringRedisTemplate, apiMetaContext);
}
@Bean
PreValidateFilter preValidateFilter() {
return new PreValidateFilter();
}
@Bean
public PreDecorationFilter preDecorationFilter(ApiMetaContext apiMetaContext, ProxyRequestHelper proxyRequestHelper) {
SopRouteLocator routeLocator = new SopRouteLocator(apiMetaContext);
return new PreDecorationFilter(routeLocator,
this.server.getServlet().getContextPath(),
this.zuulProperties,
proxyRequestHelper);
}
@Bean
ErrorFilter errorFilter() {
return new ErrorFilter();
}
@Bean
PostResultFilter postResultFilter() {
return new PostResultFilter();
}
/**
* 配置redis事件订阅
*
* @param apiMetaManager
* @param redisTemplate
* @return
*/
@Bean
RedisMessageListenerContainer container(ApiMetaManager apiMetaManager, StringRedisTemplate redisTemplate) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(redisTemplate.getConnectionFactory());
ApiMetaChangeListener apiMetaChangeListener = new ApiMetaChangeListener(apiMetaManager, redisTemplate);
container.addMessageListener(apiMetaChangeListener, new PatternTopic(API_CHANGE_CHANNEL));
return container;
}
@Bean
BaseZuulController baseZuulController() {
return ApiContext.getApiConfig().getBaseZuulController();
}
@PostConstruct
public void after() {
doAfter();
}
protected void doAfter() {
initMessage();
apiMetaManager.refresh();
}
protected void initMessage() {
ErrorFactory.initMessageSource(ApiContext.getApiConfig().getI18nModules());
}
}

@ -0,0 +1,44 @@
package com.gitee.sop.gatewaycommon.configuration;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.result.ResultExecutor;
import com.netflix.zuul.context.RequestContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 处理网关自身异常
*
* @author tanghc
*/
@Controller
@Slf4j
public class BaseZuulController implements ErrorController {
public static final String ERROR_PATH = "/error";
// 错误最终会到这里来
@RequestMapping(ERROR_PATH)
@ResponseBody
public Object error(HttpServletRequest request, HttpServletResponse response) {
RequestContext ctx = RequestContext.getCurrentContext();
Throwable throwable = ctx.getThrowable();
return this.buildResult(throwable);
}
protected Object buildResult(Throwable throwable) {
ResultExecutor resultExecutor = ApiContext.getApiConfig().getResultExecutor();
return resultExecutor.mergeError(throwable);
}
@Override
public String getErrorPath() {
return ERROR_PATH;
}
}

@ -0,0 +1,23 @@
package com.gitee.sop.gatewaycommon.configuration;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.param.ParamNames;
import com.gitee.sop.gatewaycommon.validate.taobao.TaobaoSigner;
/**
* 具备淘宝开放平台能力配置
* 淘宝开放平台http://open.taobao.com/doc.htm
* @author tanghc
*/
public class TaobaoZuulConfiguration extends BaseZuulConfiguration {
static {
ParamNames.APP_KEY_NAME = "app_key";
ParamNames.SIGN_TYPE_NAME = "sign_method";
ParamNames.VERSION_NAME = "v";
ParamNames.APP_AUTH_TOKEN_NAME = "session";
ApiContext.getApiConfig().setSigner(new TaobaoSigner());
}
}

@ -0,0 +1,20 @@
package com.gitee.sop.gatewaycommon.easyopen;
import org.springframework.cloud.netflix.zuul.filters.Route;
import java.util.Collections;
import java.util.Set;
/**
* @author tanghc
*/
public class EasyopenRoute extends Route {
public EasyopenRoute(String id, String path, String location, String prefix, Boolean retryable, Set<String> ignoredHeaders) {
super(id, path, location, prefix, retryable, ignoredHeaders);
}
public EasyopenRoute(String id, String location) {
this(id, "/" + location + "/", location, "", false, Collections.emptySet());
}
}

@ -0,0 +1,74 @@
package com.gitee.sop.gatewaycommon.easyopen;
import com.gitee.sop.gatewaycommon.filter.ErrorFilter;
import com.gitee.sop.gatewaycommon.filter.PostResultFilter;
import com.gitee.sop.gatewaycommon.easyopen.filter.PostEasyopenResultFilter;
import com.gitee.sop.gatewaycommon.filter.PreValidateFilter;
import com.gitee.sop.gatewaycommon.manager.ApiMetaContext;
import com.gitee.sop.gatewaycommon.manager.ApiMetaManager;
import com.gitee.sop.gatewaycommon.manager.DefaultApiMetaContext;
import com.gitee.sop.gatewaycommon.manager.DefaultApiMetaManager;
import com.gitee.sop.gatewaycommon.manager.SopRouteLocator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.StringRedisTemplate;
import javax.annotation.PostConstruct;
/**
* @author tanghc
*/
public class EasyopenZuulConfig {
@Autowired
protected ZuulProperties zuulProperties;
@Autowired
protected ServerProperties server;
@Autowired
protected ApiMetaManager apiMetaManager;
@Bean
public ApiMetaContext apiMetaContext() {
return new DefaultApiMetaContext();
}
@Bean
public ApiMetaManager apiMetaManager(StringRedisTemplate stringRedisTemplate, ApiMetaContext apiMetaContext) {
return new DefaultApiMetaManager(stringRedisTemplate, apiMetaContext);
}
@Bean
public PreDecorationFilter preDecorationFilter(ApiMetaContext apiMetaContext, ProxyRequestHelper proxyRequestHelper) {
SopRouteLocator routeLocator = new SopRouteLocator(apiMetaContext);
return new PreDecorationFilter(routeLocator,
this.server.getServlet().getContextPath(),
this.zuulProperties,
proxyRequestHelper);
}
@Bean
PreValidateFilter preValidateFilter() {
return new PreValidateFilter();
}
@Bean
ErrorFilter errorFilter() {
return new ErrorFilter();
}
@Bean
PostResultFilter postResultFilter() {
return new PostEasyopenResultFilter();
}
@PostConstruct
public void after() {
apiMetaManager.refresh();
}
}

@ -0,0 +1,46 @@
package com.gitee.sop.gatewaycommon.easyopen.filter;
import com.gitee.sop.gatewaycommon.filter.PostResultFilter;
/**
* 合并微服务结果统一返回格式
*
* @author tanghc
*/
public class PostEasyopenResultFilter extends PostResultFilter {
private static final String EASYOPEN_SUCCESS_CODE = "0";
//
// protected Result processServiceResult(InputStream responseDataStream, ResultBuilder resultBuilder) throws IOException {
// String responseBody = IOUtils.toString(responseDataStream, SopConstants.CHARSET_UTF8);
// ApiResult result = this.parseApiResult(responseBody);
// Result finalResult;
// if (EASYOPEN_SUCCESS_CODE.equals(result.getCode())) {
// finalResult = resultBuilder.buildSuccessResult(GATEWAY_SUCCESS_CODE, null, result.getData());
// } else {
// // 业务出错
// finalResult = resultBuilder.buildServiceError(result.getCode(), result.getMsg());
// }
// return finalResult;
// }
//
// protected ApiResult parseApiResult(String responseBody) {
// ApiParam apiParam = ApiContext.getApiParam();
// String format = apiParam.fetchFormat();
// return SopConstants.FORMAT_XML.equalsIgnoreCase(format)
// ? XmlUtil.unserialize(responseBody, ApiResult.class)
// : JSON.parseObject(responseBody, ApiResult.class);
// }
//
// protected ResultSerializer buildResultSerializer(HttpServletRequest request, ApiConfig apiConfig) {
// ApiParam apiParam = ApiContext.getApiParam();
// String format = apiParam.fetchFormat();
// if (SopConstants.FORMAT_JSON.equalsIgnoreCase(format)) {
// return apiConfig.getJsonResultSerializer();
// } else if (SopConstants.FORMAT_XML.equalsIgnoreCase(format)) {
// // xml格式输出
// return apiConfig.getXmlResultSerializer();
// } else {
// throw ErrorEnum.isv_invalid_format.getErrorMeta().getException();
// }
// }
}

@ -0,0 +1,26 @@
package com.gitee.sop.gatewaycommon.exception;
import com.gitee.sop.gatewaycommon.message.Error;
/**
* @author tanghc
*/
public class ApiException extends RuntimeException {
private Error error;
public ApiException(Throwable cause, Error error) {
super(cause);
this.error = error;
}
public ApiException(Error error) {
super(error.toString());
this.error = error;
}
public Error getError() {
return error;
}
}

@ -0,0 +1,96 @@
package com.gitee.sop.gatewaycommon.filter;
import com.alibaba.fastjson.JSON;
import com.gitee.sop.gatewaycommon.result.ApiResult;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author tanghc
*/
public abstract class BaseZuulFilter extends ZuulFilter {
protected Logger log = LoggerFactory.getLogger(getClass());
private Integer filterOrder;
protected abstract FilterType getFilterType();
protected abstract int getFilterOrder();
protected abstract Object doRun(RequestContext requestContext) throws ZuulException;
/**
* 设置过滤器顺序
*
* @param filterOrder 顺序值越小优先执行
* @return 返回自身对象
*/
public BaseZuulFilter order(int filterOrder) {
this.filterOrder = filterOrder;
return this;
}
@Override
public int filterOrder() {
return filterOrder != null ? filterOrder : this.getFilterOrder();
}
@Override
public String filterType() {
return this.getFilterType().getType();
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
return this.doRun(RequestContext.getCurrentContext());
}
/**
* 过滤该请求不往下级服务去转发请求到此结束并填充responseBody
*
* @param requestContext
* @param result
*/
public static void stopRouteAndReturn(RequestContext requestContext, Object result) {
requestContext.setSendZuulResponse(false);
requestContext.setResponseBody(JSON.toJSONString(result));
}
public static void main(String[] args) {
System.out.println(JSON.toJSONString(new ApiResult()));
}
/**
* to classify a filter by type. Standard types in Zuul are "pre" for pre-routing filtering,
* "route" for routing to an origin, "post" for post-routing filters, "error" for error handling.
* We also support a "static" type for static responses see StaticResponseFilter.
* Any filterType made be created or added and doRun by calling FilterProcessor.runFilters(type)
*/
public enum FilterType {
PRE("pre"),
ROUTE("route"),
POST("post"),
ERROR("error"),
STATIC("static"),
;
FilterType(String type) {
this.type = type;
}
private String type;
public String getType() {
return type;
}
}
}

@ -0,0 +1,59 @@
package com.gitee.sop.gatewaycommon.filter;
import com.netflix.zuul.FilterProcessor;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.cloud.netflix.zuul.filters.post.SendErrorFilter;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
/**
* @author tanghc
*/
public class ErrorFilter extends SendErrorFilter {
public static final String FAILED_FILTER = "failed.filter";
private int filterOrder = 10;
public ErrorFilter() {
initFilterProcessor();
}
public void initFilterProcessor() {
FilterProcessor instance = FilterProcessor.getInstance();
if (!(instance instanceof MyFilterProcessor)) {
FilterProcessor.setProcessor(new MyFilterProcessor());
}
}
@Override
public int filterOrder() {
return filterOrder;
}
@Override
public boolean shouldFilter() {
// 判断:仅处理来自post过滤器引起的异常
RequestContext ctx = RequestContext.getCurrentContext();
ZuulFilter failedFilter = (ZuulFilter) ctx.get(FAILED_FILTER);
return failedFilter != null && failedFilter.filterType().equals(FilterConstants.POST_TYPE);
}
public static class MyFilterProcessor extends FilterProcessor {
@Override
public Object processZuulFilter(ZuulFilter filter) throws ZuulException {
try {
return super.processZuulFilter(filter);
} catch (ZuulException e) {
RequestContext ctx = RequestContext.getCurrentContext();
ctx.set(FAILED_FILTER, filter);
throw e;
}
}
}
public void setFilterOrder(int filterOrder) {
this.filterOrder = filterOrder;
}
}

@ -0,0 +1,49 @@
package com.gitee.sop.gatewaycommon.filter;
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.bean.SopConstants;
import com.gitee.sop.gatewaycommon.result.ResultExecutor;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.io.IOUtils;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import java.io.InputStream;
/**
* 合并微服务结果统一返回格式
*
* @author tanghc
*/
public class PostResultFilter extends BaseZuulFilter {
@Override
protected FilterType getFilterType() {
return FilterType.POST;
}
@Override
protected int getFilterOrder() {
return FilterConstants.SEND_RESPONSE_FILTER_ORDER - 1;
}
@Override
protected Object doRun(RequestContext requestContext) throws ZuulException {
int responseStatusCode = requestContext.getResponseStatusCode();
InputStream responseDataStream = requestContext.getResponseDataStream();
ApiConfig apiConfig = ApiContext.getApiConfig();
ResultExecutor resultExecutor = apiConfig.getResultExecutor();
String serviceResult;
try {
serviceResult = IOUtils.toString(responseDataStream, SopConstants.CHARSET_UTF8);
} catch (Exception e) {
log.error("业务方无数据返回", e);
serviceResult = SopConstants.EMPTY_JSON;
}
String finalResult = resultExecutor.mergeResult(responseStatusCode, serviceResult);
requestContext.setResponseBody(finalResult);
return null;
}
}

@ -0,0 +1,41 @@
package com.gitee.sop.gatewaycommon.filter;
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import javax.servlet.http.HttpServletRequest;
/**
* @author tanghc
*/
public class PreTokenFilter extends BaseZuulFilter {
@Override
protected FilterType getFilterType() {
return FilterType.PRE;
}
@Override
protected int getFilterOrder() {
return FilterConstants.PRE_DECORATION_FILTER_ORDER + 1;
}
@Override
protected Object doRun(RequestContext ctx) throws ZuulException {
Object serviceId = ctx.get(FilterConstants.SERVICE_ID_KEY);
log.info("serviceId:{}", serviceId);
HttpServletRequest request = ctx.getRequest();
log.info("send {} request to {}", request.getMethod(), request.getRequestURL().toString());
String accessToken = request.getParameter("access_token");
if (StringUtils.isBlank(accessToken)) {
throw ErrorEnum.AOP_INVALID_APP_AUTH_TOKEN.getErrorMeta().getException();
}
return null;
}
}

@ -0,0 +1,48 @@
package com.gitee.sop.gatewaycommon.filter;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
import com.gitee.sop.gatewaycommon.exception.ApiException;
import com.gitee.sop.gatewaycommon.param.ApiParam;
import com.gitee.sop.gatewaycommon.validate.Validator;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import javax.servlet.http.HttpServletRequest;
/**
* 前置校验
* @author tanghc
*/
public class PreValidateFilter extends BaseZuulFilter {
@Override
protected FilterType getFilterType() {
return FilterType.PRE;
}
@Override
protected int getFilterOrder() {
// 在org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter前面
return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1;
}
@Override
protected Object doRun(RequestContext requestContext) throws ZuulException {
HttpServletRequest request = requestContext.getRequest();
ApiConfig apiConfig = ApiContext.getApiConfig();
// 解析参数
ApiParam param = apiConfig.getParamParser().parse(request);
ApiContext.setApiParam(param);
// 验证操作,这里有负责验证签名参数
Validator validator = apiConfig.getValidator();
try {
validator.validate(param);
} catch (ApiException e) {
log.error("签名验证失败,params:{}", param.toJSONString(), e);
throw e;
}
return null;
}
}

@ -0,0 +1,25 @@
package com.gitee.sop.gatewaycommon.manager;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
public class ApiMetaChangeListener implements MessageListener {
private ApiMetaManager apiMetaManager;
private StringRedisTemplate redisTemplate;
public ApiMetaChangeListener(ApiMetaManager apiMetaManager, StringRedisTemplate redisTemplate) {
this.apiMetaManager = apiMetaManager;
this.redisTemplate = redisTemplate;
}
@Override
public void onMessage(Message message, byte[] bytes) {
RedisSerializer<String> stringSerializer = redisTemplate.getStringSerializer();
String msg = stringSerializer.deserialize(message.getBody());
this.apiMetaManager.onChange(msg);
}
}

@ -0,0 +1,20 @@
package com.gitee.sop.gatewaycommon.manager;
import org.springframework.data.redis.core.StringRedisTemplate;
/**
* @author tanghc
*/
public class ApiMetaConfig {
private StringRedisTemplate redisTemplate;
public ApiMetaConfig(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
public void loadApiMetas() {
}
}

@ -0,0 +1,13 @@
package com.gitee.sop.gatewaycommon.manager;
import com.gitee.sop.gatewaycommon.bean.ServiceApiInfo;
import org.springframework.cloud.netflix.zuul.filters.Route;
/**
* @author tanghc
*/
public interface ApiMetaContext {
void reload(String serviceId, ServiceApiInfo serviceApiInfo);
Route getRoute(String nameVersion);
}

@ -0,0 +1,22 @@
package com.gitee.sop.gatewaycommon.manager;
/**
* 管理各服务接口信息
* @author tanghc
*/
public interface ApiMetaManager {
String API_STORE_KEY = "com.gitee.sop.api";
/**
* 刷新素有的微服务接口信息
*/
void refresh();
/**
* 某个服务接口更改时触发
* @param serviceApiInfoJson 接口信息
*/
void onChange(String serviceApiInfoJson);
}

@ -0,0 +1,62 @@
package com.gitee.sop.gatewaycommon.manager;
import com.gitee.sop.gatewaycommon.bean.ServiceApiInfo;
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.netflix.zuul.filters.Route;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* @author tanghc
*/
@Slf4j
public class DefaultApiMetaContext implements ApiMetaContext {
// key:nameVersion
private Map<String, Route> nameVersionServiceIdMap = new HashMap<>(128);
// key: serviceId , value: md5
private Map<String, String> serviceIdMd5Map = new HashMap<>(16);
@Override
public void reload(String serviceId, ServiceApiInfo serviceApiInfo) {
String md5 = serviceIdMd5Map.get(serviceId);
if (md5 != null && md5.equals(serviceApiInfo.getMd5())) {
log.info("MD5相同,无需更改本地接口信息,appName:{}, md5:{}", serviceApiInfo.getAppName(), serviceApiInfo.getMd5());
return;
}
log.info("更新本地接口信息,appName:{}, md5:{}", serviceApiInfo.getAppName(), serviceApiInfo.getMd5());
// 移除原来的
Iterator<Map.Entry<String, Route>> iterator = nameVersionServiceIdMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Route> entry = iterator.next();
if (entry.getValue().getLocation().equals(serviceId)) {
iterator.remove();
}
}
List<ServiceApiInfo.ApiMeta> apis = serviceApiInfo.getApis();
for (ServiceApiInfo.ApiMeta apiMeta : apis) {
Route route = this.buildRoute(serviceId, apiMeta);
nameVersionServiceIdMap.put(apiMeta.fetchNameVersion(), route);
}
serviceIdMd5Map.put(serviceId, serviceApiInfo.getMd5());
}
@Override
public Route getRoute(String nameVersion) {
Route route = nameVersionServiceIdMap.get(nameVersion);
if (route == null) {
throw ErrorEnum.ISV_INVALID_METHOD.getErrorMeta().getException();
}
return route;
}
protected Route buildRoute(String serviceId, ServiceApiInfo.ApiMeta apiMeta) {
return new Route(apiMeta.getName(), apiMeta.getPath(), serviceId, null, false, null);
}
}

@ -0,0 +1,51 @@
package com.gitee.sop.gatewaycommon.manager;
import com.alibaba.fastjson.JSON;
import com.gitee.sop.gatewaycommon.bean.ServiceApiInfo;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.Map;
/**
* 保存在redis中结构为HSET格式如下
* <pre>
* com.gitee.sop.api
* &lt;serviceId&gt;:{ md5:"xxx", apis:[{name:"", version:""}] }
* </pre>
* @author tanghc
*/
@Getter
@Slf4j
public class DefaultApiMetaManager implements ApiMetaManager {
private StringRedisTemplate redisTemplate;
private ApiMetaContext apiMetaContext;
public DefaultApiMetaManager(StringRedisTemplate redisTemplate, ApiMetaContext apiMetaContext) {
this.redisTemplate = redisTemplate;
this.apiMetaContext = apiMetaContext;
}
@Override
public void refresh() {
log.info("刷新本地接口信息");
Map<Object, Object> entries = redisTemplate.opsForHash().entries(API_STORE_KEY);
for (Map.Entry<Object, Object> entry : entries.entrySet()) {
log.info("更新微服务接口,appName:{}", entry.getKey());
String serviceId = entry.getKey().toString();
String serviceApiInfoJson = entry.getValue().toString();
ServiceApiInfo serviceApiInfo = JSON.parseObject(serviceApiInfoJson, ServiceApiInfo.class);
apiMetaContext.reload(serviceId, serviceApiInfo);
}
}
@Override
public void onChange(String serviceApiInfoJson) {
ServiceApiInfo serviceApiInfo = JSON.parseObject(serviceApiInfoJson, ServiceApiInfo.class);
log.info("Redis订阅推送接口信息,appName:{}, md5:{}", serviceApiInfo.getAppName(), serviceApiInfo.getMd5());
this.apiMetaContext.reload(serviceApiInfo.getAppName(), serviceApiInfo);
}
}

@ -0,0 +1,45 @@
package com.gitee.sop.gatewaycommon.manager;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.param.ApiParam;
import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
import org.springframework.core.Ordered;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* @author tanghc
*/
public class SopRouteLocator implements RouteLocator, Ordered {
private ApiMetaContext apiMetaContext;
public SopRouteLocator(ApiMetaContext apiMetaContext) {
this.apiMetaContext = apiMetaContext;
}
@Override
public Collection<String> getIgnoredPaths() {
return Collections.emptyList();
}
@Override
public List<Route> getRoutes() {
return null;
}
@Override
public Route getMatchingRoute(String path) {
ApiParam param = ApiContext.getApiParam();
String nameVersion = param.fetchNameVersion();
return apiMetaContext.getRoute(nameVersion);
}
@Override
public int getOrder() {
return 0;
}
}

@ -0,0 +1,48 @@
package com.gitee.sop.gatewaycommon.message;
/**
* 定义错误返回
* code返回码
* msg返回码描述
* sub_code明细返回码
* sub_msg明细返回码描述
* 解决方案
* @author tanghc
*/
public interface Error {
/**
* 获取网关状态码
*
* @return 返回状态码
*/
String getCode();
/**
* 获取网关错误信息
*
* @return 返回错误信息
*/
String getMsg();
/**
* sub_code明细返回码
* @return sub_code明细返回码
*/
String getSub_code();
/**
* sub_msg明细返回码描述
* @return sub_msg明细返回码描述
*/
String getSub_msg();
/**
* 解决方案
* @return 解决方案
*/
String getSolution();
}

@ -0,0 +1,66 @@
package com.gitee.sop.gatewaycommon.message;
/**
* @author tanghc
*/
public enum ErrorEnum {
SUCCESS("10000", ""),
ISP_UNKNOW_ERROR("20000", "isp.unknow-error"),
AOP_UNKNOW_ERROR("20000", "aop.unknow-error"),
AOP_INVALID_AUTH_TOKEN("20001", "aop.invalid-auth-token"),
AOP_INVALID_AUTH_OKEN("20001", "aop.invalid-auth-token"),
AOP_AUTH_TOKEN_TIME_OUT("20001", "aop.auth-token-time-out"),
AOP_INVALID_APP_AUTH_TOKEN("20001", "aop.invalid-app-auth-token"),
AOP_INVALID_APP_AUTH_TOKEN_NO_API("20001", "aop.invalid-app-auth-token-no-api"),
AOP_APP_AUTH_TOKEN_TIME_OUT("20001", "aop.app-auth-token-time-out"),
AOP_NO_PRODUCT_REG_BY_PARTNER("20001", "aop.no-product-reg-by-partner"),
ISV_MISSING_METHOD("40001", "isv.missing-method"),
ISV_MISSING_SIGNATURE("40001", "isv.missing-signature"),
ISV_MISSING_SIGNATURE_TYPE("40001", "isv.missing-signature-type"),
ISV_MISSING_SIGNATURE_KEY("40001", "isv.missing-signature-key"),
ISV_MISSING_APP_ID("40001", "isv.missing-app-id"),
ISV_MISSING_TIMESTAMP("40001", "isv.missing-timestamp"),
ISV_MISSING_VERSION("40001", "isv.missing-version"),
ISV_DECRYPTION_ERROR_MISSING_ENCRYPT_TYPE("40001", "isv.decryption-error-missing-encrypt-type"),
ISV_INVALID_PARAMETER("40002", "isv.invalid-parameter"),
ISV_UPLOAD_FAIL("40002", "isv.upload-fail"),
ISV_INVALID_FILE_EXTENSION("40002", "isv.invalid-file-extension"),
ISV_INVALID_FILE_SIZE("40002", "isv.invalid-file-size"),
ISV_INVALID_METHOD("40002", "isv.invalid-method"),
ISV_INVALID_FORMAT("40002", "isv.invalid-format"),
ISV_INVALID_SIGNATURE_TYPE("40002", "isv.invalid-signature-type"),
ISV_INVALID_SIGNATURE("40002", "isv.invalid-signature"),
ISV_INVALID_ENCRYPT_TYPE("40002", "isv.invalid-encrypt-type"),
ISV_INVALID_ENCRYPT("40002", "isv.invalid-encrypt"),
ISV_INVALID_APP_ID("40002", "isv.invalid-app-id"),
ISV_INVALID_TIMESTAMP("40002", "isv.invalid-timestamp"),
ISV_INVALID_CHARSET("40002", "isv.invalid-charset"),
ISV_INVALID_DIGEST("40002", "isv.invalid-digest"),
ISV_DECRYPTION_ERROR_NOT_VALID_ENCRYPT_TYPE("40002", "isv.decryption-error-not-valid-encrypt-type"),
ISV_DECRYPTION_ERROR_NOT_VALID_ENCRYPT_KEY("40002", "isv.decryption-error-not-valid-encrypt-key"),
ISV_DECRYPTION_ERROR_UNKNOWN("40002", "isv.decryption-error-unknown"),
ISV_MISSING_SIGNATURE_CONFIG("40002", "isv.missing-signature-config"),
ISV_NOT_SUPPORT_APP_AUTH("40002", "isv.not-support-app-auth"),
ISV_SUSPECTED_ATTACK("40002", "isv.suspected-attack"),
ISV_INVALID_CONTENT_TYPE("40002", "isv.invalid-content-type"),
BIZ_ERROR("40004", ""),
ISV_INSUFFICIENT_ISV_PERMISSIONS("40006", "isv.insufficient-isv-permissions"),
ISV_INSUFFICIENT_USER_PERMISSIONS("40006", "isv.insufficient-user-permissions"),
;
private ErrorMeta errorMeta;
ErrorEnum(String code, String sub_code) {
this.errorMeta = new ErrorMeta("open.error_", code, sub_code);
}
public ErrorMeta getErrorMeta() {
return errorMeta;
}
}

@ -0,0 +1,120 @@
package com.gitee.sop.gatewaycommon.message;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
/**
* 负责构建错误消息
*
* @author tanghc
*/
@Slf4j
public class ErrorFactory {
public static final String SYS_ERR = "系统错误";
private static final String I18N_OPEN_ERROR = "i18n/open/error";
public static final String UNDERLINE = "_";
private static Set<String> noModuleCache = new HashSet<String>();
private static Map<String, Error> errorCache = new HashMap<>(64);
/**
* 错误信息的国际化信息
*/
private static MessageSourceAccessor errorMessageSourceAccessor;
/**
* 设置国际化资源信息
*/
public static void initMessageSource(List<String> isvModules) {
HashSet<String> baseNamesSet = new HashSet<String>();
baseNamesSet.add(I18N_OPEN_ERROR);
if (!isvModules.isEmpty()) {
baseNamesSet.addAll(isvModules);
}
String[] totalBaseNames = baseNamesSet.toArray(new String[0]);
log.info("加载错误码国际化资源:{}", StringUtils.arrayToCommaDelimitedString(totalBaseNames));
ResourceBundleMessageSource bundleMessageSource = new ResourceBundleMessageSource();
bundleMessageSource.setBasenames(totalBaseNames);
MessageSourceAccessor messageSourceAccessor = new MessageSourceAccessor(bundleMessageSource);
setErrorMessageSourceAccessor(messageSourceAccessor);
}
/**
* 通过ErrorMetaLocaleparams构建国际化错误消息
*
* @param errorMeta 错误信息
* @param locale 本地化
* @param params 参数
* @return 如果没有配置国际化消息则直接返回errorMeta中的信息
*/
public static Error getError(ErrorMeta errorMeta, Locale locale, Object... params) {
String key = errorMeta.getModulePrefix() + errorMeta.getCode() + errorMeta.getSubCode() + locale.toString();
Error error = errorCache.get(key);
if (error == null) {
Assert.notNull(locale, "未设置Locale");
String modulePrefix = errorMeta.getModulePrefix();
String code = errorMeta.getCode();
// open.error_20000=Service is temporarily unavailable
String msg = getErrorMessage(modulePrefix + code, locale);
String subCode = errorMeta.getSubCode();
// open.error_20000_isp.unknow-error=Service is temporarily unavailable
String subMsg = getErrorMessage(modulePrefix + code + UNDERLINE + subCode, locale, params);
if (StringUtils.isEmpty(msg)) {
msg = SYS_ERR;
}
if (StringUtils.isEmpty(subMsg)) {
subMsg = SYS_ERR;
}
// solution暂未实现,如果要实现,可以这样配置:
// open.error_20000_isp.unknow-error_solution=Service is temporarily unavailable
// String solution = getErrorMessage(modulePrefix + code + UNDERLINE + subCode + "_solution", locale, params);
error = new ErrorImpl(code, msg, subCode, subMsg, null);
errorCache.put(key, error);
}
return error;
}
public static void setErrorMessageSourceAccessor(MessageSourceAccessor errorMessageSourceAccessor) {
ErrorFactory.errorMessageSourceAccessor = errorMessageSourceAccessor;
}
/**
* 返回本地化信息
*
* @param module 错误模块
* @param locale 本地化
* @param params 参数
* @return 返回信息
*/
public static String getErrorMessage(String module, Locale locale, Object... params) {
if (noModuleCache.contains(module)) {
return null;
}
try {
return errorMessageSourceAccessor.getMessage(module, params, locale);
} catch (Exception e) {
noModuleCache.add(module);
return null;
}
}
}

@ -0,0 +1,23 @@
package com.gitee.sop.gatewaycommon.message;
import lombok.Data;
@Data
public class ErrorImpl implements Error {
private String code;
private String msg;
private String sub_code;
private String sub_msg;
private String solution;
public ErrorImpl() {
}
public ErrorImpl(String code, String msg, String sub_code, String sub_msg, String solution) {
this.code = code;
this.msg = msg;
this.sub_code = sub_code;
this.sub_msg = sub_msg;
this.solution = solution;
}
}

@ -0,0 +1,47 @@
package com.gitee.sop.gatewaycommon.message;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.exception.ApiException;
import lombok.Getter;
/**
* 错误对象
*
* @author tanghc
*/
@Getter
public class ErrorMeta {
private String modulePrefix;
private String code;
private String subCode;
public ErrorMeta(String modulePrefix, String code, String subCode) {
this.modulePrefix = modulePrefix;
this.code = code;
this.subCode = subCode;
}
public Error getError() {
return ErrorFactory.getError(this, ApiContext.getLocale());
}
/**
* 返回网关exception
*
* @param params 参数
* @return 返回exception
*/
public ApiException getException(Object... params) {
if (params != null && params.length == 1) {
Object param = params[0];
if (param instanceof Throwable) {
Error error = ErrorFactory.getError(this, ApiContext.getLocale());
return new ApiException((Throwable) param, error);
}
}
Error error = ErrorFactory.getError(this, ApiContext.getLocale(), params);
return new ApiException(error);
}
}

@ -0,0 +1,197 @@
package com.gitee.sop.gatewaycommon.param;
import com.alibaba.fastjson.JSONObject;
import com.gitee.sop.gatewaycommon.bean.SopConstants;
import com.netflix.zuul.context.RequestContext;
import org.apache.commons.lang.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/**
* 客户端传来的参数放在这里.
*
* @author tanghc
*/
public class ApiParam extends JSONObject implements Param {
public ApiParam(Map<String, Object> map) {
super(map);
}
private boolean ignoreSign;
private boolean ignoreValidate;
private String restName;
private String restVersion;
/**
* 获取sign并从param中删除
*
* @return 返回sign内容
*/
public String fetchSignAndRemove() {
String sign = this.fetchSign();
this.remove(ParamNames.SIGN_NAME);
return sign;
}
public HttpServletRequest fetchRequest() {
return RequestContext.getCurrentContext().getRequest();
}
/**
* 是否忽略验证签名
*
* @return 返回true忽略签名
*/
public boolean fetchIgnoreSign() {
return ignoreSign;
}
public void setIgnoreSign(boolean ignoreSign) {
this.ignoreSign = ignoreSign;
}
public boolean fetchIgnoreValidate() {
return ignoreValidate;
}
public void setIgnoreValidate(boolean ignoreValidate) {
this.ignoreValidate = ignoreValidate;
}
/**
* 接口名,:goods.list
*/
@Override
public String fetchName() {
String name = getString(ParamNames.API_NAME);
if (name == null) {
name = this.restName;
}
return name;
}
public void setName(String name) {
this.restName = name;
}
public String fetchNameVersion() {
return buildNameVersion(this.fetchName(), this.fetchVersion());
}
public static String buildNameVersion(String name, String version) {
if (StringUtils.isEmpty(version)) {
return name;
} else {
return name + version;
}
}
/**
* 版本号
*/
@Override
public String fetchVersion() {
String version = getString(ParamNames.VERSION_NAME);
if (version == null) {
version = this.restVersion;
}
return version;
}
public void setVersion(String version) {
this.restVersion = version;
}
/**
* 接入应用ID
*/
@Override
public String fetchAppKey() {
return getString(ParamNames.APP_KEY_NAME);
}
public void setAppKey(String appKey) {
put(ParamNames.APP_KEY_NAME, appKey);
}
/**
* 参数,urlencode后的
*/
@Override
public String fetchData() {
return getString(ParamNames.BIZ_CONTENT_NAME);
}
public void setData(String json) {
put(ParamNames.BIZ_CONTENT_NAME, json);
}
/**
* 时间戳格式为yyyy-MM-dd HH:mm:ss例如2015-01-01 12:00:00
*/
@Override
public String fetchTimestamp() {
return getString(ParamNames.TIMESTAMP_NAME);
}
public void setTimestamp(String timestamp) {
put(ParamNames.TIMESTAMP_NAME, timestamp);
}
/**
* 签名串
*/
@Override
public String fetchSign() {
return getString(ParamNames.SIGN_NAME);
}
public void setSign(String sign) {
put(ParamNames.SIGN_NAME, sign);
}
@Override
public String fetchFormat() {
String format = getString(ParamNames.FORMAT_NAME);
if (format == null || "".equals(format)) {
return SopConstants.FORMAT_JSON;
}
return format;
}
public void setFormat(String format) {
put(ParamNames.FORMAT_NAME, format);
}
@Override
public String fetchAccessToken() {
return getString(ParamNames.APP_AUTH_TOKEN_NAME);
}
@Override
public String fetchSignMethod() {
String signMethod = getString(ParamNames.SIGN_TYPE_NAME);
if (signMethod == null) {
return SopConstants.DEFAULT_SIGN_METHOD;
} else {
return signMethod;
}
}
@Override
public String fetchCharset() {
return getString(ParamNames.CHARSET_NAME);
}
@Override
public ApiParam clone() {
ApiParam param = new ApiParam(this);
param.ignoreSign = this.ignoreSign;
param.ignoreValidate = this.ignoreValidate;
return param;
}
}

@ -0,0 +1,104 @@
package com.gitee.sop.gatewaycommon.param;
import com.alibaba.fastjson.JSON;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
import com.gitee.sop.gatewaycommon.util.RequestUtil;
import com.netflix.zuul.http.HttpServletRequestWrapper;
import org.springframework.http.MediaType;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* 参数解析默认实现
*
* @author tanghc
*/
public class ApiParamParser implements ParamParser {
private static final String CONTENT_TYPE_MULTIPART = MediaType.MULTIPART_FORM_DATA_VALUE;
private static final String CONTENT_TYPE_JSON = MediaType.APPLICATION_JSON_VALUE;
private static final String CONTENT_TYPE_TEXT = MediaType.TEXT_PLAIN_VALUE;
@Override
public ApiParam parse(HttpServletRequest request) {
try {
Map<String, Object> params = this.getJson(request);
return new ApiParam(params);
} catch (Exception e) {
throw ErrorEnum.ISV_INVALID_PARAMETER.getErrorMeta().getException();
}
}
public Map<String, Object> getJson(HttpServletRequest request) throws Exception {
// zuul会做一层包装
if (request instanceof HttpServletRequestWrapper) {
HttpServletRequestWrapper req = (HttpServletRequestWrapper) request;
request = req.getRequest();
}
Map<String, Object> params = null;
if (RequestUtil.isGetRequest(request)) {
params = RequestUtil.convertRequestParamsToMap(request);
} else {
String contectType = request.getContentType();
if (contectType == null) {
contectType = "";
}
contectType = contectType.toLowerCase();
// json或者纯文本形式
if (contectType.contains(CONTENT_TYPE_JSON) || contectType.contains(CONTENT_TYPE_TEXT)) {
String txt = RequestUtil.getText(request);
params = JSON.parseObject(txt);
} else if (contectType.contains(CONTENT_TYPE_MULTIPART)) {
// 上传文件形式
params = this.parseUploadRequest(request);
} else {
params = RequestUtil.convertRequestParamsToMap(request);
}
}
return params;
}
/**
* 解析文件上传请求
*
* @param request
* @return 返回json字符串
*/
protected Map<String, Object> parseUploadRequest(HttpServletRequest request) {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(
request.getSession().getServletContext());
// 检查form中是否有enctype="multipart/form-data"
if (multipartResolver.isMultipart(request)) {
MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
Map<String, MultipartFile> fileMap = multiRequest.getFileMap();
Map<String, MultipartFile> finalMap = new HashMap<>(fileMap.size());
Set<String> keys = fileMap.keySet();
for (String name : keys) {
MultipartFile file = fileMap.get(name);
if (file.getSize() > 0) {
finalMap.put(name, file);
}
}
if (finalMap.size() > 0) {
// 保存上传文件
ApiContext.setUploadContext(new ApiUploadContext(finalMap));
}
}
return RequestUtil.convertRequestParamsToMap(request);
}
}

@ -0,0 +1,42 @@
package com.gitee.sop.gatewaycommon.param;
import org.springframework.web.multipart.MultipartFile;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* 存放上传文件
*
* @author tanghc
*/
public class ApiUploadContext implements UploadContext {
private Map<String, MultipartFile> fileMap;
private List<MultipartFile> allFile;
public ApiUploadContext(Map<String, MultipartFile> map) {
if (map == null) {
map = Collections.emptyMap();
}
this.fileMap = map;
this.allFile = new ArrayList<>(map.values());
}
@Override
public MultipartFile getFile(int index) {
return this.allFile.get(index);
}
@Override
public MultipartFile getFile(String name) {
return fileMap.get(name);
}
@Override
public List<MultipartFile> getAllFile() {
return this.allFile;
}
}

@ -0,0 +1,70 @@
package com.gitee.sop.gatewaycommon.param;
import java.io.Serializable;
/**
* @author tanghc
*/
public interface Param extends Serializable {
/**
* 获取接口名
* @return 返回接口名
*/
String fetchName();
/**
* 获取版本号
* @return 返回版本号
*/
String fetchVersion();
/**
* 获取appKey
* @return 返回appKey
*/
String fetchAppKey();
/**
* 获取业务参数
* @return 返回业务参数
*/
String fetchData();
/**
* 获取时间戳
* @return 返回时间戳
*/
String fetchTimestamp();
/**
* 获取签名串
* @return 返回签名串
*/
String fetchSign();
/**
* 获取格式化类型
* @return 返回格式化类型
*/
String fetchFormat();
/**
* 获取accessToken
* @return 返回accessToken
*/
String fetchAccessToken();
/**
* 获取签名方式
* @return 返回签名方式
*/
String fetchSignMethod();
/**
* 请求使用的编码格式如utf-8,gbk,gb2312等
* @return 请求使用的编码格式如utf-8,gbk,gb2312等
*/
String fetchCharset();
}

@ -0,0 +1,46 @@
package com.gitee.sop.gatewaycommon.param;
/**
* 请求参数名定义
*
* 参数 类型 是否必填 最大长度 描述 示例值
* app_id String 32 支付宝分配给开发者的应用ID 2014072300007148
* method String 128 接口名称 alipay.trade.fastpay.refund.query
* format String 40 仅支持JSON JSON
* charset String 10 请求使用的编码格式如utf-8,gbk,gb2312等 utf-8
* sign_type String 10 商户生成签名字符串所使用的签名算法类型目前支持RSA2和RSA推荐使用RSA2 RSA2
* sign String 344 商户请求参数的签名串详见签名 详见示例
* timestamp String 19 发送请求的时间格式"yyyy-MM-dd HH:mm:ss" 2014-07-24 03:07:50
* version String 3 调用的接口版本固定为1.0 1.0
* app_auth_token String 40 详见应用授权概述
* biz_content String 请求参数的集合最大长度不限除公共参数外所有请求参数都必须放在这个参数中传递具体参照各产品快速接入文档
*
* @author tanghc
*/
public class ParamNames {
/** 分配给开发者的应用ID */
public static String APP_KEY_NAME = "app_id";
/** 接口名称 */
public static String API_NAME = "method";
/** 仅支持JSON */
public static String FORMAT_NAME = "format";
/** 请求使用的编码格式 */
public static String CHARSET_NAME = "charset";
/** 商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2 */
public static String SIGN_TYPE_NAME = "sign_type";
/** 商户请求参数的签名串 */
public static String SIGN_NAME = "sign";
/** 发送请求的时间 */
public static String TIMESTAMP_NAME = "timestamp";
/** 调用的接口版本 */
public static String VERSION_NAME = "version";
/** OAuth 2.0授权token */
public static String APP_AUTH_TOKEN_NAME = "app_auth_token";
/** 请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递,具体参照各产品快速接入文档 */
public static String BIZ_CONTENT_NAME = "biz_content";
/** */
public static String TIMESTAMP_PATTERN = "yyyy-MM-dd HH:mm:ss";
}

@ -0,0 +1,19 @@
package com.gitee.sop.gatewaycommon.param;
import javax.servlet.http.HttpServletRequest;
/**
* 负责解析参数
* @author tanghc
*
*/
public interface ParamParser {
/**
* 从request提取参数
* @param request
* @return 返回ApiParam
* @throws Exception
*/
ApiParam parse(HttpServletRequest request);
}

@ -0,0 +1,36 @@
package com.gitee.sop.gatewaycommon.param;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
/**
* 获取上传文件
*
* @author tanghc
*/
public interface UploadContext {
/**
* 根据索引获取上传文件,从0开始
*
* @param index
* @return 返回上传文件信息
*/
MultipartFile getFile(int index);
/**
* 根据表单名获取上传文件
*
* @param name
* 表单名称
* @return 返回上传文件信息
*/
MultipartFile getFile(String name);
/**
* 获取所有的上传文件
*
* @return 返回所有的上传文件
*/
List<MultipartFile> getAllFile();
}

@ -0,0 +1,77 @@
package com.gitee.sop.gatewaycommon.result;
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.Data;
/**
* 默认的结果封装类.
* <pre>
*
* xml返回结果:
* <response>
* <code>50</code>
* <msg>Remote service error</msg>
* <sub_code>isv.invalid-parameter</sub_code>
* <sub_msg>非法参数</sub_msg>
* </response>
* 成功情况
* <response>
* <code>0</code>
* <msg>成功消息</msg>
* <data>
* ...返回内容
* </data>
* </response>
*
* json返回格式
* {
* "code":"50",
* "msg":"Remote service error",
* "sub_code":"isv.invalid-parameter",
* "sub_msg":"非法参数"
* }
* 成功情况
* {
* "code":"0",
* "msg":"成功消息内容。。。",
* "data":{
* ...返回内容
* }
* }
* </pre>
* <p>
* 字段说明
* code:网关异常码 <br>
* msg:网关异常信息 <br>
* sub_code:业务异常码 <br>
* sub_msg:业务异常信息 <br>
*
* @author tanghc
*/
@XStreamAlias("response")
@Data
public class ApiResult implements Result {
/**
* 网关异常码范围0~100 成功返回"0"
*/
private String code = ErrorEnum.SUCCESS.getErrorMeta().getCode();
/**
* 网关异常信息
*/
private String msg;
/**
* 业务异常码
*/
private String sub_msg;
/**
* 业务异常信息
*/
private String sub_code;
private String sign;
}

@ -0,0 +1,125 @@
package com.gitee.sop.gatewaycommon.result;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.exception.ApiException;
import com.gitee.sop.gatewaycommon.message.Error;
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
import com.gitee.sop.gatewaycommon.message.ErrorMeta;
import com.gitee.sop.gatewaycommon.param.ApiParam;
import com.gitee.sop.gatewaycommon.param.ParamNames;
import com.netflix.zuul.exception.ZuulException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
/**
* @author tanghc
*/
@Slf4j
public class ApiResultExecutor implements ResultExecutor {
private static final ErrorMeta SUCCESS_META = ErrorEnum.SUCCESS.getErrorMeta();
private static final ErrorMeta ISP_UNKNOW_ERROR_META = ErrorEnum.ISP_UNKNOW_ERROR.getErrorMeta();
private static final ErrorMeta ISP_BIZ_ERROR = ErrorEnum.BIZ_ERROR.getErrorMeta();
public static final int BIZ_ERROR_STATUS = 4000;
private static final char DOT = '.';
private static final char UNDERLINE = '_';
public static final String GATEWAY_CODE_NAME = "code";
public static final String GATEWAY_MSG_NAME = "msg";
public static final String DATA_SUFFIX = "_response";
@Override
public String mergeResult(int responseStatus, String responseData) {
if (responseStatus == HttpStatus.OK.value() || responseStatus == BIZ_ERROR_STATUS) {
return mergeSuccess(responseStatus, responseData);
} else {
// 微服务端有可能返回500错误
// {"path":"/book/getBook3","error":"Internal Server Error","message":"id不能为空","timestamp":"2019-02-13T07:41:00.495+0000","status":500}
return mergeError(responseData);
}
}
@Override
public String mergeError(Throwable throwable) {
Error error = null;
if (throwable instanceof ZuulException) {
ZuulException ex = (ZuulException) throwable;
Throwable cause = ex.getCause();
if (cause instanceof ApiException) {
ApiException apiException = (ApiException) cause;
error = apiException.getError();
}
}
if (error == null) {
error = ErrorEnum.AOP_UNKNOW_ERROR.getErrorMeta().getError();
}
JSONObject jsonObject = (JSONObject)JSON.toJSON(error);
return this.merge(jsonObject);
}
/*
成功示例
{
"alipay_trade_fastpay_refund_query_response": {
"code": "10000",
"msg": "Success",
"trade_no": "2014112611001004680073956707",
"out_trade_no": "20150320010101001",
"out_request_no": "20150320010101001",
"refund_reason": "用户退款请求",
"total_amount": 100.2,
"refund_amount": 12.33
},
"sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
}
*/
public String mergeSuccess(int responseStatus, String serviceResult) {
JSONObject jsonObjectService;
// 如果是业务出错
if (responseStatus == BIZ_ERROR_STATUS) {
jsonObjectService = JSON.parseObject(serviceResult);
jsonObjectService.put(GATEWAY_CODE_NAME, ISP_BIZ_ERROR.getCode());
jsonObjectService.put(GATEWAY_MSG_NAME, ISP_BIZ_ERROR.getError().getMsg());
} else {
// 200正常返回
jsonObjectService = JSON.parseObject(serviceResult);
jsonObjectService.put(GATEWAY_CODE_NAME, SUCCESS_META.getCode());
jsonObjectService.put(GATEWAY_MSG_NAME, SUCCESS_META.getError().getMsg());
}
return this.merge(jsonObjectService);
}
/*
异常示例
{
"alipay_trade_fastpay_refund_query_response": {
"code": "20000",
"msg": "Service Currently Unavailable",
"sub_code": "isp.unknow-error",
"sub_msg": "系统繁忙"
},
"sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
}
*/
public String mergeError(String serviceResult) {
JSONObject jsonObjectService = new JSONObject();
jsonObjectService.put(GATEWAY_CODE_NAME, ISP_UNKNOW_ERROR_META.getCode());
jsonObjectService.put(GATEWAY_MSG_NAME, ISP_UNKNOW_ERROR_META.getError().getMsg());
return this.merge(jsonObjectService);
}
private String merge(JSONObject jsonObjectService) {
JSONObject ret = new JSONObject();
ApiParam apiParam = ApiContext.getApiParam();
// 点换成下划线
String apiName = apiParam.fetchName().replace(DOT, UNDERLINE);
ret.put(apiName + DATA_SUFFIX, jsonObjectService);
ret.put(ParamNames.SIGN_NAME, apiParam.fetchSign());
return ret.toJSONString();
}
}

@ -0,0 +1,16 @@
package com.gitee.sop.gatewaycommon.result;
import com.alibaba.fastjson.JSON;
/**
* 序列化json
* @author tanghc
*/
public class JsonResultSerializer implements ResultSerializer {
@Override
public String serialize(Object obj) {
return JSON.toJSONString(obj);
}
}

@ -0,0 +1,12 @@
package com.gitee.sop.gatewaycommon.result;
import java.io.Serializable;
/**
* 返回结果后续自定义的返回类需要实现这个接口
*
* @author tanghc
*/
public interface Result extends Serializable {
}

@ -0,0 +1,27 @@
package com.gitee.sop.gatewaycommon.result;
import com.gitee.sop.gatewaycommon.message.Error;
/**
* @author tanghc
*/
public interface ResultBuilder {
/**
* 构建网关错误返回结果
*
* @param throwable 异常
* @return 返回最终结果
*/
Result buildGatewayError(Throwable throwable);
/**
* 构建网关错误返回结果
*
* @param error error
* @return 返回最终结果
*/
Result buildGatewayError(Error error);
}

@ -0,0 +1,10 @@
package com.gitee.sop.gatewaycommon.result;
/**
* @author tanghc
*/
public interface ResultExecutor {
String mergeResult(int responseStatus, String responseData);
String mergeError(Throwable throwable);
}

@ -0,0 +1,16 @@
package com.gitee.sop.gatewaycommon.result;
/**
* 对象序列化
* @author tanghc
*
*/
public interface ResultSerializer {
/**
* 序列化
*
* @param obj
* @return 返回序列化后的结果
*/
String serialize(Object obj);
}

@ -0,0 +1,16 @@
package com.gitee.sop.gatewaycommon.result;
import com.gitee.sop.gatewaycommon.util.XmlUtil;
/**
* 序列化成xml
* @author tanghc
*/
public class XmlResultSerializer implements ResultSerializer {
@Override
public String serialize(Object obj) {
return XmlUtil.serialize(obj);
}
}

@ -0,0 +1,32 @@
package com.gitee.sop.gatewaycommon.secret;
import java.util.Map;
/**
* 负责秘钥管理
* @author tanghc
*/
public interface AppSecretManager {
/**
* 初始化秘钥数据
* @param appSecretStore
*/
void addAppSecret(Map<String, String> appSecretStore);
/**
* 获取应用程序的密钥
*
* @param appKey
* @return 返回秘钥
*/
String getSecret(String appKey);
/**
* 是否是合法的appKey
*
* @param appKey
* @return 返回appKey
*/
boolean isValidAppKey(String appKey);
}

@ -0,0 +1,33 @@
package com.gitee.sop.gatewaycommon.secret;
import java.util.HashMap;
import java.util.Map;
/**
* appkeysecret默认管理简单放在map中如果要放在redis中可以参照此方式实现AppSecretManager然后在ApiConfig中setAppSecretManager()
* @author tanghc
*
*/
public class CacheAppSecretManager implements AppSecretManager {
private Map<String, String> secretMap = new HashMap<String, String>(64);
@Override
public void addAppSecret(Map<String, String> appSecretStore) {
secretMap.putAll(appSecretStore);
}
@Override
public String getSecret(String appKey) {
return secretMap.get(appKey);
}
@Override
public boolean isValidAppKey(String appKey) {
if (appKey == null) {
return false;
}
return getSecret(appKey) != null;
}
}

@ -0,0 +1,56 @@
package com.gitee.sop.gatewaycommon.secret;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import java.io.IOException;
import java.util.Map;
import java.util.Properties;
/**
* appkeysecret文件管理功能同CacheAppSecretManager这个是将appKeysecret放在属性文件中<br>
* key为appKeyvalue为secret
* @author tanghc
*
*/
public class FileAppSecretManager implements AppSecretManager {
private String appSecretFile = "appSecret.properties";
private Properties properties;
@Override
public void addAppSecret(Map<String, String> appSecretStore) {
properties.putAll(appSecretStore);
}
@Override
public String getSecret(String appKey) {
if (properties == null) {
try {
// 默认加载class根目录的appSecret.properties文件
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
Resource resource = resourceLoader.getResource(appSecretFile);
properties = PropertiesLoaderUtils.loadProperties(resource);
} catch (IOException e) {
throw new RuntimeException("在类路径下找不到appSecret.properties的应用密钥的属性文件");
}
}
return properties.getProperty(appKey);
}
public void setAppSecretFile(String appSecretFile) {
this.appSecretFile = appSecretFile;
}
@Override
public boolean isValidAppKey(String appKey) {
if(appKey == null){
return false;
}
return getSecret(appKey) != null;
}
}

@ -0,0 +1,242 @@
package com.gitee.sop.gatewaycommon.session;
import org.springframework.util.Assert;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import javax.servlet.http.HttpSessionContext;
import java.io.Serializable;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
import java.util.Vector;
/**
* @author tanghc
*/
@SuppressWarnings("deprecation")
public class ApiHttpSession implements HttpSession, Serializable {
private static final long serialVersionUID = 946272038219216222L;
private final String id;
private final long creationTime = System.currentTimeMillis();
private int maxInactiveInterval;
private long lastAccessedTime = System.currentTimeMillis();
private final ServletContext servletContext;
private final Map<String, Object> attributes = new LinkedHashMap<String, Object>();
private boolean invalid = false;
private boolean isNew = true;
/**
* Create a new ApiHttpSession
*/
public ApiHttpSession() {
this(null);
}
/**
* Create a new ApiHttpSession.
*
* @param servletContext
* the ServletContext that the session runs in
*/
public ApiHttpSession(ServletContext servletContext) {
this(servletContext, null);
}
/**
* Create a new ApiHttpSession.
*
* @param servletContext
* the ServletContext that the session runs in
* @param id
* a unique identifier for this session
*/
public ApiHttpSession(ServletContext servletContext, String id) {
this.servletContext = servletContext;
this.id = this.buildId(id);
}
protected String buildId(String id) {
return (id != null ? id : UUID.randomUUID().toString().replace("-", "").toUpperCase());
}
@Override
public long getCreationTime() {
return this.creationTime;
}
@Override
public String getId() {
return this.id;
}
public void access() {
this.lastAccessedTime = System.currentTimeMillis();
this.isNew = false;
}
@Override
public long getLastAccessedTime() {
return this.lastAccessedTime;
}
@Override
public ServletContext getServletContext() {
return this.servletContext;
}
@Override
public void setMaxInactiveInterval(int interval) {
this.maxInactiveInterval = interval;
}
@Override
public int getMaxInactiveInterval() {
return this.maxInactiveInterval;
}
@Override
public HttpSessionContext getSessionContext() {
throw new UnsupportedOperationException("getSessionContext");
}
@Override
public Object getAttribute(String name) {
Assert.notNull(name, "Attribute name must not be null");
return this.attributes.get(name);
}
@Override
public Object getValue(String name) {
return getAttribute(name);
}
@Override
public Enumeration<String> getAttributeNames() {
return new Vector<String>(this.attributes.keySet()).elements();
}
@Override
public String[] getValueNames() {
return this.attributes.keySet().toArray(new String[this.attributes.size()]);
}
@Override
public void setAttribute(String name, Object value) {
Assert.notNull(name, "Attribute name must not be null");
if (value != null) {
this.attributes.put(name, value);
if (value instanceof HttpSessionBindingListener) {
((HttpSessionBindingListener) value).valueBound(new HttpSessionBindingEvent(this, name, value));
}
} else {
removeAttribute(name);
}
}
@Override
public void putValue(String name, Object value) {
setAttribute(name, value);
}
@Override
public void removeAttribute(String name) {
Assert.notNull(name, "Attribute name must not be null");
Object value = this.attributes.remove(name);
if (value instanceof HttpSessionBindingListener) {
((HttpSessionBindingListener) value).valueUnbound(new HttpSessionBindingEvent(this, name, value));
}
}
@Override
public void removeValue(String name) {
removeAttribute(name);
}
/**
* Clear all of this session's attributes.
*/
public void clearAttributes() {
for (Iterator<Map.Entry<String, Object>> it = this.attributes.entrySet().iterator(); it.hasNext();) {
Map.Entry<String, Object> entry = it.next();
String name = entry.getKey();
Object value = entry.getValue();
it.remove();
if (value instanceof HttpSessionBindingListener) {
((HttpSessionBindingListener) value).valueUnbound(new HttpSessionBindingEvent(this, name, value));
}
}
}
@Override
public void invalidate() {
this.invalid = true;
clearAttributes();
}
public boolean isInvalid() {
return this.invalid;
}
public void setNew(boolean value) {
this.isNew = value;
}
@Override
public boolean isNew() {
return this.isNew;
}
/**
* Serialize the attributes of this session into an object that can be
* turned into a byte array with standard Java serialization.
*
* @return a representation of this session's serialized state
*/
public Serializable serializeState() {
HashMap<String, Serializable> state = new HashMap<String, Serializable>();
for (Iterator<Map.Entry<String, Object>> it = this.attributes.entrySet().iterator(); it.hasNext();) {
Map.Entry<String, Object> entry = it.next();
String name = entry.getKey();
Object value = entry.getValue();
it.remove();
if (value instanceof Serializable) {
state.put(name, (Serializable) value);
} else {
// Not serializable... Servlet containers usually automatically
// unbind the attribute in this case.
if (value instanceof HttpSessionBindingListener) {
((HttpSessionBindingListener) value).valueUnbound(new HttpSessionBindingEvent(this, name, value));
}
}
}
return state;
}
/**
* Deserialize the attributes of this session from a state object created by
* {@link #serializeState()}.
*
* @param state
* a representation of this session's serialized state
*/
@SuppressWarnings("unchecked")
public void deserializeState(Serializable state) {
Assert.isTrue(state instanceof Map, "Serialized state needs to be of type [java.util.Map]");
this.attributes.putAll((Map<String, Object>) state);
}
}

@ -0,0 +1,30 @@
package com.gitee.sop.gatewaycommon.session;
import org.springframework.data.redis.connection.DefaultStringRedisConnection;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* @author tanghc
*/
public class ApiRedisTemplate extends RedisTemplate<String, Object> {
public ApiRedisTemplate() {
RedisSerializer<String> stringSerializer = new StringRedisSerializer();
setKeySerializer(stringSerializer);
setHashKeySerializer(stringSerializer);
}
public ApiRedisTemplate(RedisConnectionFactory connectionFactory) {
this();
setConnectionFactory(connectionFactory);
afterPropertiesSet();
}
@Override
protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
return new DefaultStringRedisConnection(connection);
}
}

@ -0,0 +1,100 @@
package com.gitee.sop.gatewaycommon.session;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import java.util.concurrent.TimeUnit;
/**
* session管理,默认存放的是{@link ApiHttpSession}采用谷歌guava缓存实现
*
* @author tanghc
*
*/
public class ApiSessionManager implements SessionManager {
private static Logger logger = LoggerFactory.getLogger(ApiSessionManager.class);
private int sessionTimeout = 20;
private LoadingCache<String, HttpSession> cache;
public ApiSessionManager() {
cache = this.buildCache();
}
@Override
public HttpSession getSession(String sessionId) {
if(sessionId == null) {
return this.createSession(sessionId);
}
try {
HttpSession session = cache.get(sessionId);
return session;
} catch (Exception e) {
logger.error(e.getMessage(), e);
throw ErrorEnum.AOP_UNKNOW_ERROR.getErrorMeta().getException();
}
}
/**
* 创建一个session
*
* @param sessionId 传null将返回一个新session
* @return 返回session
*/
protected HttpSession createSession(String sessionId) {
ServletContext servletContext = getServletContext();
HttpSession session = this.newSession(sessionId, servletContext);
session.setMaxInactiveInterval(getSessionTimeout());
this.cache.put(session.getId(), session);
return session;
}
/**
* 返回新的session实例
*
* @param sessionId
* @param servletContext
* @return 返回session
*/
protected HttpSession newSession(String sessionId, ServletContext servletContext) {
return new ApiHttpSession(servletContext, sessionId);
}
protected ServletContext getServletContext() {
return ApiContext.getServletContext();
}
protected LoadingCache<String, HttpSession> buildCache() {
return CacheBuilder.newBuilder().expireAfterAccess(getSessionTimeout(), TimeUnit.MINUTES)
.build(new CacheLoader<String, HttpSession>() {
// 找不到sessionId对应的HttpSession时,进入这个方法
// 找不到就新建一个
@Override
public HttpSession load(String sessionId) throws Exception {
return createSession(sessionId);
}
});
}
public void setSessionTimeout(int sessionTimeout) {
this.sessionTimeout = sessionTimeout;
}
/**
* 过期时间,分钟,默认20分钟
*
* @return 返回过期时间
*/
public int getSessionTimeout() {
return sessionTimeout;
}
}

@ -0,0 +1,264 @@
package com.gitee.sop.gatewaycommon.session;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.Assert;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionContext;
import java.io.Serializable;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* RedisHttpSession
*
* @author tanghc
*/
@SuppressWarnings("deprecation")
public class RedisHttpSession implements HttpSession, Serializable {
private static final long serialVersionUID = -8081963657251144855L;
private static final int SEC60 = 60;
private static final String SESSION_ATTR = "session_attr:";
private static final String CREATION_TIME = "creationTime";
private static final String LAST_ACCESSED_TIME = "lastAccessedTime";
private static final String MAX_INACTIVE_INTERVAL = "maxInactiveInterval";
/**
* 存入redis的key
*/
private String key;
/**
* sessionId
*/
private String id;
private ServletContext servletContext;
private RedisTemplate redisTemplate;
private RedisHttpSession() {
}
protected static String buildId(String id) {
return (id != null ? id : UUID.randomUUID().toString().replace("-", "").toUpperCase());
}
public static String buildKey(String keyPrefix, String sessionId) {
Assert.notNull(keyPrefix, "sessionPrefix不能为null");
return keyPrefix + sessionId;
}
/**
* 创建新的session
*
* @param servletContext
* @param sessionId
* @param sessionTimeout 过期时间单位秒
* @param redisTemplate redis客户端
* @param keyPrefix 存入的key前缀
* @return 返回session
*/
public static RedisHttpSession createNewSession(ServletContext servletContext, String sessionId, int sessionTimeout, RedisTemplate redisTemplate, String keyPrefix) {
Assert.notNull(redisTemplate, "redisTemplate can not null.");
Assert.notNull(sessionId, "sessionId can not null.");
Assert.notNull(keyPrefix, "keyPrefix can not null.");
RedisHttpSession redisHttpSession = new RedisHttpSession();
redisHttpSession.setId(sessionId);
redisHttpSession.setKey(buildKey(keyPrefix, sessionId));
redisHttpSession.setRedisTemplate(redisTemplate);
redisHttpSession.setServletContext(servletContext);
long creationTime = System.currentTimeMillis();
// 过期时间,分转换成秒
int maxInactiveInterval = sessionTimeout * SEC60;
redisHttpSession.setCreationTime(creationTime);
redisHttpSession.setLastAccessedTime(creationTime);
redisHttpSession.setMaxInactiveInterval(maxInactiveInterval);
redisHttpSession.refresh();
return redisHttpSession;
}
/**
* 创建已经存在的session,数据在redis里面
*
* @param sessionId
* @param servletContext
* @param redisTemplate redis客户端
* @param keyPrefix 存入的key前缀
* @return 返回session
*/
public static RedisHttpSession createExistSession(String sessionId, ServletContext servletContext, RedisTemplate redisTemplate, String keyPrefix) {
Assert.notNull(redisTemplate, "redisTemplate can not null.");
Assert.notNull(sessionId, "sessionId can not null.");
Assert.notNull(keyPrefix, "keyPrefix can not null.");
RedisHttpSession redisHttpSession = new RedisHttpSession();
redisHttpSession.setId(sessionId);
redisHttpSession.setKey(buildKey(keyPrefix, sessionId));
redisHttpSession.setRedisTemplate(redisTemplate);
redisHttpSession.setServletContext(servletContext);
redisHttpSession.refresh();
return redisHttpSession;
}
public void setId(String id) {
this.id = id;
}
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
public void setCreationTime(long creationTime) {
this.redisTemplate.opsForHash().put(key, CREATION_TIME, String.valueOf(creationTime));
}
@Override
public long getCreationTime() {
Object createTime = this.redisTemplate.opsForHash().get(key, CREATION_TIME);
return Long.valueOf(String.valueOf(createTime));
}
@Override
public String getId() {
return id;
}
@Override
public long getLastAccessedTime() {
Object lastAccessedTime = this.redisTemplate.opsForHash().get(key, LAST_ACCESSED_TIME);
return Long.valueOf(String.valueOf(lastAccessedTime));
}
@Override
public ServletContext getServletContext() {
return servletContext;
}
@Override
public void setMaxInactiveInterval(int interval) {
this.redisTemplate.opsForHash().put(key, MAX_INACTIVE_INTERVAL, String.valueOf(interval));
}
@Override
public int getMaxInactiveInterval() {
Object maxInactiveInterval = this.redisTemplate.opsForHash().get(key, MAX_INACTIVE_INTERVAL);
return Integer.valueOf(String.valueOf(maxInactiveInterval));
}
@Override
public HttpSessionContext getSessionContext() {
return null;
}
@Override
public Object getAttribute(String name) {
return this.redisTemplate.opsForHash().get(key, SESSION_ATTR + name);
}
@Override
public Object getValue(String name) {
return getAttribute(name);
}
@Override
public Enumeration<String> getAttributeNames() {
return Collections.enumeration(getAttributeKeys());
}
private Set<String> getAttributeKeys() {
Set keys = this.redisTemplate.opsForHash().keys(key);
Set<String> attrNames = new HashSet<>();
for (Object key : keys) {
String k = String.valueOf(key);
if (k.startsWith(SESSION_ATTR)) {
attrNames.add(k.substring(SESSION_ATTR.length()));
}
}
return attrNames;
}
@Override
public String[] getValueNames() {
return getAttributeKeys().toArray(new String[0]);
}
@Override
public void setAttribute(String name, Object value) {
this.redisTemplate.opsForHash().put(key, SESSION_ATTR + name, value);
}
@Override
public void putValue(String name, Object value) {
setAttribute(name, value);
}
@Override
public void removeAttribute(String name) {
this.redisTemplate.opsForHash().delete(key, name);
}
@Override
public void removeValue(String name) {
removeAttribute(name);
}
@Override
public void invalidate() {
this.redisTemplate.delete(key);
}
@Override
public boolean isNew() {
return false;
}
/**
* update expireTime,accessTime
*/
public void refresh() {
// token更新过期时间
this.redisTemplate.expire(key, getMaxInactiveInterval(), TimeUnit.SECONDS);
// 设置访问时间
this.setLastAccessedTime(System.currentTimeMillis());
}
public void setLastAccessedTime(long lastAccessedTime) {
this.redisTemplate.opsForHash().put(key, LAST_ACCESSED_TIME, String.valueOf(lastAccessedTime));
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public boolean isInvalidated() {
return !this.redisTemplate.hasKey(key);
}
public void setRedisTemplate(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
}

@ -0,0 +1,100 @@
package com.gitee.sop.gatewaycommon.session;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.Assert;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import java.util.UUID;
/**
* SessionManager的redis实现使用redis管理session
*
* @author tanghc
*
*/
public class RedisSessionManager implements SessionManager {
private ApiRedisTemplate redisTemplate;
/** 过期时间,30分钟 */
private int sessionTimeout = 30;
/** 存入redis中key的前缀 */
private String keyPrefix = "session:";
public RedisSessionManager(@SuppressWarnings("rawtypes") RedisTemplate redisTemplate) {
Assert.notNull(redisTemplate, "RedisSessionManager中的redisTemplate不能为null");
this.redisTemplate = new ApiRedisTemplate(redisTemplate.getConnectionFactory());
}
@Override
public HttpSession getSession(String sessionId) {
return this.getSession(sessionId, this.keyPrefix);
}
public HttpSession getSession(String sessionId, String keyPrefix) {
if (this.hasKey(sessionId)) {
return RedisHttpSession.createExistSession(sessionId, getServletContext(), redisTemplate, keyPrefix);
} else {
sessionId = this.buildSessionId(sessionId);
return RedisHttpSession.createNewSession(getServletContext(), sessionId, this.getSessionTimeout(),
redisTemplate, keyPrefix);
}
}
/**
* 构建sessionId
* @param id
* @return 返回sessionid
*/
public String buildSessionId(String id) {
return (id != null ? id : UUID.randomUUID().toString().replace("-", "").toUpperCase());
}
public boolean hasKey(String sessionId) {
if (sessionId == null) {
return false;
} else {
String key = RedisHttpSession.buildKey(this.keyPrefix, sessionId);
return redisTemplate.hasKey(key);
}
}
public ServletContext getServletContext() {
return ApiContext.getServletContext();
}
public int getSessionTimeout() {
return sessionTimeout;
}
/**
* 设置session过期时间单位分钟
* @param sessionTimeout
*/
public void setSessionTimeout(int sessionTimeout) {
this.sessionTimeout = sessionTimeout;
}
public ApiRedisTemplate getRedisTemplate() {
return redisTemplate;
}
public void setRedisTemplate(ApiRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
public String getKeyPrefix() {
return keyPrefix;
}
/**
* 设置存入redis中key的前缀默认为"session:"
* @param keyPrefix
*/
public void setKeyPrefix(String keyPrefix) {
this.keyPrefix = keyPrefix;
}
}

@ -0,0 +1,20 @@
package com.gitee.sop.gatewaycommon.session;
import javax.servlet.http.HttpSession;
/**
* session管理
*
* @author tanghc
*
*/
public interface SessionManager {
/**
* 根据sessionId获取session
*
* @param sessionId 客户端传过来的sessionId,为null时创建一个新session
* @return 返回session
*/
HttpSession getSession(String sessionId);
}

@ -0,0 +1,146 @@
package com.gitee.sop.gatewaycommon.util;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
/**
* AES-128 ECB加密.<br>
*
* <pre>
* 字符集:UTF-8
* 算法模式:ECB
* 数据块:128位
* 补码方式:PKCS5Padding
* 加密结果编码方式:Base64
* </pre>
*
* @author tanghc
*
*/
public class AESUtil {
private static final String UTF8 = "UTF-8";
private static final String ALGORITHM = "AES";
/** 默认的加密算法 */
private static final String ALGORITHM_CIPHER = "AES/ECB/PKCS5Padding";
private static final int LIMIT_LEN = 16;
/**
* 生成一个SecretKey
* @param password 长度必须小于等于16
* @return 返回SecretKey
*/
public static SecretKey getSecretKey(String password) {
byte[] passwordData = password.getBytes();
if(passwordData.length > LIMIT_LEN) {
throw new IllegalArgumentException("password 长度必须小于等于16");
}
// 创建一个空的16位字节数组(默认值为0),16byte(128bit)
byte[] keyData = new byte[16];
System.arraycopy(passwordData, 0, keyData, 0, passwordData.length);
return new SecretKeySpec(keyData, ALGORITHM);
}
/**
* 加密
* @param data 待加密数据
* @param password 密码
* @return 返回加密成功后数据
* @throws Exception
*/
public static byte[] encrypt(byte[] data, String password) throws Exception {
SecretKey secretKey = getSecretKey(password);
// Ciphr完成加密或解密工作类
Cipher cipher = Cipher.getInstance(ALGORITHM_CIPHER);
// 对Cipher初始化,解密模式
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
// 加密data
return cipher.doFinal(data);
}
/**
* 解密
* @param data 待解密数据
* @param password 密码
* @return 返回解密后的数据
* @throws Exception
*/
public static byte[] decrypt(byte[] data, String password) throws Exception {
SecretKey secretKey = getSecretKey(password);
// Cipher完成加密或解密工作类
Cipher cipher = Cipher.getInstance(ALGORITHM_CIPHER);
// 对Cipher初始化,解密模式
cipher.init(Cipher.DECRYPT_MODE, secretKey);
// 解密data
return cipher.doFinal(data);
}
/**
* 文本加密
* @param content 明文
* @param password 密码
* @return 返回base64内容
* @throws Exception
*/
public static String encryptToBase64String(String content, String password) throws Exception {
byte[] data = content.getBytes(UTF8);
byte[] result = encrypt(data, password);
return Base64.encodeBase64String(result);
}
/**
* 文本解密
* @param base64String 待解密文本
* @param password 密码
* @return 返回明文
* @throws Exception
*/
public static String decryptFromBase64String(String base64String, String password) throws Exception {
byte[] data = Base64.decodeBase64(base64String);
byte[] contentData = decrypt(data, password);
return new String(contentData, UTF8);
}
/**
* 文本加密
* @param content 明文
* @param password 密码
* @return 返回16进制内容
* @throws Exception
*/
public static String encryptToHex(String content, String password) throws Exception {
byte[] data = content.getBytes(UTF8);
byte[] result = encrypt(data, password);
return Hex.encodeHexString(result);
}
/**
* 文本解密
* @param hex 待解密文本
* @param password 密码
* @return 返回明文
* @throws Exception
*/
public static String decryptFromHex(String hex, String password) throws Exception {
byte[] data = Hex.decodeHex(hex);
byte[] contentData = decrypt(data, password);
return new String(contentData,UTF8);
}
/*public static void main(String[] args) throws Exception {
String content = "我爱你";
String password = "1234567890123456";
System.out.println("password:" + password);
String ret2 = encryptToBase64String(content, password);
System.out.println("密文:" + ret2);
String content3 = decryptFromBase64String(ret2, password);
System.out.println(content.equals(content3));
}*/
}

@ -0,0 +1,25 @@
package com.gitee.sop.gatewaycommon.util;
/**
* @author tanghc
*/
public class KeyStore {
private String publicKey;
private String privateKey;
public String getPublicKey() {
return publicKey;
}
public void setPublicKey(String publicKey) {
this.publicKey = publicKey;
}
public String getPrivateKey() {
return privateKey;
}
public void setPrivateKey(String privateKey) {
this.privateKey = privateKey;
}
}

@ -0,0 +1,208 @@
package com.gitee.sop.gatewaycommon.util;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
* RSA加解密工具<br>
* @author tanghc
*/
public class RSANewUtil {
public static final String RSA_ALGORITHM = "RSA";
public static final String UTF8 = "UTF-8";
/**
* 创建公钥私钥
*
* @return 返回公私钥对
* @throws Exception
*/
public static KeyStore createKeys() throws Exception {
KeyPairGenerator keyPairGeno = KeyPairGenerator.getInstance(RSA_ALGORITHM);
keyPairGeno.initialize(1024);
KeyPair keyPair = keyPairGeno.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
KeyStore keyStore = new KeyStore();
keyStore.setPublicKey(Base64.encodeBase64String(publicKey.getEncoded()));
keyStore.setPrivateKey(Base64.encodeBase64String(privateKey.getEncoded()));
return keyStore;
}
/**
* 获取公钥对象
*
* @param pubKeyData 公钥数据
* @return 公钥对象
* @throws Exception
*/
public static RSAPublicKey getPublicKey(byte[] pubKeyData) throws Exception {
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(pubKeyData);
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
return (RSAPublicKey) keyFactory.generatePublic(keySpec);
}
/**
* 获取公钥对象
*
* @param pubKey
* 公钥
* @return 返回公钥对象
* @throws Exception
*/
public static RSAPublicKey getPublicKey(String pubKey) throws Exception {
return getPublicKey(Base64.decodeBase64(pubKey));
}
/**
* 获取私钥对象
*
* @param priKey
* 私钥
* @return 返回私钥对象
* @throws Exception
*/
public static RSAPrivateKey getPrivateKey(String priKey) throws Exception {
return getPrivateKey(Base64.decodeBase64(priKey));
}
/**
* 通过私钥byte[]将公钥还原适用于RSA算法
*
* @param keyBytes 私钥数据
* @return 返回公钥对象
* @throws Exception
*/
public static RSAPrivateKey getPrivateKey(byte[] keyBytes) throws Exception {
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
}
public static String encryptByPublicKey(String data, String publicKey) throws Exception {
return encryptByPublicKey(data, getPublicKey(publicKey));
}
/**
* 公钥加密
*
* @param data 内容
* @param publicKey 公钥
* @return 返回密文
* @throws Exception
*/
public static String encryptByPublicKey(String data, RSAPublicKey publicKey) throws Exception {
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] bytes = cipher.doFinal(data.getBytes(UTF8));
return Base64.encodeBase64String(bytes);
}
public static String decryptByPublicKey(String data, String rsaPublicKey) throws Exception {
return decryptByPublicKey(data, getPublicKey(rsaPublicKey));
}
/**
* 公钥解密
*
* @param data 待解密内容
* @param rsaPublicKey 公钥
* @return 返回明文
* @throws Exception
*/
public static String decryptByPublicKey(String data, RSAPublicKey rsaPublicKey) throws Exception {
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, rsaPublicKey);
byte[] inputData = Base64.decodeBase64(data);
byte[] bytes = cipher.doFinal(inputData);
return new String(bytes, UTF8);
}
public static String encryptByPrivateKey(String data, String privateKey) throws Exception {
return encryptByPrivateKey(data, getPrivateKey(privateKey));
}
/**
* 私钥加密
*
* @param data 内容
* @param privateKey 私钥
* @return 返回密文
* @throws Exception
*/
public static String encryptByPrivateKey(String data, RSAPrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] bytes = cipher.doFinal(data.getBytes(UTF8));
return Base64.encodeBase64String(bytes);
}
public static String decryptByPrivateKey(String data, String privateKey) throws Exception {
return decryptByPrivateKey(data, getPrivateKey(privateKey));
}
/**
* 私钥解密
*
* @param data 待解密内容
* @param privateKey 私钥
* @return 返回明文
* @throws Exception
*/
public static String decryptByPrivateKey(String data, RSAPrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] inputData = Base64.decodeBase64(data);
byte[] bytes = cipher.doFinal(inputData);
return new String(bytes, UTF8);
}
/*
pubKey:
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCG/iIZZzb16PxKqslkDMYa4tVFb3IVPBpLj4BgHQmDfe843sG4gkJIPXCm7+t6QxIbfDfynBpqZJLvu0c6E7TqlCtynBIlRFOBZrQVNEFkaanR2Kln3vd3CIidR571UstOC32XDyqAQNlvjD19zeIDVfmLa0Q+Or0zaxY99QwBHwIDAQAB
priKey:
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIb+IhlnNvXo/EqqyWQMxhri1UVvchU8GkuPgGAdCYN97zjewbiCQkg9cKbv63pDEht8N/KcGmpkku+7RzoTtOqUK3KcEiVEU4FmtBU0QWRpqdHYqWfe93cIiJ1HnvVSy04LfZcPKoBA2W+MPX3N4gNV+YtrRD46vTNrFj31DAEfAgMBAAECgYBiNPQdwwcq86rHr2QAE4L0AF3ju+YlKKqAmg9s3PMU5ENq/jO0xZ7u6zPPXu/S7IR51m7lY0ecazqyiW6SA9AzYH7ImWWkZ4stZ03beTB2US3cSeJIkugoexoN5fQRAGZiZezTLs91CeJivESOZyDKnnQdgJ49mveBV5OvievD8QJBAMztpqiWWavdR4tqQ+plat+rwYoXqejsK3Hyfg0pVJqEdazve2sr74rla7yI9P47ZAh1sklCv0CO//ctICv366UCQQCoop3T0FeZtbKJG+fHzZvpAe63tXpdhLMaQvTBuXLG8vi78Wyfhg5r7HOWR0Z1V7nzF1gzMywL53Pmkq9tB65zAkAiHu/A4kfL9ewTqn3kaT6CP3baJ1aDEc+qCVYzms4bbDKruLQ0A/y+g7SMj8E7E2h0gCRPTm3JsgWsgjb5Gy6BAkAA8mjQd6sGQe7utilnBdCKTmh4v5wgSk53J0kYjWIHm/WpmIFzo90Q3hMIFP5gSk3Q/6CPKQpmRrZv5QL3KcPhAkEAuMoQbij/7hyLlIxRHZs2SMXxfHPiZgDc6rVi1KNxeq8HXTlERi7Npc2Uz5TeWN4JwBBx9uA50zowk9iS05nclQ==
用公钥加密mi : c3B0jtMdvkqrgaPxHZCK2cXMUQC2QzLud2ouLMNx0nBAj9k2/ytOuVJViTGe/DozB/ky5jvl4spD9Ey6aTMrwLHfQVhn0gRJ+wHcmx/51dXQDIgsldt6bf7YpdPdnghBjQz2+P5RhqSkeFDbTZKkl2BNaLE78a/OyWWeCGwN+4s=
true
用私钥加密mi2 : QU5vDnQ1ukj8GsauokFlgcB/g61U882tj82wHGrrqHEnvaga+4cXjML9RhjpZtKqwDGZTCujsmpynDk4qek6IGOQ/oxdWLwV4ZNjfa/oqA8OFDothVUT8wpqCu9kOYHrTdGybmXD0dB2Iy1/AMQTAgPNNXXiRXdvsz9xWYTV6z8=
true
*/
/*public static void main(String[] args) throws Exception {
KeyStore keys = createKeys();
String pubKey = keys.getPublicKey();
System.out.println("pubKey:");
System.out.println(pubKey);
String priKey = keys.getPrivateKey();
System.out.println("priKey:");
System.out.println(priKey);
String ming = "1234567890123456";
// 用公钥加密
String mi = encryptByPublicKey(ming, pubKey);
System.out.println("用公钥加密mi : " + mi);
// 用私钥解密
System.out.println(ming.equals(decryptByPrivateKey(mi, priKey)));
// 用私钥加密
String mi2 = encryptByPrivateKey(ming, priKey);
System.out.println("用私钥加密mi2 : " + mi2);
// 用公钥解密
String ming2 = decryptByPublicKey(mi2, pubKey);
System.out.println(ming.equals(ming2));
}*/
}

@ -0,0 +1,312 @@
package com.gitee.sop.gatewaycommon.util;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
* RSA加解密工具<br>
* @author tanghc
*/
public class RSAUtil {
public static String RSA_ALGORITHM = "RSA";
/**
* 创建公钥私钥
*
* @return 返回公私钥对
* @throws Exception
*/
public static KeyStore createKeys() throws Exception {
KeyPairGenerator keyPairGeno = KeyPairGenerator.getInstance(RSA_ALGORITHM);
keyPairGeno.initialize(1024);
KeyPair keyPair = keyPairGeno.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
KeyStore keyStore = new KeyStore();
keyStore.setPublicKey(Base64.encodeBase64String(publicKey.getEncoded()));
keyStore.setPrivateKey(Base64.encodeBase64String(privateKey.getEncoded()));
return keyStore;
}
/**
* 获取公钥对象
*
* @param pubKeyData 公钥
* @return 返回公钥对象
* @throws Exception
*/
public static RSAPublicKey getPublicKey(byte[] pubKeyData) throws Exception {
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(pubKeyData);
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
return (RSAPublicKey) keyFactory.generatePublic(keySpec);
}
/**
* 获取公钥对象
*
* @param pubKey
* 公钥
* @return 返回私钥对象
* @throws Exception
*/
public static RSAPublicKey getPublicKey(String pubKey) throws Exception {
return getPublicKey(Base64.decodeBase64(pubKey));
}
/**
* 获取私钥对象
*
* @param priKey
* 私钥
* @return 私钥对象
* @throws Exception
*/
public static RSAPrivateKey getPrivateKey(String priKey) throws Exception {
return getPrivateKey(Base64.decodeBase64(priKey));
}
/**
* 通过私钥byte[]将公钥还原适用于RSA算法
*
* @param keyBytes
* @return 返回私钥
* @throws Exception
*/
public static RSAPrivateKey getPrivateKey(byte[] keyBytes) throws Exception {
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
}
/**
* 公钥加密
*
* @param data 待加密内容
* @param publicKey 公钥
* @return 返回密文
* @throws Exception
*/
public static String encryptByPublicKey(String data, RSAPublicKey publicKey) throws Exception {
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
// 模长
int key_len = publicKey.getModulus().bitLength() / 8;
// 加密数据长度 <= 模长-11
String[] datas = splitString(data, key_len - 11);
String mi = "";
// 如果明文长度大于模长-11则要分组加密
for (String s : datas) {
mi += bcd2Str(cipher.doFinal(s.getBytes()));
}
return mi;
}
public static String encryptByPrivateKey(String data, String privateKey) throws Exception {
return encryptByPrivateKey(data, getPrivateKey(privateKey));
}
/**
* 私钥加密
*
* @param data 待加密数据
* @param privateKey 私钥
* @return 返回密文
* @throws Exception
*/
public static String encryptByPrivateKey(String data, RSAPrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
// 模长
int key_len = privateKey.getModulus().bitLength() / 8;
// 加密数据长度 <= 模长-11
String[] datas = splitString(data, key_len - 11);
String mi = "";
// 如果明文长度大于模长-11则要分组加密
for (String s : datas) {
mi += bcd2Str(cipher.doFinal(s.getBytes()));
}
return mi;
}
public static String decryptByPrivateKey(String data, String privateKey) throws Exception {
return decryptByPrivateKey(data, getPrivateKey(privateKey));
}
/**
* 私钥解密
*
* @param data 待解密内容
* @param privateKey 私钥
* @return 返回明文
* @throws Exception
*/
public static String decryptByPrivateKey(String data, RSAPrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
// 模长
int key_len = privateKey.getModulus().bitLength() / 8;
byte[] bytes = data.getBytes();
byte[] bcd = ASCII_To_BCD(bytes, bytes.length);
// 如果密文长度大于模长则要分组解密
String ming = "";
byte[][] arrays = splitArray(bcd, key_len);
for (byte[] arr : arrays) {
ming += new String(cipher.doFinal(arr));
}
return ming;
}
/**
* 公钥解密
*
* @param data 待解密内容
* @param rsaPublicKey 公钥
* @return 返回明文
* @throws Exception
*/
public static String decryptByPublicKey(String data, RSAPublicKey rsaPublicKey) throws Exception {
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, rsaPublicKey);
// 模长
int key_len = rsaPublicKey.getModulus().bitLength() / 8;
byte[] bytes = data.getBytes();
byte[] bcd = ASCII_To_BCD(bytes, bytes.length);
// 如果密文长度大于模长则要分组解密
String ming = "";
byte[][] arrays = splitArray(bcd, key_len);
for (byte[] arr : arrays) {
ming += new String(cipher.doFinal(arr));
}
return ming;
}
/**
* ASCII码转BCD码
*
*/
public static byte[] ASCII_To_BCD(byte[] ascii, int asc_len) {
byte[] bcd = new byte[asc_len / 2];
int j = 0;
for (int i = 0; i < (asc_len + 1) / 2; i++) {
bcd[i] = asc_to_bcd(ascii[j++]);
bcd[i] = (byte) (((j >= asc_len) ? 0x00 : asc_to_bcd(ascii[j++]) & 0xff) + (bcd[i] << 4));
}
return bcd;
}
public static byte asc_to_bcd(byte asc) {
byte bcd;
if ((asc >= '0') && (asc <= '9')) {
bcd = (byte) (asc - '0');
} else if ((asc >= 'A') && (asc <= 'F')) {
bcd = (byte) (asc - 'A' + 10);
} else if ((asc >= 'a') && (asc <= 'f')) {
bcd = (byte) (asc - 'a' + 10);
} else {
bcd = (byte) (asc - 48);
}
return bcd;
}
/**
* BCD转字符串
*/
public static String bcd2Str(byte[] bytes) {
char[] temp = new char[bytes.length * 2];
char val;
for (int i = 0; i < bytes.length; i++) {
val = (char) (((bytes[i] & 0xf0) >> 4) & 0x0f);
temp[i * 2] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
val = (char) (bytes[i] & 0x0f);
temp[i * 2 + 1] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
}
return new String(temp);
}
/**
* 拆分字符串
*/
public static String[] splitString(String string, int len) {
int x = string.length() / len;
int y = string.length() % len;
int z = 0;
if (y != 0) {
z = 1;
}
String[] strings = new String[x + z];
String str = "";
for (int i = 0; i < x + z; i++) {
if (i == x + z - 1 && y != 0) {
str = string.substring(i * len, i * len + y);
} else {
str = string.substring(i * len, i * len + len);
}
strings[i] = str;
}
return strings;
}
/**
* 拆分数组
*/
public static byte[][] splitArray(byte[] data, int len) {
int x = data.length / len;
int y = data.length % len;
int z = 0;
if (y != 0) {
z = 1;
}
byte[][] arrays = new byte[x + z][];
byte[] arr;
for (int i = 0; i < x + z; i++) {
arr = new byte[len];
if (i == x + z - 1 && y != 0) {
System.arraycopy(data, i * len, arr, 0, y);
} else {
System.arraycopy(data, i * len, arr, 0, len);
}
arrays[i] = arr;
}
return arrays;
}
/*public static void main(String[] args) throws Exception {
KeyStore keys = createKeys();
String pubKey = keys.getPublicKey();
System.out.println("pubKey:");
System.out.println(pubKey);
String priKey = keys.getPrivateKey();
System.out.println("priKey:");
System.out.println(priKey);
String ming = "6460201d23954f8e90cf79b818844ca0";
// 用公钥加密
String mi = encryptByPublicKey(ming, getPublicKey(pubKey));
System.out.println("mi : " + mi);
// 用私钥解密
System.out.println("ming : " + decryptByPrivateKey(mi, getPrivateKey(priKey)));
// 用私钥加密
String mi2 = encryptByPrivateKey(ming, getPrivateKey(priKey));
System.out.println("mi2 : " + mi2);
// 用公钥解密
System.out.println("ming2 : " + decryptByPublicKey(mi2, getPublicKey(pubKey)));
}*/
}

@ -0,0 +1,139 @@
package com.gitee.sop.gatewaycommon.util;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import java.util.Collections;
/**
* redis分布式锁详见https://blog.csdn.net/thc1987/article/details/80355155<br>
* 思路
* <pre>
* 用SETNX命令SETNX只有在key不存在时才返回成功这意味着只有一个线程可以成功运行SETNX命令而其他线程会失败然后不断重试直到它们能建立锁
* 然后使用脚本来创建锁因为一个redis脚本同一时刻只能运行一次
* 创建锁代码
* <code>
-- KEYS[1] key,
-- ARGV[1] value,
-- ARGV[2] expireTimeMilliseconds
if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then
redis.call('pexpire', KEYS[1], ARGV[2])
return 1
else
return 0
end
* </code>
* 最后使用脚本来解锁
* 解锁代码
*
* <code>
-- KEYS[1] key,
-- ARGV[1] value
if redis.call("get", KEYS[1]) == ARGV[1]
then
return redis.call("del", KEYS[1])
else
return 0
end
* </code>
* </pre>
*
* @author tanghc
*/
public class RedisLockUtil {
private static final Long SUCCESS = 1L;
/** 加锁脚本 */
private static final String SCRIPT_LOCK = "if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then redis.call('pexpire', KEYS[1], ARGV[2]) return 1 else return 0 end";
/** 解锁脚本 */
private static final String SCRIPT_UNLOCK = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
/** 加锁脚本sha1值 */
private static final String SCRIPT_LOCK_SHA1 = DigestUtils.sha1Hex(SCRIPT_LOCK);
/** 解锁脚本sha1值 */
private static final String SCRIPT_UNLOCK_SHA1 = DigestUtils.sha1Hex(SCRIPT_UNLOCK);
/**
* 尝试获取分布式锁
*
* @param redisTemplate
* Redis客户端
* @param lockKey
*
* @param requestId
* 请求标识
* @param expireTimeMilliseconds
* 超期时间多少毫秒后这把锁自动释放
* @return 返回true表示拿到锁
*/
@SuppressWarnings("unchecked")
public static boolean tryGetDistributedLock(@SuppressWarnings("rawtypes") final RedisTemplate redisTemplate,
final String lockKey, final String requestId, final int expireTimeMilliseconds) {
Object result = redisTemplate.execute(new RedisScript<Long>() {
@Override
public String getSha1() {
return SCRIPT_LOCK_SHA1;
}
@Override
public Class<Long> getResultType() {
return Long.class;
}
@Override
public String getScriptAsString() {
return SCRIPT_LOCK;
}
},
// KEYS[1]
Collections.singletonList(lockKey),
// ARGV[1]
requestId,
// ARGV[2]
String.valueOf(expireTimeMilliseconds)
);
return SUCCESS.equals(result);
}
/**
* 释放分布式锁
*
* @param redisTemplate
* Redis客户端
* @param lockKey
*
* @param requestId
* 请求标识
* @return 返回true表示释放锁成功
*/
@SuppressWarnings("unchecked")
public static boolean releaseDistributedLock(@SuppressWarnings("rawtypes") RedisTemplate redisTemplate,
String lockKey, String requestId) {
Object result = redisTemplate.execute(new RedisScript<Long>() {
@Override
public String getSha1() {
return SCRIPT_UNLOCK_SHA1;
}
@Override
public Class<Long> getResultType() {
return Long.class;
}
@Override
public String getScriptAsString() {
return SCRIPT_UNLOCK;
}
}, Collections.singletonList(lockKey), requestId);
return SUCCESS.equals(result);
}
}

@ -0,0 +1,137 @@
package com.gitee.sop.gatewaycommon.util;
import com.alibaba.fastjson.JSON;
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* @author tanghc
*/
public class RequestUtil {
private static final String CONTENT_TYPE_URLENCODED = "application/x-www-form-urlencoded";
private static final String CONTENT_TYPE_JSON = "application/json";
private static final String CONTENT_TYPE_TEXT = "text/plain";
private static final String UTF8 = "UTF-8";
private static final String GET = "get";
private static final String UNKOWN = "unknown";
private static final String LOCAL_IP = "127.0.0.1";
private static final int IP_LEN = 15;
public static String getText(HttpServletRequest request) throws Exception {
return IOUtils.toString(request.getInputStream(), UTF8);
}
/**
* 从request中获取json如果提交方式是application/x-www-form-urlencoded则组装成json格式
*
* @param request request对象
* @return 返回json
* @throws IOException
*/
public static String getJson(HttpServletRequest request) throws Exception {
String requestJson = null;
String contectType = request.getContentType();
if (StringUtils.isBlank(contectType)) {
throw ErrorEnum.ISV_INVALID_CONTENT_TYPE.getErrorMeta().getException(contectType);
}
contectType = contectType.toLowerCase();
if (contectType.contains(CONTENT_TYPE_JSON) || contectType.contains(CONTENT_TYPE_TEXT)) {
requestJson = getText(request);
} else if (contectType.contains(CONTENT_TYPE_URLENCODED)) {
Map<String, Object> params = convertRequestParamsToMap(request);
requestJson = JSON.toJSONString(params);
} else {
throw ErrorEnum.ISV_INVALID_CONTENT_TYPE.getErrorMeta().getException(contectType);
}
return requestJson;
}
/**
* request中的参数转换成map
*
* @param request request对象
* @return 返回参数键值对
*/
public static Map<String, Object> convertRequestParamsToMap(HttpServletRequest request) {
Map<String, String[]> paramMap = request.getParameterMap();
if(paramMap == null || paramMap.isEmpty()) {
return Collections.emptyMap();
}
Map<String, Object> retMap = new HashMap<String, Object>(paramMap.size());
Set<Entry<String, String[]>> entrySet = paramMap.entrySet();
for (Entry<String, String[]> entry : entrySet) {
String name = entry.getKey();
String[] values = entry.getValue();
if (values.length == 1) {
retMap.put(name, values[0]);
}
}
return retMap;
}
/**
* 获取客户端真实IP
*
* @param request request对象
* @return 返回ip
*/
public static String getClientIP(HttpServletRequest request) {
String ipAddress = request.getHeader("x-forwarded-for");
if (ipAddress == null || ipAddress.length() == 0 || UNKOWN.equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || UNKOWN.equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || UNKOWN.equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if (LOCAL_IP.equals(ipAddress)) {
// 根据网卡取本机配置的IP
try {
InetAddress inet = InetAddress.getLocalHost();
ipAddress = inet.getHostAddress();
} catch (UnknownHostException e) {
// ignore
}
}
}
// 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
if (ipAddress != null && ipAddress.length() > IP_LEN) {
if (ipAddress.indexOf(",") > 0) {
ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
}
}
return ipAddress;
}
/**
* 是否是get请求
* @param request request对象
* @return true
*/
public static boolean isGetRequest(HttpServletRequest request) {
return GET.equalsIgnoreCase(request.getMethod());
}
}

@ -0,0 +1,119 @@
package com.gitee.sop.gatewaycommon.util;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author tanghc
*/
public class ResponseUtil {
public static final String UTF_8 = "UTF-8";
private static Logger log = LoggerFactory.getLogger(ResponseUtil.class);
public static void writeJson(HttpServletResponse response, Object result) {
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
response.setCharacterEncoding(UTF_8);
try {
response.getWriter().write(JSON.toJSONString(result));
} catch (IOException e) {
log.error("doWriter", e);
}
}
/**
* map转成xml
*
* @param parameters
* @return
*/
public static String mapToXml(JSONObject parameters) {
String content = doMap2xml(parameters);
return content;
}
private static String doMap2xml(JSONObject parameters) {
StringBuffer sb = new StringBuffer();
Set es = parameters.entrySet();
Iterator it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
Object v = entry.getValue();
if (v instanceof JSONObject) {
sb.append("<").append(k).append(">")
.append(doMap2xml((JSONObject) v))
.append("</").append(k).append(">");
} else if (v instanceof JSONArray) {
JSONArray collection = (JSONArray) v;
String items = buildItems(k + "_item", collection);
sb.append("<").append(k).append(">")
.append(items)
.append("</").append(k).append(">");
} else {
sb.append("<").append(k).append("><![CDATA[")
.append(v)
.append("]]></").append(k).append(">");
}
}
return sb.toString();
}
private static String buildItems(String key, JSONArray collection) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < collection.size(); i++) {
Object jsonObject = collection.get(i);
sb.append("<").append(key).append(">");
if (jsonObject instanceof JSONObject) {
sb.append(doMap2xml((JSONObject) jsonObject));
} else {
sb.append(jsonObject);
}
sb.append("</").append(key).append(">");
}
return sb.toString();
}
public static void main(String[] args) {
Persion persion = new Persion();
persion.setId(1);
persion.setName("aaa");
String jsonString = JSON.toJSONString(persion);
JSONObject jsonObject = JSON.parseObject(jsonString);
String xml = mapToXml(jsonObject);
System.out.println(xml);
}
@Data
public static class Persion {
int id;
String name;
List<String> items = Arrays.asList("item1", "item2");
List<Man> child = Arrays.asList(new Man("Jim"), new Man("Tom"));
}
@Data
public static class Man{
String name;
public Man(String name) {
this.name = name;
}
}
}

@ -0,0 +1,32 @@
package com.gitee.sop.gatewaycommon.util;
import com.gitee.sop.gatewaycommon.result.ApiResult;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.naming.NoNameCoder;
import com.thoughtworks.xstream.io.xml.StaxDriver;
/**
* @author tanghc
*/
public class XmlUtil {
private static XStream xStream = new XStream(new StaxDriver(new NoNameCoder()));
static {
xStream.processAnnotations(ApiResult.class);
xStream.aliasSystemAttribute(null, "class");
}
public static String serialize(Object obj) {
return getXStream().toXML(obj);
}
public static <T> T unserialize(String xml, Class<T> clazz) {
xStream.processAnnotations(clazz);
Object object = xStream.fromXML(xml);
T cast = clazz.cast(object);
return cast;
}
public static XStream getXStream() {
return xStream;
}
}

@ -0,0 +1,36 @@
package com.gitee.sop.gatewaycommon.validate;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
import com.gitee.sop.gatewaycommon.param.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import javax.servlet.http.HttpServletRequest;
/**
* @author tanghc
*/
@Slf4j
public abstract class AbstractSigner implements Signer {
/**
* 构建服务端签名串
*
* @param params
* @param secret
* @return
*/
protected abstract String buildServerSign(ApiParam params, String secret);
@Override
public boolean checkSign(HttpServletRequest request, String secret) {
ApiParam apiParam = ApiContext.getApiParam();
String clientSign = apiParam.fetchSignAndRemove();
if (StringUtils.isBlank(clientSign)) {
throw ErrorEnum.ISV_MISSING_SIGNATURE.getErrorMeta().getException();
}
String serverSign = buildServerSign(apiParam, secret);
return clientSign.equals(serverSign);
}
}

@ -0,0 +1,60 @@
package com.gitee.sop.gatewaycommon.validate;
import org.apache.commons.codec.digest.DigestUtils;
import com.gitee.sop.gatewaycommon.util.AESUtil;
import com.gitee.sop.gatewaycommon.util.RSANewUtil;
import com.gitee.sop.gatewaycommon.util.RSAUtil;
/**
* 负责各类加解密
* @author tanghc
*
*/
public class ApiEncrypter implements Encrypter {
@Override
public String aesEncryptToHex(String content, String password) throws Exception {
return AESUtil.encryptToHex(content, password);
}
@Override
public String aesDecryptFromHex(String hex, String password) throws Exception {
return AESUtil.decryptFromHex(hex, password);
}
@Override
public String aesEncryptToBase64String(String content, String password) throws Exception {
return AESUtil.encryptToBase64String(content, password);
}
@Override
public String aesDecryptFromBase64String(String base64String, String password) throws Exception {
return AESUtil.decryptFromBase64String(base64String, password);
}
@Override
public String rsaDecryptByPrivateKey(String data, String privateKey) throws Exception {
return RSAUtil.decryptByPrivateKey(data, privateKey);
}
@Override
public String rsaEncryptByPrivateKey(String data, String privateKey) throws Exception {
return RSAUtil.encryptByPrivateKey(data, privateKey);
}
@Override
public String rsaDecryptByPrivateKeyNew(String data, String privateKey) throws Exception {
return RSANewUtil.decryptByPrivateKey(data, privateKey);
}
@Override
public String rsaEncryptByPrivateKeyNew(String data, String privateKey) throws Exception {
return RSANewUtil.encryptByPrivateKey(data, privateKey);
}
@Override
public String md5(String value) {
return DigestUtils.md5Hex(value);
}
}

@ -0,0 +1,56 @@
package com.gitee.sop.gatewaycommon.validate;
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
import com.gitee.sop.gatewaycommon.param.ApiParam;
import org.apache.tomcat.util.buf.HexUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 签名验证实现
*
* @author tanghc
*/
public class ApiSigner extends AbstractSigner {
private Map<String, SignEncipher> signEncipherMap = new HashMap<>();
public ApiSigner() {
signEncipherMap.put("md5", new SignEncipherMD5());
signEncipherMap.put("hmac", new SignEncipherHMAC_MD5());
}
@Override
public String buildServerSign(ApiParam param, String secret) {
String signMethod = param.fetchSignMethod();
SignEncipher signEncipher = signEncipherMap.get(signMethod);
if (signEncipher == null) {
throw ErrorEnum.ISV_INVALID_SIGNATURE_TYPE.getErrorMeta().getException(signMethod);
}
// 第一步:参数排序
Set<String> keySet = param.keySet();
List<String> paramNames = new ArrayList<String>(keySet);
Collections.sort(paramNames);
// 第二步:把所有参数名和参数值串在一起
StringBuilder paramNameValue = new StringBuilder();
for (String paramName : paramNames) {
paramNameValue.append(paramName).append(param.get(paramName));
}
// 第三步:使用MD5/HMAC加密
String source = paramNameValue.toString();
byte[] bytes = signEncipher.encrypt(source, secret);
// 第四步:把二进制转化为大写的十六进制
return HexUtils.toHexString(bytes).toUpperCase();
}
}

@ -0,0 +1,150 @@
package com.gitee.sop.gatewaycommon.validate;
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
import com.gitee.sop.gatewaycommon.param.ApiParam;
import com.gitee.sop.gatewaycommon.param.ParamNames;
import com.gitee.sop.gatewaycommon.param.UploadContext;
import com.gitee.sop.gatewaycommon.secret.AppSecretManager;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
/**
* 负责校验,校验工作都在这里
*
* @author tanghc
*/
public class ApiValidator implements Validator {
private static final Logger logger = LoggerFactory.getLogger(ApiValidator.class);
private static final int MILLISECOND_OF_ONE_SECOND = 1000;
private static List<String> FORMAT_LIST = Arrays.asList("json", "xml");
@Override
public void validate(ApiParam param) {
ApiConfig apiConfig = ApiContext.getApiConfig();
if (apiConfig.isIgnoreValidate() || param.fetchIgnoreValidate()) {
logger.debug("忽略所有验证(ignoreValidate=true), name:{}, version:{}", param.fetchName(), param.fetchVersion());
return;
}
if (param.fetchIgnoreSign()) {
logger.debug("忽略签名验证, name:{}, version:{}", param.fetchName(), param.fetchVersion());
} else {
// 需要验证签名
checkAppKey(param);
checkSign(param);
}
checkUploadFile(param);
checkTimeout(param);
checkFormat(param);
}
/**
* 校验上传文件内容
*
* @param param
*/
protected void checkUploadFile(ApiParam param) {
UploadContext uploadContext = ApiContext.getUploadContext();
if (uploadContext != null) {
try {
List<MultipartFile> files = uploadContext.getAllFile();
for (MultipartFile file : files) {
// 客户端传来的文件md5
String clientMd5 = param.getString(file.getName());
if (clientMd5 != null) {
String fileMd5 = DigestUtils.md5Hex(file.getBytes());
if (!clientMd5.equals(fileMd5)) {
throw ErrorEnum.ISV_UPLOAD_FAIL.getErrorMeta().getException();
}
}
}
} catch (IOException e) {
logger.error("验证上传文件MD5错误", e);
throw ErrorEnum.ISV_UPLOAD_FAIL.getErrorMeta().getException();
}
}
}
protected void checkTimeout(ApiParam param) {
int timeoutSeconds = ApiContext.getApiConfig().getTimeoutSeconds();
// 如果设置为0,表示不校验
if (timeoutSeconds == 0) {
return;
}
if (timeoutSeconds < 0) {
throw new IllegalArgumentException("服务端timeoutSeconds设置错误");
}
String requestTime = param.fetchTimestamp();
try {
Date requestDate = new SimpleDateFormat(ParamNames.TIMESTAMP_PATTERN).parse(requestTime);
long requestMilliseconds = requestDate.getTime();
if (System.currentTimeMillis() - requestMilliseconds > timeoutSeconds * MILLISECOND_OF_ONE_SECOND) {
throw ErrorEnum.ISV_INVALID_TIMESTAMP.getErrorMeta().getException();
}
} catch (ParseException e) {
throw ErrorEnum.ISV_INVALID_TIMESTAMP.getErrorMeta().getException(param.fetchNameVersion());
}
}
protected void checkAppKey(ApiParam param) {
if (StringUtils.isEmpty(param.fetchAppKey())) {
throw ErrorEnum.ISV_MISSING_APP_ID.getErrorMeta().getException();
}
AppSecretManager appSecretManager = ApiContext.getApiConfig().getAppSecretManager();
Assert.notNull(appSecretManager, "appSecretManager未初始化");
boolean isTrueAppKey = appSecretManager.isValidAppKey(param.fetchAppKey());
if (!isTrueAppKey) {
throw ErrorEnum.ISV_INVALID_APP_ID.getErrorMeta().getException();
}
}
protected void checkSign(ApiParam param) {
String clientSign = param.fetchSign();
try {
if (StringUtils.isEmpty(param.fetchSign())) {
throw ErrorEnum.ISV_MISSING_SIGNATURE.getErrorMeta().getException(param.fetchNameVersion(), ParamNames.SIGN_NAME);
}
String secret = ApiContext.getApiConfig().getAppSecretManager().getSecret(param.fetchAppKey());
if (StringUtils.isEmpty(secret)) {
throw ErrorEnum.ISV_MISSING_SIGNATURE_CONFIG.getErrorMeta().getException();
}
Signer signer = ApiContext.getApiConfig().getSigner();
boolean isRightSign = signer.checkSign(ApiContext.getRequest(), secret);
// 错误的sign
if (!isRightSign) {
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(param.fetchNameVersion());
}
} finally {
// 校验过程中会移除sign,这里需要重新设置进去
param.setSign(clientSign);
}
}
protected void checkFormat(ApiParam param) {
String format = param.fetchFormat();
boolean contains = FORMAT_LIST.contains(format.toLowerCase());
if (!contains) {
throw ErrorEnum.ISV_INVALID_FORMAT.getErrorMeta().getException(param.fetchNameVersion(), format);
}
}
}

@ -0,0 +1,95 @@
package com.gitee.sop.gatewaycommon.validate;
/**
* 负责加解密
*
* @author tanghc
*/
public interface Encrypter {
/**
* AES文本加密
*
* @param content 明文
* @param password 密码
* @return 返回16进制内容
* @throws Exception
*/
String aesEncryptToHex(String content, String password) throws Exception;
/**
* AES文本解密
*
* @param hex 待解密文本,16进制内容
* @param password 密码
* @return 返回明文
* @throws Exception
*/
String aesDecryptFromHex(String hex, String password) throws Exception;
/**
* AES文本加密
*
* @param content 明文
* @param password 密码
* @return 返回base64内容
* @throws Exception
*/
String aesEncryptToBase64String(String content, String password) throws Exception;
/**
* AES文本解密
*
* @param base64String 待解密文本,16进制内容
* @param password 密码
* @return 返回明文
* @throws Exception
*/
String aesDecryptFromBase64String(String base64String, String password) throws Exception;
/**
* RSA私钥解密
*
* @param data 解密内容
* @param privateKey 私钥
* @return 返回明文
* @throws Exception
*/
String rsaDecryptByPrivateKey(String data, String privateKey) throws Exception;
/**
* 新版rsa私钥解密
* @param data 解密内容
* @param privateKey 私钥
* @return 返回明文
* @throws Exception
*/
String rsaDecryptByPrivateKeyNew(String data, String privateKey) throws Exception;
/**
* RSA私钥加密
*
* @param data 明文
* @param privateKey 私钥
* @return 返回密文
* @throws Exception
*/
String rsaEncryptByPrivateKey(String data, String privateKey) throws Exception;
/**
* 新版rsa私钥加密
* @param data 明文
* @param privateKey 私钥
* @return 返回密文
* @throws Exception
*/
String rsaEncryptByPrivateKeyNew(String data, String privateKey) throws Exception;
/**
* md5加密,全部小写
*
* @param value
* @return 返回md5内容
*/
String md5(String value);
}

@ -0,0 +1,13 @@
package com.gitee.sop.gatewaycommon.validate;
/**
* @author tanghc
*/
public interface SignEncipher {
/**
* 签名的摘要算法
* @param input 待签名数据
* @return 返回加密后的数据
*/
byte[] encrypt(String input, String secret);
}

@ -0,0 +1,37 @@
package com.gitee.sop.gatewaycommon.validate;
import com.gitee.sop.gatewaycommon.bean.SopConstants;
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
import lombok.extern.slf4j.Slf4j;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
/**
* HMAC_MD5加密
* @author tanghc
*/
@Slf4j
public class SignEncipherHMAC_MD5 implements SignEncipher {
public static final String HMAC_MD5 = "HmacMD5";
@Override
public byte[] encrypt(String input, String secret) {
try {
SecretKey secretKey = new SecretKeySpec(secret.getBytes(SopConstants.CHARSET_UTF8), HMAC_MD5);
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
mac.init(secretKey);
return mac.doFinal(input.getBytes(SopConstants.CHARSET_UTF8));
} catch (NoSuchAlgorithmException e) {
log.error("HMAC_MD5加密加密失败NoSuchAlgorithmException", e);
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException();
} catch (InvalidKeyException e) {
log.error("HMAC_MD5加密加密失败InvalidKeyException", e);
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException();
}
}
}

@ -0,0 +1,16 @@
package com.gitee.sop.gatewaycommon.validate;
import org.apache.commons.codec.digest.DigestUtils;
import java.nio.charset.StandardCharsets;
/**
* @author tanghc
*/
public class SignEncipherMD5 implements SignEncipher {
@Override
public byte[] encrypt(String input, String secret) {
String source = secret + input + secret;
return DigestUtils.md5(source.getBytes(StandardCharsets.UTF_8));
}
}

@ -0,0 +1,20 @@
package com.gitee.sop.gatewaycommon.validate;
import javax.servlet.http.HttpServletRequest;
/**
* 负责签名校验
* @author tanghc
*
*/
public interface Signer {
/**
* 签名校验
* @param request
* @param secret 秘钥
* @return true签名正确
*/
boolean checkSign(HttpServletRequest request, String secret);
}

@ -0,0 +1,18 @@
package com.gitee.sop.gatewaycommon.validate;
import com.gitee.sop.gatewaycommon.param.ApiParam;
/**
* 校验接口
*
* @author tanghc
*
*/
public interface Validator {
/**
* 接口验证
* @param param 接口参数
*/
void validate(ApiParam param);
}

@ -0,0 +1,97 @@
/**
* Alipay.com Inc.
* Copyright (c) 2004-2012 All Rights Reserved.
*/
package com.gitee.sop.gatewaycommon.validate.alipay;
/**
*
* @author runzhi
*/
public class AlipayConstants {
public static final String SIGN_TYPE = "sign_type";
public static final String SIGN_TYPE_RSA = "RSA";
/**
* sha256WithRsa 算法请求类型
*/
public static final String SIGN_TYPE_RSA2 = "RSA2";
public static final String SIGN_ALGORITHMS = "SHA1WithRSA";
public static final String SIGN_SHA256RSA_ALGORITHMS = "SHA256WithRSA";
public static final String ENCRYPT_TYPE_AES = "AES";
public static final String APP_ID = "app_id";
public static final String FORMAT = "format";
public static final String METHOD = "method";
public static final String TIMESTAMP = "timestamp";
public static final String VERSION = "version";
public static final String SIGN = "sign";
public static final String ALIPAY_SDK = "alipay_sdk";
public static final String ACCESS_TOKEN = "auth_token";
public static final String APP_AUTH_TOKEN = "app_auth_token";
public static final String TERMINAL_TYPE = "terminal_type";
public static final String TERMINAL_INFO = "terminal_info";
public static final String CHARSET = "charset";
public static final String NOTIFY_URL = "notify_url";
public static final String RETURN_URL = "return_url";
public static final String ENCRYPT_TYPE = "encrypt_type";
//-----===-------///
public static final String BIZ_CONTENT_KEY = "biz_content";
/** 默认时间格式 **/
public static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
/** Date默认时区 **/
public static final String DATE_TIMEZONE = "GMT+8";
/** UTF-8字符集 **/
public static final String CHARSET_UTF8 = "UTF-8";
/** GBK字符集 **/
public static final String CHARSET_GBK = "GBK";
/** JSON 应格式 */
public static final String FORMAT_JSON = "json";
/** XML 应格式 */
public static final String FORMAT_XML = "xml";
/** SDK版本号 */
public static final String SDK_VERSION = "alipay-sdk-java-3.6.0.ALL";
public static final String PROD_CODE = "prod_code";
/** 老版本失败节点 */
public static final String ERROR_RESPONSE = "error_response";
/** 新版本节点后缀 */
public static final String RESPONSE_SUFFIX = "_response";
/** 加密后XML返回报文的节点名字 */
public static final String RESPONSE_XML_ENCRYPT_NODE_NAME = "response_encrypted";
/** 批量请求id **/
public static final String BATCH_REQUEST_ID = "batch_request_id";
}

@ -0,0 +1,616 @@
/**
* Alipay.com Inc.
* Copyright (c) 2004-2012 All Rights Reserved.
*/
package com.gitee.sop.gatewaycommon.validate.alipay;
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* @author runzhi
*/
public class AlipaySignature {
/**
* RSA最大加密明文大小
*/
private static final int MAX_ENCRYPT_BLOCK = 117;
/**
* RSA最大解密密文大小
*/
private static final int MAX_DECRYPT_BLOCK = 128;
/**
* @param sortedParams
* @return
*/
public static String getSignContent(Map<String, Object> sortedParams) {
StringBuffer content = new StringBuffer();
List<String> keys = new ArrayList<String>(sortedParams.keySet());
Collections.sort(keys);
int index = 0;
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
String value = String.valueOf(sortedParams.get(key));
if (StringUtils.areNotEmpty(key, value)) {
content.append((index == 0 ? "" : "&") + key + "=" + value);
index++;
}
}
return content.toString();
}
/**
* rsa内容签名
*
* @param content
* @param publicKey
* @param charset
* @return
*/
public static String rsaSign(String content, String publicKey, String charset,
String signType) {
if (AlipayConstants.SIGN_TYPE_RSA.equals(signType)) {
return rsaSign(content, publicKey, charset);
} else if (AlipayConstants.SIGN_TYPE_RSA2.equals(signType)) {
return rsa256Sign(content, publicKey, charset);
} else {
throw ErrorEnum.ISV_INVALID_SIGNATURE_TYPE.getErrorMeta().getException();
// throw new AlipayApiException("Sign Type is Not Support : signType=" + signType);
}
}
/**
* sha256WithRsa 加签
*
* @param content
* @param publicKey
* @param charset
* @return
*/
public static String rsa256Sign(String content, String publicKey,
String charset) {
try {
PrivateKey priKey = getPrivateKeyFromPKCS8(AlipayConstants.SIGN_TYPE_RSA,
new ByteArrayInputStream(publicKey.getBytes()));
java.security.Signature signature = java.security.Signature
.getInstance(AlipayConstants.SIGN_SHA256RSA_ALGORITHMS);
signature.initSign(priKey);
if (StringUtils.isEmpty(charset)) {
signature.update(content.getBytes());
} else {
signature.update(content.getBytes(charset));
}
byte[] signed = signature.sign();
return new String(Base64.encodeBase64(signed));
} catch (Exception e) {
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(e);
// throw new AlipayApiException("RSAcontent = " + content + "; charset = " + charset, e);
}
}
/**
* sha1WithRsa 加签
*
* @param content
* @param publicKey
* @param charset
* @return
*/
public static String rsaSign(String content, String publicKey,
String charset) {
try {
PrivateKey priKey = getPrivateKeyFromPKCS8(AlipayConstants.SIGN_TYPE_RSA,
new ByteArrayInputStream(publicKey.getBytes()));
java.security.Signature signature = java.security.Signature
.getInstance(AlipayConstants.SIGN_ALGORITHMS);
signature.initSign(priKey);
if (StringUtils.isEmpty(charset)) {
signature.update(content.getBytes());
} else {
signature.update(content.getBytes(charset));
}
byte[] signed = signature.sign();
return new String(Base64.encodeBase64(signed));
} catch (InvalidKeySpecException ie) {
throw ErrorEnum.ISV_INVALID_SIGNATURE_TYPE.getErrorMeta().getException(ie);
// throw new AlipayApiException("RSA私钥格式不正确,请检查是否正确配置了PKCS8格式的私钥", ie);
} catch (Exception e) {
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(e);
// throw new AlipayApiException("RSAcontent = " + content + "; charset = " + charset, e);
}
}
public static String rsaSign(Map<String, Object> params, String publicKey,
String charset, String signType) {
String signContent = getSignContent(params);
return rsaSign(signContent, publicKey, charset, signType);
}
public static PrivateKey getPrivateKeyFromPKCS8(String algorithm,
InputStream ins) throws Exception {
if (ins == null || StringUtils.isEmpty(algorithm)) {
return null;
}
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
byte[] encodedKey = StreamUtil.readText(ins, "UTF-8").getBytes();
encodedKey = Base64.decodeBase64(encodedKey);
return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodedKey));
}
public static String getSignCheckContentV1(Map<String, String> params) {
if (params == null) {
return null;
}
params.remove("sign");
params.remove("sign_type");
StringBuffer content = new StringBuffer();
List<String> keys = new ArrayList<String>(params.keySet());
Collections.sort(keys);
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
String value = params.get(key);
content.append((i == 0 ? "" : "&") + key + "=" + value);
}
return content.toString();
}
public static String getSignCheckContentV2(Map<String, ?> params) {
if (params == null) {
return null;
}
params.remove("sign");
StringBuffer content = new StringBuffer();
List<String> keys = new ArrayList<String>(params.keySet());
Collections.sort(keys);
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
String value = String.valueOf(params.get(key));
content.append((i == 0 ? "" : "&") + key + "=" + value);
}
return content.toString();
}
public static boolean rsaCheckV1(Map<String, String> params, String publicKey,
String charset) {
String sign = params.get("sign");
String content = getSignCheckContentV1(params);
return rsaCheckContent(content, sign, publicKey, charset);
}
public static boolean rsaCheckV1(Map<String, String> params, String publicKey,
String charset, String signType) {
String sign = params.get("sign");
String content = getSignCheckContentV1(params);
return rsaCheck(content, sign, publicKey, charset, signType);
}
public static boolean rsaCheckV2(Map<String, String> params, String publicKey,
String charset) {
String sign = params.get("sign");
String content = getSignCheckContentV2(params);
return rsaCheckContent(content, sign, publicKey, charset);
}
public static boolean rsaCheckV2(Map<String, ?> params, String publicKey,
String charset, String signType) {
String sign = String.valueOf(params.get("sign"));
String content = getSignCheckContentV2(params);
return rsaCheck(content, sign, publicKey, charset, signType);
}
public static boolean rsaCheck(String content, String sign, String publicKey, String charset,
String signType) {
if (AlipayConstants.SIGN_TYPE_RSA.equals(signType)) {
return rsaCheckContent(content, sign, publicKey, charset);
} else if (AlipayConstants.SIGN_TYPE_RSA2.equals(signType)) {
return rsa256CheckContent(content, sign, publicKey, charset);
} else {
throw ErrorEnum.ISV_INVALID_SIGNATURE_TYPE.getErrorMeta().getException();
// throw new AlipayApiException("Sign Type is Not Support : signType=" + signType);
}
}
public static boolean rsa256CheckContent(String content, String sign, String publicKey,
String charset) {
try {
PublicKey pubKey = getPublicKeyFromX509("RSA",
new ByteArrayInputStream(publicKey.getBytes()));
java.security.Signature signature = java.security.Signature
.getInstance(AlipayConstants.SIGN_SHA256RSA_ALGORITHMS);
signature.initVerify(pubKey);
if (StringUtils.isEmpty(charset)) {
signature.update(content.getBytes());
} else {
signature.update(content.getBytes(charset));
}
return signature.verify(Base64.decodeBase64(sign.getBytes()));
} catch (Exception e) {
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(e);
// throw new AlipayApiException(
// "RSAcontent = " + content + ",sign=" + sign + ",charset = " + charset, e);
}
}
public static boolean rsaCheckContent(String content, String sign, String publicKey,
String charset) {
try {
PublicKey pubKey = getPublicKeyFromX509("RSA",
new ByteArrayInputStream(publicKey.getBytes()));
java.security.Signature signature = java.security.Signature
.getInstance(AlipayConstants.SIGN_ALGORITHMS);
signature.initVerify(pubKey);
if (StringUtils.isEmpty(charset)) {
signature.update(content.getBytes());
} else {
signature.update(content.getBytes(charset));
}
return signature.verify(Base64.decodeBase64(sign.getBytes()));
} catch (Exception e) {
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(e);
// throw new AlipayApiException(
// "RSAcontent = " + content + ",sign=" + sign + ",charset = " + charset, e);
}
}
public static PublicKey getPublicKeyFromX509(String algorithm,
InputStream ins) throws Exception {
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
StringWriter writer = new StringWriter();
StreamUtil.io(new InputStreamReader(ins), writer);
byte[] encodedKey = writer.toString().getBytes();
encodedKey = Base64.decodeBase64(encodedKey);
return keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
}
/**
* 验签并解密
* <p>
* <b>目前适用于公众号</b><br>
* params参数示例
* <br>{
* <br>biz_content=M0qGiGz+8kIpxe8aF4geWJdBn0aBTuJRQItLHo9R7o5JGhpic/MIUjvXo2BLB++BbkSq2OsJCEQFDZ0zK5AJYwvBgeRX30gvEj6eXqXRt16/IkB9HzAccEqKmRHrZJ7PjQWE0KfvDAHsJqFIeMvEYk1Zei2QkwSQPlso7K0oheo/iT+HYE8aTATnkqD/ByD9iNDtGg38pCa2xnnns63abKsKoV8h0DfHWgPH62urGY7Pye3r9FCOXA2Ykm8X4/Bl1bWFN/PFCEJHWe/HXj8KJKjWMO6ttsoV0xRGfeyUO8agu6t587Dl5ux5zD/s8Lbg5QXygaOwo3Fz1G8EqmGhi4+soEIQb8DBYanQOS3X+m46tVqBGMw8Oe+hsyIMpsjwF4HaPKMr37zpW3fe7xOMuimbZ0wq53YP/jhQv6XWodjT3mL0H5ACqcsSn727B5ztquzCPiwrqyjUHjJQQefFTzOse8snaWNQTUsQS7aLsHq0FveGpSBYORyA90qPdiTjXIkVP7mAiYiAIWW9pCEC7F3XtViKTZ8FRMM9ySicfuAlf3jtap6v2KPMtQv70X+hlmzO/IXB6W0Ep8DovkF5rB4r/BJYJLw/6AS0LZM9w5JfnAZhfGM2rKzpfNsgpOgEZS1WleG4I2hoQC0nxg9IcP0Hs+nWIPkEUcYNaiXqeBc=,
* <br>sign=rlqgA8O+RzHBVYLyHmrbODVSANWPXf3pSrr82OCO/bm3upZiXSYrX5fZr6UBmG6BZRAydEyTIguEW6VRuAKjnaO/sOiR9BsSrOdXbD5Rhos/Xt7/mGUWbTOt/F+3W0/XLuDNmuYg1yIC/6hzkg44kgtdSTsQbOC9gWM7ayB4J4c=,
* sign_type=RSA,
* <br>charset=UTF-8
* <br>}
* </p>
*
* @param params
* @param alipayPublicKey 支付宝公钥
* @param cusPrivateKey 商户私钥
* @param isCheckSign 是否验签
* @param isDecrypt 是否解密
* @return 解密后明文验签失败则异常抛出
*/
public static String checkSignAndDecrypt(Map<String, String> params, String alipayPublicKey,
String cusPrivateKey, boolean isCheckSign,
boolean isDecrypt) {
String charset = params.get("charset");
String bizContent = params.get("biz_content");
if (isCheckSign) {
if (!rsaCheckV2(params, alipayPublicKey, charset)) {
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException();
// throw new AlipayApiException("rsaCheck failure:rsaParams=" + params);
}
}
if (isDecrypt) {
return rsaDecrypt(bizContent, cusPrivateKey, charset);
}
return bizContent;
}
/**
* 验签并解密
* <p>
* <b>目前适用于公众号</b><br>
* params参数示例
* <br>{
* <br>biz_content=M0qGiGz+8kIpxe8aF4geWJdBn0aBTuJRQItLHo9R7o5JGhpic/MIUjvXo2BLB++BbkSq2OsJCEQFDZ0zK5AJYwvBgeRX30gvEj6eXqXRt16/IkB9HzAccEqKmRHrZJ7PjQWE0KfvDAHsJqFIeMvEYk1Zei2QkwSQPlso7K0oheo/iT+HYE8aTATnkqD/ByD9iNDtGg38pCa2xnnns63abKsKoV8h0DfHWgPH62urGY7Pye3r9FCOXA2Ykm8X4/Bl1bWFN/PFCEJHWe/HXj8KJKjWMO6ttsoV0xRGfeyUO8agu6t587Dl5ux5zD/s8Lbg5QXygaOwo3Fz1G8EqmGhi4+soEIQb8DBYanQOS3X+m46tVqBGMw8Oe+hsyIMpsjwF4HaPKMr37zpW3fe7xOMuimbZ0wq53YP/jhQv6XWodjT3mL0H5ACqcsSn727B5ztquzCPiwrqyjUHjJQQefFTzOse8snaWNQTUsQS7aLsHq0FveGpSBYORyA90qPdiTjXIkVP7mAiYiAIWW9pCEC7F3XtViKTZ8FRMM9ySicfuAlf3jtap6v2KPMtQv70X+hlmzO/IXB6W0Ep8DovkF5rB4r/BJYJLw/6AS0LZM9w5JfnAZhfGM2rKzpfNsgpOgEZS1WleG4I2hoQC0nxg9IcP0Hs+nWIPkEUcYNaiXqeBc=,
* <br>sign=rlqgA8O+RzHBVYLyHmrbODVSANWPXf3pSrr82OCO/bm3upZiXSYrX5fZr6UBmG6BZRAydEyTIguEW6VRuAKjnaO/sOiR9BsSrOdXbD5Rhos/Xt7/mGUWbTOt/F+3W0/XLuDNmuYg1yIC/6hzkg44kgtdSTsQbOC9gWM7ayB4J4c=,
* sign_type=RSA,
* <br>charset=UTF-8
* <br>}
* </p>
*
* @param params
* @param alipayPublicKey 支付宝公钥
* @param cusPrivateKey 商户私钥
* @param isCheckSign 是否验签
* @param isDecrypt 是否解密
* @return 解密后明文验签失败则异常抛出
*/
public static String checkSignAndDecrypt(Map<String, String> params, String alipayPublicKey,
String cusPrivateKey, boolean isCheckSign,
boolean isDecrypt, String signType) {
String charset = params.get("charset");
String bizContent = params.get("biz_content");
if (isCheckSign) {
if (!rsaCheckV2(params, alipayPublicKey, charset, signType)) {
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException();
// throw new AlipayApiException("rsaCheck failure:rsaParams=" + params);
}
}
if (isDecrypt) {
return rsaDecrypt(bizContent, cusPrivateKey, charset);
}
return bizContent;
}
/**
* 加密并签名<br>
* <b>目前适用于公众号</b>
*
* @param bizContent 待加密签名内容
* @param alipayPublicKey 支付宝公钥
* @param cusPrivateKey 商户私钥
* @param charset 字符集如UTF-8, GBK, GB2312
* @param isEncrypt 是否加密true-加密 false-不加密
* @param isSign 是否签名true-签名 false-不签名
* @return 加密签名后xml内容字符串
* <p>
* 返回示例
* <alipay>
* <response>密文</response>
* <encryption_type>RSA</encryption_type>
* <sign>sign</sign>
* <sign_type>RSA</sign_type>
* </alipay>
* </p>
*/
public static String encryptAndSign(String bizContent, String alipayPublicKey,
String cusPrivateKey, String charset, boolean isEncrypt,
boolean isSign) {
StringBuilder sb = new StringBuilder();
if (StringUtils.isEmpty(charset)) {
charset = AlipayConstants.CHARSET_GBK;
}
sb.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>");
if (isEncrypt) {// 加密
sb.append("<alipay>");
String encrypted = rsaEncrypt(bizContent, alipayPublicKey, charset);
sb.append("<response>" + encrypted + "</response>");
sb.append("<encryption_type>RSA</encryption_type>");
if (isSign) {
String sign = rsaSign(encrypted, cusPrivateKey, charset);
sb.append("<sign>" + sign + "</sign>");
sb.append("<sign_type>RSA</sign_type>");
}
sb.append("</alipay>");
} else if (isSign) {// 不加密,但需要签名
sb.append("<alipay>");
sb.append("<response>" + bizContent + "</response>");
String sign = rsaSign(bizContent, cusPrivateKey, charset);
sb.append("<sign>" + sign + "</sign>");
sb.append("<sign_type>RSA</sign_type>");
sb.append("</alipay>");
} else {// 不加密,不加签
sb.append(bizContent);
}
return sb.toString();
}
/**
* 加密并签名<br>
* <b>目前适用于公众号</b>
*
* @param bizContent 待加密签名内容
* @param alipayPublicKey 支付宝公钥
* @param cusPrivateKey 商户私钥
* @param charset 字符集如UTF-8, GBK, GB2312
* @param isEncrypt 是否加密true-加密 false-不加密
* @param isSign 是否签名true-签名 false-不签名
* @return 加密签名后xml内容字符串
* <p>
* 返回示例
* <alipay>
* <response>密文</response>
* <encryption_type>RSA</encryption_type>
* <sign>sign</sign>
* <sign_type>RSA</sign_type>
* </alipay>
* </p>
*/
public static String encryptAndSign(String bizContent, String alipayPublicKey,
String cusPrivateKey, String charset, boolean isEncrypt,
boolean isSign, String signType) {
StringBuilder sb = new StringBuilder();
if (StringUtils.isEmpty(charset)) {
charset = AlipayConstants.CHARSET_GBK;
}
sb.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>");
if (isEncrypt) {// 加密
sb.append("<alipay>");
String encrypted = rsaEncrypt(bizContent, alipayPublicKey, charset);
sb.append("<response>" + encrypted + "</response>");
sb.append("<encryption_type>RSA</encryption_type>");
if (isSign) {
String sign = rsaSign(encrypted, cusPrivateKey, charset, signType);
sb.append("<sign>" + sign + "</sign>");
sb.append("<sign_type>");
sb.append(signType);
sb.append("</sign_type>");
}
sb.append("</alipay>");
} else if (isSign) {// 不加密,但需要签名
sb.append("<alipay>");
sb.append("<response>" + bizContent + "</response>");
String sign = rsaSign(bizContent, cusPrivateKey, charset, signType);
sb.append("<sign>" + sign + "</sign>");
sb.append("<sign_type>");
sb.append(signType);
sb.append("</sign_type>");
sb.append("</alipay>");
} else {// 不加密,不加签
sb.append(bizContent);
}
return sb.toString();
}
/**
* 公钥加密
*
* @param content 待加密内容
* @param publicKey 公钥
* @param charset 字符集如UTF-8, GBK, GB2312
* @return 密文内容
*/
public static String rsaEncrypt(String content, String publicKey,
String charset) {
try {
PublicKey pubKey = getPublicKeyFromX509(AlipayConstants.SIGN_TYPE_RSA,
new ByteArrayInputStream(publicKey.getBytes()));
Cipher cipher = Cipher.getInstance(AlipayConstants.SIGN_TYPE_RSA);
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] data = StringUtils.isEmpty(charset) ? content.getBytes()
: content.getBytes(charset);
int inputLen = data.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
// 对数据分段加密
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
} else {
cache = cipher.doFinal(data, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_ENCRYPT_BLOCK;
}
byte[] encryptedData = Base64.encodeBase64(out.toByteArray());
out.close();
return StringUtils.isEmpty(charset) ? new String(encryptedData)
: new String(encryptedData, charset);
} catch (Exception e) {
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(e);
// throw new AlipayApiException("EncryptContent = " + content + ",charset = " + charset,
// e);
}
}
/**
* 私钥解密
*
* @param content 待解密内容
* @param publicKey 私钥
* @param charset 字符集如UTF-8, GBK, GB2312
* @return 明文内容
*/
public static String rsaDecrypt(String content, String publicKey,
String charset) {
try {
PrivateKey priKey = getPrivateKeyFromPKCS8(AlipayConstants.SIGN_TYPE_RSA,
new ByteArrayInputStream(publicKey.getBytes()));
Cipher cipher = Cipher.getInstance(AlipayConstants.SIGN_TYPE_RSA);
cipher.init(Cipher.DECRYPT_MODE, priKey);
byte[] encryptedData = StringUtils.isEmpty(charset)
? Base64.decodeBase64(content.getBytes())
: Base64.decodeBase64(content.getBytes(charset));
int inputLen = encryptedData.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
// 对数据分段解密
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
} else {
cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_DECRYPT_BLOCK;
}
byte[] decryptedData = out.toByteArray();
out.close();
return StringUtils.isEmpty(charset) ? new String(decryptedData)
: new String(decryptedData, charset);
} catch (Exception e) {
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(e);
// throw new AlipayApiException("EncodeContent = " + content + ",charset = " + charset, e);
}
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save