mirror of
https://gitee.com/durcframework/SOP.git
synced 2025-08-11 12:56:28 +08:00
init
This commit is contained in:
42
.gitignore
vendored
42
.gitignore
vendored
@@ -1,23 +1,25 @@
|
|||||||
# Compiled class file
|
/target/
|
||||||
*.class
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
|
||||||
# Log file
|
### STS ###
|
||||||
*.log
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
# BlueJ files
|
### IntelliJ IDEA ###
|
||||||
*.ctxt
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
# Mobile Tools for Java (J2ME)
|
### NetBeans ###
|
||||||
.mtj.tmp/
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
# Package Files #
|
/dist/
|
||||||
*.jar
|
/nbdist/
|
||||||
*.war
|
/.nb-gradle/
|
||||||
*.nar
|
/build/
|
||||||
*.ear
|
|
||||||
*.zip
|
|
||||||
*.tar.gz
|
|
||||||
*.rar
|
|
||||||
|
|
||||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
|
||||||
hs_err_pid*
|
|
||||||
|
62
README.md
62
README.md
@@ -1,37 +1,45 @@
|
|||||||
# SOP
|
# SOP(Simple Open Platform)
|
||||||
|
|
||||||
#### 介绍
|
一个开放平台解决方案项目,基于Spring Cloud实现,目标是能够让用户快速得搭建起自己的开放平台。
|
||||||
一个简单易用的接口开放平台(Simple Open Platform),可实现类似于淘宝开放平台的调用方式。
|
|
||||||
|
|
||||||
#### 软件架构
|
SOP提供了两种接口调用方式,分别是:[支付宝开放平台](https://docs.open.alipay.com/api)的调用方式和[淘宝开放平台](http://open.taobao.com/api.htm?docId=285&docType=2)的调用方式。
|
||||||
软件架构说明
|
通过简单的配置后,你的项目就具备了和支付宝开放平台的一样的接口提供能力。
|
||||||
|
|
||||||
|
SOP封装了开放平台大部分功能包括:签名验证、统一异常处理、统一返回内容 、业务参数验证(JSR-303)、秘钥管理等,未来还会实现更多功能。
|
||||||
|
|
||||||
|
## 项目特点
|
||||||
|
|
||||||
|
- 接入方式简单,与老项目不冲突,老项目注册到注册中心,然后的方法上加上注解即可。
|
||||||
|
- 架构松耦合,业务代码实现在各自微服务上,SOP不参与业务实现,这也是Spring Cloud微服务体系带来的好处。
|
||||||
|
- 扩展简单,开放平台对应的功能各自独立,可以自定义实现自己的需求,如:更改参数,更改签名规则等。
|
||||||
|
|
||||||
|
## 谁能使用这个项目
|
||||||
|
|
||||||
|
- 有现成的项目,想改造成开放平台供他人调用
|
||||||
|
- 有现成的项目,想暴露其中几个接口并通过开放平台供他人调用
|
||||||
|
- 想搭一个开放平台新项目,并结合微服务的方式去维护
|
||||||
|
- 对开放平台感兴趣的朋友
|
||||||
|
|
||||||
|
以上情况都可以考虑使用SOP
|
||||||
|
|
||||||
|
## 架构图
|
||||||
|
|
||||||
|
|
||||||
#### 安装教程
|
|
||||||
|
|
||||||
1. xxxx
|
## 工程说明
|
||||||
2. xxxx
|
|
||||||
3. xxxx
|
|
||||||
|
|
||||||
#### 使用说明
|
- sop-registry:注册中心,eureka实现
|
||||||
|
- sop-gateway:网关,zuul实现,统一访问入口
|
||||||
|
- sop-gateway-common:网关公共模块,封装常用功能,包含签名校验等功能。
|
||||||
|
- sop-server-common:微服务端公共模块,封装配套功能
|
||||||
|
- sop-story:示例,story服务,同时作为Provider提供服务
|
||||||
|
- sop-book:示例,book服务,也是Consumer,调用story提供的服务
|
||||||
|
- sop-test:测试用例
|
||||||
|
|
||||||
1. xxxx
|
## 相关文档
|
||||||
2. xxxx
|
|
||||||
3. xxxx
|
|
||||||
|
|
||||||
#### 参与贡献
|
|
||||||
|
|
||||||
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) 这个地址来了解码云上的优秀开源项目
|
Q群:328419269
|
||||||
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/)
|
|
||||||
|
286
mvnw
vendored
Executable file
286
mvnw
vendored
Executable file
@@ -0,0 +1,286 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Maven2 Start Up Batch script
|
||||||
|
#
|
||||||
|
# Required ENV vars:
|
||||||
|
# ------------------
|
||||||
|
# JAVA_HOME - location of a JDK home dir
|
||||||
|
#
|
||||||
|
# Optional ENV vars
|
||||||
|
# -----------------
|
||||||
|
# M2_HOME - location of maven2's installed home dir
|
||||||
|
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||||
|
# e.g. to debug Maven itself, use
|
||||||
|
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||||
|
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
if [ -z "$MAVEN_SKIP_RC" ] ; then
|
||||||
|
|
||||||
|
if [ -f /etc/mavenrc ] ; then
|
||||||
|
. /etc/mavenrc
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "$HOME/.mavenrc" ] ; then
|
||||||
|
. "$HOME/.mavenrc"
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
# OS specific support. $var _must_ be set to either true or false.
|
||||||
|
cygwin=false;
|
||||||
|
darwin=false;
|
||||||
|
mingw=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN*) cygwin=true ;;
|
||||||
|
MINGW*) mingw=true;;
|
||||||
|
Darwin*) darwin=true
|
||||||
|
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
|
||||||
|
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
|
||||||
|
if [ -z "$JAVA_HOME" ]; then
|
||||||
|
if [ -x "/usr/libexec/java_home" ]; then
|
||||||
|
export JAVA_HOME="`/usr/libexec/java_home`"
|
||||||
|
else
|
||||||
|
export JAVA_HOME="/Library/Java/Home"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ] ; then
|
||||||
|
if [ -r /etc/gentoo-release ] ; then
|
||||||
|
JAVA_HOME=`java-config --jre-home`
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$M2_HOME" ] ; then
|
||||||
|
## resolve links - $0 may be a link to maven's home
|
||||||
|
PRG="$0"
|
||||||
|
|
||||||
|
# need this for relative symlinks
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG="`dirname "$PRG"`/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
saveddir=`pwd`
|
||||||
|
|
||||||
|
M2_HOME=`dirname "$PRG"`/..
|
||||||
|
|
||||||
|
# make it fully qualified
|
||||||
|
M2_HOME=`cd "$M2_HOME" && pwd`
|
||||||
|
|
||||||
|
cd "$saveddir"
|
||||||
|
# echo Using m2 at $M2_HOME
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
||||||
|
if $cygwin ; then
|
||||||
|
[ -n "$M2_HOME" ] &&
|
||||||
|
M2_HOME=`cygpath --unix "$M2_HOME"`
|
||||||
|
[ -n "$JAVA_HOME" ] &&
|
||||||
|
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||||
|
[ -n "$CLASSPATH" ] &&
|
||||||
|
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Mingw, ensure paths are in UNIX format before anything is touched
|
||||||
|
if $mingw ; then
|
||||||
|
[ -n "$M2_HOME" ] &&
|
||||||
|
M2_HOME="`(cd "$M2_HOME"; pwd)`"
|
||||||
|
[ -n "$JAVA_HOME" ] &&
|
||||||
|
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
|
||||||
|
# TODO classpath?
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ]; then
|
||||||
|
javaExecutable="`which javac`"
|
||||||
|
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
|
||||||
|
# readlink(1) is not available as standard on Solaris 10.
|
||||||
|
readLink=`which readlink`
|
||||||
|
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
|
||||||
|
if $darwin ; then
|
||||||
|
javaHome="`dirname \"$javaExecutable\"`"
|
||||||
|
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
|
||||||
|
else
|
||||||
|
javaExecutable="`readlink -f \"$javaExecutable\"`"
|
||||||
|
fi
|
||||||
|
javaHome="`dirname \"$javaExecutable\"`"
|
||||||
|
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
|
||||||
|
JAVA_HOME="$javaHome"
|
||||||
|
export JAVA_HOME
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAVACMD" ] ; then
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="`which java`"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
echo "Error: JAVA_HOME is not defined correctly." >&2
|
||||||
|
echo " We cannot execute $JAVACMD" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ] ; then
|
||||||
|
echo "Warning: JAVA_HOME environment variable is not set."
|
||||||
|
fi
|
||||||
|
|
||||||
|
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
|
||||||
|
|
||||||
|
# traverses directory structure from process work directory to filesystem root
|
||||||
|
# first directory with .mvn subdirectory is considered project base directory
|
||||||
|
find_maven_basedir() {
|
||||||
|
|
||||||
|
if [ -z "$1" ]
|
||||||
|
then
|
||||||
|
echo "Path not specified to find_maven_basedir"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
basedir="$1"
|
||||||
|
wdir="$1"
|
||||||
|
while [ "$wdir" != '/' ] ; do
|
||||||
|
if [ -d "$wdir"/.mvn ] ; then
|
||||||
|
basedir=$wdir
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
|
||||||
|
if [ -d "${wdir}" ]; then
|
||||||
|
wdir=`cd "$wdir/.."; pwd`
|
||||||
|
fi
|
||||||
|
# end of workaround
|
||||||
|
done
|
||||||
|
echo "${basedir}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# concatenates all lines of a file
|
||||||
|
concat_lines() {
|
||||||
|
if [ -f "$1" ]; then
|
||||||
|
echo "$(tr -s '\n' ' ' < "$1")"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
BASE_DIR=`find_maven_basedir "$(pwd)"`
|
||||||
|
if [ -z "$BASE_DIR" ]; then
|
||||||
|
exit 1;
|
||||||
|
fi
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||||
|
# This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||||
|
##########################################################################################
|
||||||
|
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Found .mvn/wrapper/maven-wrapper.jar"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
|
||||||
|
fi
|
||||||
|
jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"
|
||||||
|
while IFS="=" read key value; do
|
||||||
|
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
|
||||||
|
esac
|
||||||
|
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Downloading from: $jarUrl"
|
||||||
|
fi
|
||||||
|
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
|
||||||
|
|
||||||
|
if command -v wget > /dev/null; then
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Found wget ... using wget"
|
||||||
|
fi
|
||||||
|
wget "$jarUrl" -O "$wrapperJarPath"
|
||||||
|
elif command -v curl > /dev/null; then
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Found curl ... using curl"
|
||||||
|
fi
|
||||||
|
curl -o "$wrapperJarPath" "$jarUrl"
|
||||||
|
else
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Falling back to using Java to download"
|
||||||
|
fi
|
||||||
|
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
|
||||||
|
if [ -e "$javaClass" ]; then
|
||||||
|
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo " - Compiling MavenWrapperDownloader.java ..."
|
||||||
|
fi
|
||||||
|
# Compiling the Java class
|
||||||
|
("$JAVA_HOME/bin/javac" "$javaClass")
|
||||||
|
fi
|
||||||
|
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
|
||||||
|
# Running the downloader
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo " - Running MavenWrapperDownloader.java ..."
|
||||||
|
fi
|
||||||
|
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
##########################################################################################
|
||||||
|
# End of extension
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo $MAVEN_PROJECTBASEDIR
|
||||||
|
fi
|
||||||
|
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
|
||||||
|
|
||||||
|
# For Cygwin, switch paths to Windows format before running java
|
||||||
|
if $cygwin; then
|
||||||
|
[ -n "$M2_HOME" ] &&
|
||||||
|
M2_HOME=`cygpath --path --windows "$M2_HOME"`
|
||||||
|
[ -n "$JAVA_HOME" ] &&
|
||||||
|
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
|
||||||
|
[ -n "$CLASSPATH" ] &&
|
||||||
|
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
|
||||||
|
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
|
||||||
|
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
|
||||||
|
fi
|
||||||
|
|
||||||
|
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||||
|
|
||||||
|
exec "$JAVACMD" \
|
||||||
|
$MAVEN_OPTS \
|
||||||
|
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
|
||||||
|
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
|
||||||
|
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
|
161
mvnw.cmd
vendored
Normal file
161
mvnw.cmd
vendored
Normal file
@@ -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%
|
20
pom.xml
Normal file
20
pom.xml
Normal file
@@ -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>
|
15
sop-book/pom.xml
Normal file
15
sop-book/pom.xml
Normal file
@@ -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>
|
25
sop-book/sop-book-api/.gitignore
vendored
Normal file
25
sop-book/sop-book-api/.gitignore
vendored
Normal file
@@ -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/
|
46
sop-book/sop-book-api/pom.xml
Normal file
46
sop-book/sop-book-api/pom.xml
Normal file
@@ -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);
|
||||||
|
}
|
25
sop-book/sop-book-web/.gitignore
vendored
Normal file
25
sop-book/sop-book-web/.gitignore
vendored
Normal file
@@ -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/
|
BIN
sop-book/sop-book-web/.mvn/wrapper/maven-wrapper.jar
vendored
Normal file
BIN
sop-book/sop-book-web/.mvn/wrapper/maven-wrapper.jar
vendored
Normal file
Binary file not shown.
1
sop-book/sop-book-web/.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
1
sop-book/sop-book-web/.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
@@ -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
|
286
sop-book/sop-book-web/mvnw
vendored
Executable file
286
sop-book/sop-book-web/mvnw
vendored
Executable file
@@ -0,0 +1,286 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Maven2 Start Up Batch script
|
||||||
|
#
|
||||||
|
# Required ENV vars:
|
||||||
|
# ------------------
|
||||||
|
# JAVA_HOME - location of a JDK home dir
|
||||||
|
#
|
||||||
|
# Optional ENV vars
|
||||||
|
# -----------------
|
||||||
|
# M2_HOME - location of maven2's installed home dir
|
||||||
|
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||||
|
# e.g. to debug Maven itself, use
|
||||||
|
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||||
|
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
if [ -z "$MAVEN_SKIP_RC" ] ; then
|
||||||
|
|
||||||
|
if [ -f /etc/mavenrc ] ; then
|
||||||
|
. /etc/mavenrc
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "$HOME/.mavenrc" ] ; then
|
||||||
|
. "$HOME/.mavenrc"
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
# OS specific support. $var _must_ be set to either true or false.
|
||||||
|
cygwin=false;
|
||||||
|
darwin=false;
|
||||||
|
mingw=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN*) cygwin=true ;;
|
||||||
|
MINGW*) mingw=true;;
|
||||||
|
Darwin*) darwin=true
|
||||||
|
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
|
||||||
|
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
|
||||||
|
if [ -z "$JAVA_HOME" ]; then
|
||||||
|
if [ -x "/usr/libexec/java_home" ]; then
|
||||||
|
export JAVA_HOME="`/usr/libexec/java_home`"
|
||||||
|
else
|
||||||
|
export JAVA_HOME="/Library/Java/Home"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ] ; then
|
||||||
|
if [ -r /etc/gentoo-release ] ; then
|
||||||
|
JAVA_HOME=`java-config --jre-home`
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$M2_HOME" ] ; then
|
||||||
|
## resolve links - $0 may be a link to maven's home
|
||||||
|
PRG="$0"
|
||||||
|
|
||||||
|
# need this for relative symlinks
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG="`dirname "$PRG"`/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
saveddir=`pwd`
|
||||||
|
|
||||||
|
M2_HOME=`dirname "$PRG"`/..
|
||||||
|
|
||||||
|
# make it fully qualified
|
||||||
|
M2_HOME=`cd "$M2_HOME" && pwd`
|
||||||
|
|
||||||
|
cd "$saveddir"
|
||||||
|
# echo Using m2 at $M2_HOME
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
||||||
|
if $cygwin ; then
|
||||||
|
[ -n "$M2_HOME" ] &&
|
||||||
|
M2_HOME=`cygpath --unix "$M2_HOME"`
|
||||||
|
[ -n "$JAVA_HOME" ] &&
|
||||||
|
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||||
|
[ -n "$CLASSPATH" ] &&
|
||||||
|
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Mingw, ensure paths are in UNIX format before anything is touched
|
||||||
|
if $mingw ; then
|
||||||
|
[ -n "$M2_HOME" ] &&
|
||||||
|
M2_HOME="`(cd "$M2_HOME"; pwd)`"
|
||||||
|
[ -n "$JAVA_HOME" ] &&
|
||||||
|
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
|
||||||
|
# TODO classpath?
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ]; then
|
||||||
|
javaExecutable="`which javac`"
|
||||||
|
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
|
||||||
|
# readlink(1) is not available as standard on Solaris 10.
|
||||||
|
readLink=`which readlink`
|
||||||
|
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
|
||||||
|
if $darwin ; then
|
||||||
|
javaHome="`dirname \"$javaExecutable\"`"
|
||||||
|
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
|
||||||
|
else
|
||||||
|
javaExecutable="`readlink -f \"$javaExecutable\"`"
|
||||||
|
fi
|
||||||
|
javaHome="`dirname \"$javaExecutable\"`"
|
||||||
|
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
|
||||||
|
JAVA_HOME="$javaHome"
|
||||||
|
export JAVA_HOME
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAVACMD" ] ; then
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="`which java`"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
echo "Error: JAVA_HOME is not defined correctly." >&2
|
||||||
|
echo " We cannot execute $JAVACMD" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ] ; then
|
||||||
|
echo "Warning: JAVA_HOME environment variable is not set."
|
||||||
|
fi
|
||||||
|
|
||||||
|
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
|
||||||
|
|
||||||
|
# traverses directory structure from process work directory to filesystem root
|
||||||
|
# first directory with .mvn subdirectory is considered project base directory
|
||||||
|
find_maven_basedir() {
|
||||||
|
|
||||||
|
if [ -z "$1" ]
|
||||||
|
then
|
||||||
|
echo "Path not specified to find_maven_basedir"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
basedir="$1"
|
||||||
|
wdir="$1"
|
||||||
|
while [ "$wdir" != '/' ] ; do
|
||||||
|
if [ -d "$wdir"/.mvn ] ; then
|
||||||
|
basedir=$wdir
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
|
||||||
|
if [ -d "${wdir}" ]; then
|
||||||
|
wdir=`cd "$wdir/.."; pwd`
|
||||||
|
fi
|
||||||
|
# end of workaround
|
||||||
|
done
|
||||||
|
echo "${basedir}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# concatenates all lines of a file
|
||||||
|
concat_lines() {
|
||||||
|
if [ -f "$1" ]; then
|
||||||
|
echo "$(tr -s '\n' ' ' < "$1")"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
BASE_DIR=`find_maven_basedir "$(pwd)"`
|
||||||
|
if [ -z "$BASE_DIR" ]; then
|
||||||
|
exit 1;
|
||||||
|
fi
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||||
|
# This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||||
|
##########################################################################################
|
||||||
|
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Found .mvn/wrapper/maven-wrapper.jar"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
|
||||||
|
fi
|
||||||
|
jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"
|
||||||
|
while IFS="=" read key value; do
|
||||||
|
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
|
||||||
|
esac
|
||||||
|
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Downloading from: $jarUrl"
|
||||||
|
fi
|
||||||
|
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
|
||||||
|
|
||||||
|
if command -v wget > /dev/null; then
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Found wget ... using wget"
|
||||||
|
fi
|
||||||
|
wget "$jarUrl" -O "$wrapperJarPath"
|
||||||
|
elif command -v curl > /dev/null; then
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Found curl ... using curl"
|
||||||
|
fi
|
||||||
|
curl -o "$wrapperJarPath" "$jarUrl"
|
||||||
|
else
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Falling back to using Java to download"
|
||||||
|
fi
|
||||||
|
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
|
||||||
|
if [ -e "$javaClass" ]; then
|
||||||
|
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo " - Compiling MavenWrapperDownloader.java ..."
|
||||||
|
fi
|
||||||
|
# Compiling the Java class
|
||||||
|
("$JAVA_HOME/bin/javac" "$javaClass")
|
||||||
|
fi
|
||||||
|
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
|
||||||
|
# Running the downloader
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo " - Running MavenWrapperDownloader.java ..."
|
||||||
|
fi
|
||||||
|
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
##########################################################################################
|
||||||
|
# End of extension
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo $MAVEN_PROJECTBASEDIR
|
||||||
|
fi
|
||||||
|
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
|
||||||
|
|
||||||
|
# For Cygwin, switch paths to Windows format before running java
|
||||||
|
if $cygwin; then
|
||||||
|
[ -n "$M2_HOME" ] &&
|
||||||
|
M2_HOME=`cygpath --path --windows "$M2_HOME"`
|
||||||
|
[ -n "$JAVA_HOME" ] &&
|
||||||
|
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
|
||||||
|
[ -n "$CLASSPATH" ] &&
|
||||||
|
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
|
||||||
|
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
|
||||||
|
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
|
||||||
|
fi
|
||||||
|
|
||||||
|
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||||
|
|
||||||
|
exec "$JAVACMD" \
|
||||||
|
$MAVEN_OPTS \
|
||||||
|
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
|
||||||
|
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
|
||||||
|
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
|
161
sop-book/sop-book-web/mvnw.cmd
vendored
Normal file
161
sop-book/sop-book-web/mvnw.cmd
vendored
Normal file
@@ -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%
|
84
sop-book/sop-book-web/pom.xml
Normal file
84
sop-book/sop-book-web/pom.xml
Normal file
@@ -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() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
25
sop-gateway-common/.gitignore
vendored
Normal file
25
sop-gateway-common/.gitignore
vendored
Normal file
@@ -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/
|
131
sop-gateway-common/pom.xml
Normal file
131
sop-gateway-common/pom.xml
Normal file
@@ -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>
|
3
sop-gateway-common/readme.md
Normal file
3
sop-gateway-common/readme.md
Normal file
@@ -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
Reference in New Issue
Block a user