parent
							
								
									85d61d2f1e
								
							
						
					
					
						commit
						46a546f1da
					
				| @ -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 | ||||
|  | ||||
| @ -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,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/ | ||||
									
										Binary file not shown.
									
								
							
						| @ -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 | ||||
|  *     <serviceId>:{ 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); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 通过ErrorMeta,Locale,params构建国际化错误消息 | ||||
|      * | ||||
|      * @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; | ||||
| 
 | ||||
| /** | ||||
|  * appkey,secret默认管理,简单放在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; | ||||
| 
 | ||||
| /** | ||||
|  * appkey,secret文件管理,功能同CacheAppSecretManager,这个是将appKey,secret放在属性文件中<br> | ||||
|  * key为appKey,value为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…
					
					
				
		Reference in new issue