parent
85d61d2f1e
commit
46a546f1da
@ -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* |
|
||||||
|
@ -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)、秘钥管理等,未来还会实现更多功能。 |
||||||
|
|
||||||
#### 安装教程 |
## 项目特点 |
||||||
|
|
||||||
1. xxxx |
- 接入方式简单,与老项目不冲突,老项目注册到注册中心,然后的方法上加上注解即可。 |
||||||
2. xxxx |
- 架构松耦合,业务代码实现在各自微服务上,SOP不参与业务实现,这也是Spring Cloud微服务体系带来的好处。 |
||||||
3. xxxx |
- 扩展简单,开放平台对应的功能各自独立,可以自定义实现自己的需求,如:更改参数,更改签名规则等。 |
||||||
|
|
||||||
#### 使用说明 |
## 谁能使用这个项目 |
||||||
|
|
||||||
1. xxxx |
- 有现成的项目,想改造成开放平台供他人调用 |
||||||
2. xxxx |
- 有现成的项目,想暴露其中几个接口并通过开放平台供他人调用 |
||||||
3. xxxx |
- 想搭一个开放平台新项目,并结合微服务的方式去维护 |
||||||
|
- 对开放平台感兴趣的朋友 |
||||||
|
|
||||||
#### 参与贡献 |
以上情况都可以考虑使用SOP |
||||||
|
|
||||||
1. Fork 本仓库 |
## 架构图 |
||||||
2. 新建 Feat_xxx 分支 |
|
||||||
3. 提交代码 |
|
||||||
4. 新建 Pull Request |
|
||||||
|
|
||||||
|
|
||||||
#### 码云特技 |
|
||||||
|
|
||||||
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md |
## 工程说明 |
||||||
2. 码云官方博客 [blog.gitee.com](https://blog.gitee.com) |
|
||||||
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解码云上的优秀开源项目 |
- sop-registry:注册中心,eureka实现 |
||||||
4. [GVP](https://gitee.com/gvp) 全称是码云最有价值开源项目,是码云综合评定出的优秀开源项目 |
- sop-gateway:网关,zuul实现,统一访问入口 |
||||||
5. 码云官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) |
- sop-gateway-common:网关公共模块,封装常用功能,包含签名校验等功能。 |
||||||
6. 码云封面人物是一档用来展示码云会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) |
- sop-server-common:微服务端公共模块,封装配套功能 |
||||||
|
- sop-story:示例,story服务,同时作为Provider提供服务 |
||||||
|
- sop-book:示例,book服务,也是Consumer,调用story提供的服务 |
||||||
|
- sop-test:测试用例 |
||||||
|
|
||||||
|
## 相关文档 |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 沟通交流 |
||||||
|
|
||||||
|
Q群:328419269 |
||||||
|
@ -0,0 +1,286 @@ |
|||||||
|
#!/bin/sh |
||||||
|
# ---------------------------------------------------------------------------- |
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one |
||||||
|
# or more contributor license agreements. See the NOTICE file |
||||||
|
# distributed with this work for additional information |
||||||
|
# regarding copyright ownership. The ASF licenses this file |
||||||
|
# to you under the Apache License, Version 2.0 (the |
||||||
|
# "License"); you may not use this file except in compliance |
||||||
|
# with the License. You may obtain a copy of the License at |
||||||
|
# |
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
# |
||||||
|
# Unless required by applicable law or agreed to in writing, |
||||||
|
# software distributed under the License is distributed on an |
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
||||||
|
# KIND, either express or implied. See the License for the |
||||||
|
# specific language governing permissions and limitations |
||||||
|
# under the License. |
||||||
|
# ---------------------------------------------------------------------------- |
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------- |
||||||
|
# Maven2 Start Up Batch script |
||||||
|
# |
||||||
|
# Required ENV vars: |
||||||
|
# ------------------ |
||||||
|
# JAVA_HOME - location of a JDK home dir |
||||||
|
# |
||||||
|
# Optional ENV vars |
||||||
|
# ----------------- |
||||||
|
# M2_HOME - location of maven2's installed home dir |
||||||
|
# MAVEN_OPTS - parameters passed to the Java VM when running Maven |
||||||
|
# e.g. to debug Maven itself, use |
||||||
|
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 |
||||||
|
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files |
||||||
|
# ---------------------------------------------------------------------------- |
||||||
|
|
||||||
|
if [ -z "$MAVEN_SKIP_RC" ] ; then |
||||||
|
|
||||||
|
if [ -f /etc/mavenrc ] ; then |
||||||
|
. /etc/mavenrc |
||||||
|
fi |
||||||
|
|
||||||
|
if [ -f "$HOME/.mavenrc" ] ; then |
||||||
|
. "$HOME/.mavenrc" |
||||||
|
fi |
||||||
|
|
||||||
|
fi |
||||||
|
|
||||||
|
# OS specific support. $var _must_ be set to either true or false. |
||||||
|
cygwin=false; |
||||||
|
darwin=false; |
||||||
|
mingw=false |
||||||
|
case "`uname`" in |
||||||
|
CYGWIN*) cygwin=true ;; |
||||||
|
MINGW*) mingw=true;; |
||||||
|
Darwin*) darwin=true |
||||||
|
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home |
||||||
|
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html |
||||||
|
if [ -z "$JAVA_HOME" ]; then |
||||||
|
if [ -x "/usr/libexec/java_home" ]; then |
||||||
|
export JAVA_HOME="`/usr/libexec/java_home`" |
||||||
|
else |
||||||
|
export JAVA_HOME="/Library/Java/Home" |
||||||
|
fi |
||||||
|
fi |
||||||
|
;; |
||||||
|
esac |
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ] ; then |
||||||
|
if [ -r /etc/gentoo-release ] ; then |
||||||
|
JAVA_HOME=`java-config --jre-home` |
||||||
|
fi |
||||||
|
fi |
||||||
|
|
||||||
|
if [ -z "$M2_HOME" ] ; then |
||||||
|
## resolve links - $0 may be a link to maven's home |
||||||
|
PRG="$0" |
||||||
|
|
||||||
|
# need this for relative symlinks |
||||||
|
while [ -h "$PRG" ] ; do |
||||||
|
ls=`ls -ld "$PRG"` |
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'` |
||||||
|
if expr "$link" : '/.*' > /dev/null; then |
||||||
|
PRG="$link" |
||||||
|
else |
||||||
|
PRG="`dirname "$PRG"`/$link" |
||||||
|
fi |
||||||
|
done |
||||||
|
|
||||||
|
saveddir=`pwd` |
||||||
|
|
||||||
|
M2_HOME=`dirname "$PRG"`/.. |
||||||
|
|
||||||
|
# make it fully qualified |
||||||
|
M2_HOME=`cd "$M2_HOME" && pwd` |
||||||
|
|
||||||
|
cd "$saveddir" |
||||||
|
# echo Using m2 at $M2_HOME |
||||||
|
fi |
||||||
|
|
||||||
|
# For Cygwin, ensure paths are in UNIX format before anything is touched |
||||||
|
if $cygwin ; then |
||||||
|
[ -n "$M2_HOME" ] && |
||||||
|
M2_HOME=`cygpath --unix "$M2_HOME"` |
||||||
|
[ -n "$JAVA_HOME" ] && |
||||||
|
JAVA_HOME=`cygpath --unix "$JAVA_HOME"` |
||||||
|
[ -n "$CLASSPATH" ] && |
||||||
|
CLASSPATH=`cygpath --path --unix "$CLASSPATH"` |
||||||
|
fi |
||||||
|
|
||||||
|
# For Mingw, ensure paths are in UNIX format before anything is touched |
||||||
|
if $mingw ; then |
||||||
|
[ -n "$M2_HOME" ] && |
||||||
|
M2_HOME="`(cd "$M2_HOME"; pwd)`" |
||||||
|
[ -n "$JAVA_HOME" ] && |
||||||
|
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" |
||||||
|
# TODO classpath? |
||||||
|
fi |
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ]; then |
||||||
|
javaExecutable="`which javac`" |
||||||
|
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then |
||||||
|
# readlink(1) is not available as standard on Solaris 10. |
||||||
|
readLink=`which readlink` |
||||||
|
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then |
||||||
|
if $darwin ; then |
||||||
|
javaHome="`dirname \"$javaExecutable\"`" |
||||||
|
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" |
||||||
|
else |
||||||
|
javaExecutable="`readlink -f \"$javaExecutable\"`" |
||||||
|
fi |
||||||
|
javaHome="`dirname \"$javaExecutable\"`" |
||||||
|
javaHome=`expr "$javaHome" : '\(.*\)/bin'` |
||||||
|
JAVA_HOME="$javaHome" |
||||||
|
export JAVA_HOME |
||||||
|
fi |
||||||
|
fi |
||||||
|
fi |
||||||
|
|
||||||
|
if [ -z "$JAVACMD" ] ; then |
||||||
|
if [ -n "$JAVA_HOME" ] ; then |
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then |
||||||
|
# IBM's JDK on AIX uses strange locations for the executables |
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java" |
||||||
|
else |
||||||
|
JAVACMD="$JAVA_HOME/bin/java" |
||||||
|
fi |
||||||
|
else |
||||||
|
JAVACMD="`which java`" |
||||||
|
fi |
||||||
|
fi |
||||||
|
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then |
||||||
|
echo "Error: JAVA_HOME is not defined correctly." >&2 |
||||||
|
echo " We cannot execute $JAVACMD" >&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ] ; then |
||||||
|
echo "Warning: JAVA_HOME environment variable is not set." |
||||||
|
fi |
||||||
|
|
||||||
|
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher |
||||||
|
|
||||||
|
# traverses directory structure from process work directory to filesystem root |
||||||
|
# first directory with .mvn subdirectory is considered project base directory |
||||||
|
find_maven_basedir() { |
||||||
|
|
||||||
|
if [ -z "$1" ] |
||||||
|
then |
||||||
|
echo "Path not specified to find_maven_basedir" |
||||||
|
return 1 |
||||||
|
fi |
||||||
|
|
||||||
|
basedir="$1" |
||||||
|
wdir="$1" |
||||||
|
while [ "$wdir" != '/' ] ; do |
||||||
|
if [ -d "$wdir"/.mvn ] ; then |
||||||
|
basedir=$wdir |
||||||
|
break |
||||||
|
fi |
||||||
|
# workaround for JBEAP-8937 (on Solaris 10/Sparc) |
||||||
|
if [ -d "${wdir}" ]; then |
||||||
|
wdir=`cd "$wdir/.."; pwd` |
||||||
|
fi |
||||||
|
# end of workaround |
||||||
|
done |
||||||
|
echo "${basedir}" |
||||||
|
} |
||||||
|
|
||||||
|
# concatenates all lines of a file |
||||||
|
concat_lines() { |
||||||
|
if [ -f "$1" ]; then |
||||||
|
echo "$(tr -s '\n' ' ' < "$1")" |
||||||
|
fi |
||||||
|
} |
||||||
|
|
||||||
|
BASE_DIR=`find_maven_basedir "$(pwd)"` |
||||||
|
if [ -z "$BASE_DIR" ]; then |
||||||
|
exit 1; |
||||||
|
fi |
||||||
|
|
||||||
|
########################################################################################## |
||||||
|
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central |
||||||
|
# This allows using the maven wrapper in projects that prohibit checking in binary data. |
||||||
|
########################################################################################## |
||||||
|
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then |
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then |
||||||
|
echo "Found .mvn/wrapper/maven-wrapper.jar" |
||||||
|
fi |
||||||
|
else |
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then |
||||||
|
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." |
||||||
|
fi |
||||||
|
jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" |
||||||
|
while IFS="=" read key value; do |
||||||
|
case "$key" in (wrapperUrl) jarUrl="$value"; break ;; |
||||||
|
esac |
||||||
|
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" |
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then |
||||||
|
echo "Downloading from: $jarUrl" |
||||||
|
fi |
||||||
|
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" |
||||||
|
|
||||||
|
if command -v wget > /dev/null; then |
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then |
||||||
|
echo "Found wget ... using wget" |
||||||
|
fi |
||||||
|
wget "$jarUrl" -O "$wrapperJarPath" |
||||||
|
elif command -v curl > /dev/null; then |
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then |
||||||
|
echo "Found curl ... using curl" |
||||||
|
fi |
||||||
|
curl -o "$wrapperJarPath" "$jarUrl" |
||||||
|
else |
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then |
||||||
|
echo "Falling back to using Java to download" |
||||||
|
fi |
||||||
|
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" |
||||||
|
if [ -e "$javaClass" ]; then |
||||||
|
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then |
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then |
||||||
|
echo " - Compiling MavenWrapperDownloader.java ..." |
||||||
|
fi |
||||||
|
# Compiling the Java class |
||||||
|
("$JAVA_HOME/bin/javac" "$javaClass") |
||||||
|
fi |
||||||
|
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then |
||||||
|
# Running the downloader |
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then |
||||||
|
echo " - Running MavenWrapperDownloader.java ..." |
||||||
|
fi |
||||||
|
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") |
||||||
|
fi |
||||||
|
fi |
||||||
|
fi |
||||||
|
fi |
||||||
|
########################################################################################## |
||||||
|
# End of extension |
||||||
|
########################################################################################## |
||||||
|
|
||||||
|
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} |
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then |
||||||
|
echo $MAVEN_PROJECTBASEDIR |
||||||
|
fi |
||||||
|
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" |
||||||
|
|
||||||
|
# For Cygwin, switch paths to Windows format before running java |
||||||
|
if $cygwin; then |
||||||
|
[ -n "$M2_HOME" ] && |
||||||
|
M2_HOME=`cygpath --path --windows "$M2_HOME"` |
||||||
|
[ -n "$JAVA_HOME" ] && |
||||||
|
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` |
||||||
|
[ -n "$CLASSPATH" ] && |
||||||
|
CLASSPATH=`cygpath --path --windows "$CLASSPATH"` |
||||||
|
[ -n "$MAVEN_PROJECTBASEDIR" ] && |
||||||
|
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` |
||||||
|
fi |
||||||
|
|
||||||
|
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain |
||||||
|
|
||||||
|
exec "$JAVACMD" \ |
||||||
|
$MAVEN_OPTS \ |
||||||
|
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ |
||||||
|
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ |
||||||
|
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" |
@ -0,0 +1,161 @@ |
|||||||
|
@REM ---------------------------------------------------------------------------- |
||||||
|
@REM Licensed to the Apache Software Foundation (ASF) under one |
||||||
|
@REM or more contributor license agreements. See the NOTICE file |
||||||
|
@REM distributed with this work for additional information |
||||||
|
@REM regarding copyright ownership. The ASF licenses this file |
||||||
|
@REM to you under the Apache License, Version 2.0 (the |
||||||
|
@REM "License"); you may not use this file except in compliance |
||||||
|
@REM with the License. You may obtain a copy of the License at |
||||||
|
@REM |
||||||
|
@REM http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
@REM |
||||||
|
@REM Unless required by applicable law or agreed to in writing, |
||||||
|
@REM software distributed under the License is distributed on an |
||||||
|
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
||||||
|
@REM KIND, either express or implied. See the License for the |
||||||
|
@REM specific language governing permissions and limitations |
||||||
|
@REM under the License. |
||||||
|
@REM ---------------------------------------------------------------------------- |
||||||
|
|
||||||
|
@REM ---------------------------------------------------------------------------- |
||||||
|
@REM Maven2 Start Up Batch script |
||||||
|
@REM |
||||||
|
@REM Required ENV vars: |
||||||
|
@REM JAVA_HOME - location of a JDK home dir |
||||||
|
@REM |
||||||
|
@REM Optional ENV vars |
||||||
|
@REM M2_HOME - location of maven2's installed home dir |
||||||
|
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands |
||||||
|
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending |
||||||
|
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven |
||||||
|
@REM e.g. to debug Maven itself, use |
||||||
|
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 |
||||||
|
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files |
||||||
|
@REM ---------------------------------------------------------------------------- |
||||||
|
|
||||||
|
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' |
||||||
|
@echo off |
||||||
|
@REM set title of command window |
||||||
|
title %0 |
||||||
|
@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' |
||||||
|
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% |
||||||
|
|
||||||
|
@REM set %HOME% to equivalent of $HOME |
||||||
|
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") |
||||||
|
|
||||||
|
@REM Execute a user defined script before this one |
||||||
|
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre |
||||||
|
@REM check for pre script, once with legacy .bat ending and once with .cmd ending |
||||||
|
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" |
||||||
|
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" |
||||||
|
:skipRcPre |
||||||
|
|
||||||
|
@setlocal |
||||||
|
|
||||||
|
set ERROR_CODE=0 |
||||||
|
|
||||||
|
@REM To isolate internal variables from possible post scripts, we use another setlocal |
||||||
|
@setlocal |
||||||
|
|
||||||
|
@REM ==== START VALIDATION ==== |
||||||
|
if not "%JAVA_HOME%" == "" goto OkJHome |
||||||
|
|
||||||
|
echo. |
||||||
|
echo Error: JAVA_HOME not found in your environment. >&2 |
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the >&2 |
||||||
|
echo location of your Java installation. >&2 |
||||||
|
echo. |
||||||
|
goto error |
||||||
|
|
||||||
|
:OkJHome |
||||||
|
if exist "%JAVA_HOME%\bin\java.exe" goto init |
||||||
|
|
||||||
|
echo. |
||||||
|
echo Error: JAVA_HOME is set to an invalid directory. >&2 |
||||||
|
echo JAVA_HOME = "%JAVA_HOME%" >&2 |
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the >&2 |
||||||
|
echo location of your Java installation. >&2 |
||||||
|
echo. |
||||||
|
goto error |
||||||
|
|
||||||
|
@REM ==== END VALIDATION ==== |
||||||
|
|
||||||
|
:init |
||||||
|
|
||||||
|
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". |
||||||
|
@REM Fallback to current working directory if not found. |
||||||
|
|
||||||
|
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% |
||||||
|
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir |
||||||
|
|
||||||
|
set EXEC_DIR=%CD% |
||||||
|
set WDIR=%EXEC_DIR% |
||||||
|
:findBaseDir |
||||||
|
IF EXIST "%WDIR%"\.mvn goto baseDirFound |
||||||
|
cd .. |
||||||
|
IF "%WDIR%"=="%CD%" goto baseDirNotFound |
||||||
|
set WDIR=%CD% |
||||||
|
goto findBaseDir |
||||||
|
|
||||||
|
:baseDirFound |
||||||
|
set MAVEN_PROJECTBASEDIR=%WDIR% |
||||||
|
cd "%EXEC_DIR%" |
||||||
|
goto endDetectBaseDir |
||||||
|
|
||||||
|
:baseDirNotFound |
||||||
|
set MAVEN_PROJECTBASEDIR=%EXEC_DIR% |
||||||
|
cd "%EXEC_DIR%" |
||||||
|
|
||||||
|
:endDetectBaseDir |
||||||
|
|
||||||
|
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig |
||||||
|
|
||||||
|
@setlocal EnableExtensions EnableDelayedExpansion |
||||||
|
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a |
||||||
|
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% |
||||||
|
|
||||||
|
:endReadAdditionalConfig |
||||||
|
|
||||||
|
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" |
||||||
|
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" |
||||||
|
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain |
||||||
|
|
||||||
|
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" |
||||||
|
FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( |
||||||
|
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B |
||||||
|
) |
||||||
|
|
||||||
|
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central |
||||||
|
@REM This allows using the maven wrapper in projects that prohibit checking in binary data. |
||||||
|
if exist %WRAPPER_JAR% ( |
||||||
|
echo Found %WRAPPER_JAR% |
||||||
|
) else ( |
||||||
|
echo Couldn't find %WRAPPER_JAR%, downloading it ... |
||||||
|
echo Downloading from: %DOWNLOAD_URL% |
||||||
|
powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" |
||||||
|
echo Finished downloading %WRAPPER_JAR% |
||||||
|
) |
||||||
|
@REM End of extension |
||||||
|
|
||||||
|
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* |
||||||
|
if ERRORLEVEL 1 goto error |
||||||
|
goto end |
||||||
|
|
||||||
|
:error |
||||||
|
set ERROR_CODE=1 |
||||||
|
|
||||||
|
:end |
||||||
|
@endlocal & set ERROR_CODE=%ERROR_CODE% |
||||||
|
|
||||||
|
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost |
||||||
|
@REM check for post script, once with legacy .bat ending and once with .cmd ending |
||||||
|
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" |
||||||
|
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" |
||||||
|
:skipRcPost |
||||||
|
|
||||||
|
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' |
||||||
|
if "%MAVEN_BATCH_PAUSE%" == "on" pause |
||||||
|
|
||||||
|
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% |
||||||
|
|
||||||
|
exit /B %ERROR_CODE% |
@ -0,0 +1,20 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||||
|
<modelVersion>4.0.0</modelVersion> |
||||||
|
<groupId>com.gitee.sop</groupId> |
||||||
|
<artifactId>sop-parent</artifactId> |
||||||
|
<version>1.0.0-SNAPSHOT</version> |
||||||
|
<packaging>pom</packaging> |
||||||
|
|
||||||
|
<modules> |
||||||
|
<module>sop-registry</module> |
||||||
|
<module>sop-gateway</module> |
||||||
|
<module>sop-gateway-common</module> |
||||||
|
<module>sop-server-common</module> |
||||||
|
<module>sop-story</module> |
||||||
|
<module>sop-book</module> |
||||||
|
<module>sop-test</module> |
||||||
|
</modules> |
||||||
|
</project> |
@ -0,0 +1,15 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||||
|
<modelVersion>4.0.0</modelVersion> |
||||||
|
<groupId>com.gitee.sop</groupId> |
||||||
|
<artifactId>sop-book-parent</artifactId> |
||||||
|
<version>1.0.0-SNAPSHOT</version> |
||||||
|
<packaging>pom</packaging> |
||||||
|
|
||||||
|
<modules> |
||||||
|
<module>sop-book-api</module> |
||||||
|
<module>sop-book-web</module> |
||||||
|
</modules> |
||||||
|
</project> |
@ -0,0 +1,25 @@ |
|||||||
|
/target/ |
||||||
|
!.mvn/wrapper/maven-wrapper.jar |
||||||
|
|
||||||
|
### STS ### |
||||||
|
.apt_generated |
||||||
|
.classpath |
||||||
|
.factorypath |
||||||
|
.project |
||||||
|
.settings |
||||||
|
.springBeans |
||||||
|
.sts4-cache |
||||||
|
|
||||||
|
### IntelliJ IDEA ### |
||||||
|
.idea |
||||||
|
*.iws |
||||||
|
*.iml |
||||||
|
*.ipr |
||||||
|
|
||||||
|
### NetBeans ### |
||||||
|
/nbproject/private/ |
||||||
|
/nbbuild/ |
||||||
|
/dist/ |
||||||
|
/nbdist/ |
||||||
|
/.nb-gradle/ |
||||||
|
/build/ |
@ -0,0 +1,46 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||||
|
<modelVersion>4.0.0</modelVersion> |
||||||
|
<parent> |
||||||
|
<groupId>org.springframework.boot</groupId> |
||||||
|
<artifactId>spring-boot-starter-parent</artifactId> |
||||||
|
<version>2.1.2.RELEASE</version> |
||||||
|
<relativePath/> <!-- lookup parent from repository --> |
||||||
|
</parent> |
||||||
|
<groupId>com.gitee.sop</groupId> |
||||||
|
<artifactId>sop-book-api</artifactId> |
||||||
|
<version>1.0-SNAPSHOT</version> |
||||||
|
|
||||||
|
<properties> |
||||||
|
<java.version>1.8</java.version> |
||||||
|
<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version> |
||||||
|
</properties> |
||||||
|
|
||||||
|
<dependencies> |
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework.boot</groupId> |
||||||
|
<artifactId>spring-boot-starter-web</artifactId> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<dependency> |
||||||
|
<groupId>org.projectlombok</groupId> |
||||||
|
<artifactId>lombok</artifactId> |
||||||
|
<version>1.18.4</version> |
||||||
|
<scope>provided</scope> |
||||||
|
</dependency> |
||||||
|
</dependencies> |
||||||
|
|
||||||
|
<dependencyManagement> |
||||||
|
<dependencies> |
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework.cloud</groupId> |
||||||
|
<artifactId>spring-cloud-dependencies</artifactId> |
||||||
|
<version>${spring-cloud.version}</version> |
||||||
|
<type>pom</type> |
||||||
|
<scope>import</scope> |
||||||
|
</dependency> |
||||||
|
</dependencies> |
||||||
|
</dependencyManagement> |
||||||
|
</project> |
@ -0,0 +1,12 @@ |
|||||||
|
package com.gitee.book.api.domain; |
||||||
|
|
||||||
|
import lombok.Data; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
@Data |
||||||
|
public class Book { |
||||||
|
private int id; |
||||||
|
private String name; |
||||||
|
} |
@ -0,0 +1,14 @@ |
|||||||
|
package com.hhdd.book.api.service; |
||||||
|
|
||||||
|
import com.gitee.book.api.domain.Book; |
||||||
|
import org.springframework.web.bind.annotation.RequestMapping; |
||||||
|
import org.springframework.web.bind.annotation.RequestParam; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
@RequestMapping("/book") |
||||||
|
public interface BookService { |
||||||
|
@RequestMapping("/getBook") |
||||||
|
Book getBook(@RequestParam("id") int id); |
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
/target/ |
||||||
|
!.mvn/wrapper/maven-wrapper.jar |
||||||
|
|
||||||
|
### STS ### |
||||||
|
.apt_generated |
||||||
|
.classpath |
||||||
|
.factorypath |
||||||
|
.project |
||||||
|
.settings |
||||||
|
.springBeans |
||||||
|
.sts4-cache |
||||||
|
|
||||||
|
### IntelliJ IDEA ### |
||||||
|
.idea |
||||||
|
*.iws |
||||||
|
*.iml |
||||||
|
*.ipr |
||||||
|
|
||||||
|
### NetBeans ### |
||||||
|
/nbproject/private/ |
||||||
|
/nbbuild/ |
||||||
|
/dist/ |
||||||
|
/nbdist/ |
||||||
|
/.nb-gradle/ |
||||||
|
/build/ |
Binary file not shown.
@ -0,0 +1 @@ |
|||||||
|
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.0/apache-maven-3.6.0-bin.zip |
@ -0,0 +1,286 @@ |
|||||||
|
#!/bin/sh |
||||||
|
# ---------------------------------------------------------------------------- |
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one |
||||||
|
# or more contributor license agreements. See the NOTICE file |
||||||
|
# distributed with this work for additional information |
||||||
|
# regarding copyright ownership. The ASF licenses this file |
||||||
|
# to you under the Apache License, Version 2.0 (the |
||||||
|
# "License"); you may not use this file except in compliance |
||||||
|
# with the License. You may obtain a copy of the License at |
||||||
|
# |
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
# |
||||||
|
# Unless required by applicable law or agreed to in writing, |
||||||
|
# software distributed under the License is distributed on an |
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
||||||
|
# KIND, either express or implied. See the License for the |
||||||
|
# specific language governing permissions and limitations |
||||||
|
# under the License. |
||||||
|
# ---------------------------------------------------------------------------- |
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------- |
||||||
|
# Maven2 Start Up Batch script |
||||||
|
# |
||||||
|
# Required ENV vars: |
||||||
|
# ------------------ |
||||||
|
# JAVA_HOME - location of a JDK home dir |
||||||
|
# |
||||||
|
# Optional ENV vars |
||||||
|
# ----------------- |
||||||
|
# M2_HOME - location of maven2's installed home dir |
||||||
|
# MAVEN_OPTS - parameters passed to the Java VM when running Maven |
||||||
|
# e.g. to debug Maven itself, use |
||||||
|
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 |
||||||
|
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files |
||||||
|
# ---------------------------------------------------------------------------- |
||||||
|
|
||||||
|
if [ -z "$MAVEN_SKIP_RC" ] ; then |
||||||
|
|
||||||
|
if [ -f /etc/mavenrc ] ; then |
||||||
|
. /etc/mavenrc |
||||||
|
fi |
||||||
|
|
||||||
|
if [ -f "$HOME/.mavenrc" ] ; then |
||||||
|
. "$HOME/.mavenrc" |
||||||
|
fi |
||||||
|
|
||||||
|
fi |
||||||
|
|
||||||
|
# OS specific support. $var _must_ be set to either true or false. |
||||||
|
cygwin=false; |
||||||
|
darwin=false; |
||||||
|
mingw=false |
||||||
|
case "`uname`" in |
||||||
|
CYGWIN*) cygwin=true ;; |
||||||
|
MINGW*) mingw=true;; |
||||||
|
Darwin*) darwin=true |
||||||
|
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home |
||||||
|
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html |
||||||
|
if [ -z "$JAVA_HOME" ]; then |
||||||
|
if [ -x "/usr/libexec/java_home" ]; then |
||||||
|
export JAVA_HOME="`/usr/libexec/java_home`" |
||||||
|
else |
||||||
|
export JAVA_HOME="/Library/Java/Home" |
||||||
|
fi |
||||||
|
fi |
||||||
|
;; |
||||||
|
esac |
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ] ; then |
||||||
|
if [ -r /etc/gentoo-release ] ; then |
||||||
|
JAVA_HOME=`java-config --jre-home` |
||||||
|
fi |
||||||
|
fi |
||||||
|
|
||||||
|
if [ -z "$M2_HOME" ] ; then |
||||||
|
## resolve links - $0 may be a link to maven's home |
||||||
|
PRG="$0" |
||||||
|
|
||||||
|
# need this for relative symlinks |
||||||
|
while [ -h "$PRG" ] ; do |
||||||
|
ls=`ls -ld "$PRG"` |
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'` |
||||||
|
if expr "$link" : '/.*' > /dev/null; then |
||||||
|
PRG="$link" |
||||||
|
else |
||||||
|
PRG="`dirname "$PRG"`/$link" |
||||||
|
fi |
||||||
|
done |
||||||
|
|
||||||
|
saveddir=`pwd` |
||||||
|
|
||||||
|
M2_HOME=`dirname "$PRG"`/.. |
||||||
|
|
||||||
|
# make it fully qualified |
||||||
|
M2_HOME=`cd "$M2_HOME" && pwd` |
||||||
|
|
||||||
|
cd "$saveddir" |
||||||
|
# echo Using m2 at $M2_HOME |
||||||
|
fi |
||||||
|
|
||||||
|
# For Cygwin, ensure paths are in UNIX format before anything is touched |
||||||
|
if $cygwin ; then |
||||||
|
[ -n "$M2_HOME" ] && |
||||||
|
M2_HOME=`cygpath --unix "$M2_HOME"` |
||||||
|
[ -n "$JAVA_HOME" ] && |
||||||
|
JAVA_HOME=`cygpath --unix "$JAVA_HOME"` |
||||||
|
[ -n "$CLASSPATH" ] && |
||||||
|
CLASSPATH=`cygpath --path --unix "$CLASSPATH"` |
||||||
|
fi |
||||||
|
|
||||||
|
# For Mingw, ensure paths are in UNIX format before anything is touched |
||||||
|
if $mingw ; then |
||||||
|
[ -n "$M2_HOME" ] && |
||||||
|
M2_HOME="`(cd "$M2_HOME"; pwd)`" |
||||||
|
[ -n "$JAVA_HOME" ] && |
||||||
|
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" |
||||||
|
# TODO classpath? |
||||||
|
fi |
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ]; then |
||||||
|
javaExecutable="`which javac`" |
||||||
|
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then |
||||||
|
# readlink(1) is not available as standard on Solaris 10. |
||||||
|
readLink=`which readlink` |
||||||
|
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then |
||||||
|
if $darwin ; then |
||||||
|
javaHome="`dirname \"$javaExecutable\"`" |
||||||
|
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" |
||||||
|
else |
||||||
|
javaExecutable="`readlink -f \"$javaExecutable\"`" |
||||||
|
fi |
||||||
|
javaHome="`dirname \"$javaExecutable\"`" |
||||||
|
javaHome=`expr "$javaHome" : '\(.*\)/bin'` |
||||||
|
JAVA_HOME="$javaHome" |
||||||
|
export JAVA_HOME |
||||||
|
fi |
||||||
|
fi |
||||||
|
fi |
||||||
|
|
||||||
|
if [ -z "$JAVACMD" ] ; then |
||||||
|
if [ -n "$JAVA_HOME" ] ; then |
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then |
||||||
|
# IBM's JDK on AIX uses strange locations for the executables |
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java" |
||||||
|
else |
||||||
|
JAVACMD="$JAVA_HOME/bin/java" |
||||||
|
fi |
||||||
|
else |
||||||
|
JAVACMD="`which java`" |
||||||
|
fi |
||||||
|
fi |
||||||
|
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then |
||||||
|
echo "Error: JAVA_HOME is not defined correctly." >&2 |
||||||
|
echo " We cannot execute $JAVACMD" >&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ] ; then |
||||||
|
echo "Warning: JAVA_HOME environment variable is not set." |
||||||
|
fi |
||||||
|
|
||||||
|
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher |
||||||
|
|
||||||
|
# traverses directory structure from process work directory to filesystem root |
||||||
|
# first directory with .mvn subdirectory is considered project base directory |
||||||
|
find_maven_basedir() { |
||||||
|
|
||||||
|
if [ -z "$1" ] |
||||||
|
then |
||||||
|
echo "Path not specified to find_maven_basedir" |
||||||
|
return 1 |
||||||
|
fi |
||||||
|
|
||||||
|
basedir="$1" |
||||||
|
wdir="$1" |
||||||
|
while [ "$wdir" != '/' ] ; do |
||||||
|
if [ -d "$wdir"/.mvn ] ; then |
||||||
|
basedir=$wdir |
||||||
|
break |
||||||
|
fi |
||||||
|
# workaround for JBEAP-8937 (on Solaris 10/Sparc) |
||||||
|
if [ -d "${wdir}" ]; then |
||||||
|
wdir=`cd "$wdir/.."; pwd` |
||||||
|
fi |
||||||
|
# end of workaround |
||||||
|
done |
||||||
|
echo "${basedir}" |
||||||
|
} |
||||||
|
|
||||||
|
# concatenates all lines of a file |
||||||
|
concat_lines() { |
||||||
|
if [ -f "$1" ]; then |
||||||
|
echo "$(tr -s '\n' ' ' < "$1")" |
||||||
|
fi |
||||||
|
} |
||||||
|
|
||||||
|
BASE_DIR=`find_maven_basedir "$(pwd)"` |
||||||
|
if [ -z "$BASE_DIR" ]; then |
||||||
|
exit 1; |
||||||
|
fi |
||||||
|
|
||||||
|
########################################################################################## |
||||||
|
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central |
||||||
|
# This allows using the maven wrapper in projects that prohibit checking in binary data. |
||||||
|
########################################################################################## |
||||||
|
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then |
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then |
||||||
|
echo "Found .mvn/wrapper/maven-wrapper.jar" |
||||||
|
fi |
||||||
|
else |
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then |
||||||
|
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." |
||||||
|
fi |
||||||
|
jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" |
||||||
|
while IFS="=" read key value; do |
||||||
|
case "$key" in (wrapperUrl) jarUrl="$value"; break ;; |
||||||
|
esac |
||||||
|
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" |
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then |
||||||
|
echo "Downloading from: $jarUrl" |
||||||
|
fi |
||||||
|
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" |
||||||
|
|
||||||
|
if command -v wget > /dev/null; then |
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then |
||||||
|
echo "Found wget ... using wget" |
||||||
|
fi |
||||||
|
wget "$jarUrl" -O "$wrapperJarPath" |
||||||
|
elif command -v curl > /dev/null; then |
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then |
||||||
|
echo "Found curl ... using curl" |
||||||
|
fi |
||||||
|
curl -o "$wrapperJarPath" "$jarUrl" |
||||||
|
else |
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then |
||||||
|
echo "Falling back to using Java to download" |
||||||
|
fi |
||||||
|
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" |
||||||
|
if [ -e "$javaClass" ]; then |
||||||
|
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then |
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then |
||||||
|
echo " - Compiling MavenWrapperDownloader.java ..." |
||||||
|
fi |
||||||
|
# Compiling the Java class |
||||||
|
("$JAVA_HOME/bin/javac" "$javaClass") |
||||||
|
fi |
||||||
|
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then |
||||||
|
# Running the downloader |
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then |
||||||
|
echo " - Running MavenWrapperDownloader.java ..." |
||||||
|
fi |
||||||
|
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") |
||||||
|
fi |
||||||
|
fi |
||||||
|
fi |
||||||
|
fi |
||||||
|
########################################################################################## |
||||||
|
# End of extension |
||||||
|
########################################################################################## |
||||||
|
|
||||||
|
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} |
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then |
||||||
|
echo $MAVEN_PROJECTBASEDIR |
||||||
|
fi |
||||||
|
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" |
||||||
|
|
||||||
|
# For Cygwin, switch paths to Windows format before running java |
||||||
|
if $cygwin; then |
||||||
|
[ -n "$M2_HOME" ] && |
||||||
|
M2_HOME=`cygpath --path --windows "$M2_HOME"` |
||||||
|
[ -n "$JAVA_HOME" ] && |
||||||
|
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` |
||||||
|
[ -n "$CLASSPATH" ] && |
||||||
|
CLASSPATH=`cygpath --path --windows "$CLASSPATH"` |
||||||
|
[ -n "$MAVEN_PROJECTBASEDIR" ] && |
||||||
|
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` |
||||||
|
fi |
||||||
|
|
||||||
|
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain |
||||||
|
|
||||||
|
exec "$JAVACMD" \ |
||||||
|
$MAVEN_OPTS \ |
||||||
|
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ |
||||||
|
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ |
||||||
|
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" |
@ -0,0 +1,161 @@ |
|||||||
|
@REM ---------------------------------------------------------------------------- |
||||||
|
@REM Licensed to the Apache Software Foundation (ASF) under one |
||||||
|
@REM or more contributor license agreements. See the NOTICE file |
||||||
|
@REM distributed with this work for additional information |
||||||
|
@REM regarding copyright ownership. The ASF licenses this file |
||||||
|
@REM to you under the Apache License, Version 2.0 (the |
||||||
|
@REM "License"); you may not use this file except in compliance |
||||||
|
@REM with the License. You may obtain a copy of the License at |
||||||
|
@REM |
||||||
|
@REM http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
@REM |
||||||
|
@REM Unless required by applicable law or agreed to in writing, |
||||||
|
@REM software distributed under the License is distributed on an |
||||||
|
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
||||||
|
@REM KIND, either express or implied. See the License for the |
||||||
|
@REM specific language governing permissions and limitations |
||||||
|
@REM under the License. |
||||||
|
@REM ---------------------------------------------------------------------------- |
||||||
|
|
||||||
|
@REM ---------------------------------------------------------------------------- |
||||||
|
@REM Maven2 Start Up Batch script |
||||||
|
@REM |
||||||
|
@REM Required ENV vars: |
||||||
|
@REM JAVA_HOME - location of a JDK home dir |
||||||
|
@REM |
||||||
|
@REM Optional ENV vars |
||||||
|
@REM M2_HOME - location of maven2's installed home dir |
||||||
|
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands |
||||||
|
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending |
||||||
|
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven |
||||||
|
@REM e.g. to debug Maven itself, use |
||||||
|
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 |
||||||
|
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files |
||||||
|
@REM ---------------------------------------------------------------------------- |
||||||
|
|
||||||
|
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' |
||||||
|
@echo off |
||||||
|
@REM set title of command window |
||||||
|
title %0 |
||||||
|
@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' |
||||||
|
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% |
||||||
|
|
||||||
|
@REM set %HOME% to equivalent of $HOME |
||||||
|
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") |
||||||
|
|
||||||
|
@REM Execute a user defined script before this one |
||||||
|
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre |
||||||
|
@REM check for pre script, once with legacy .bat ending and once with .cmd ending |
||||||
|
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" |
||||||
|
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" |
||||||
|
:skipRcPre |
||||||
|
|
||||||
|
@setlocal |
||||||
|
|
||||||
|
set ERROR_CODE=0 |
||||||
|
|
||||||
|
@REM To isolate internal variables from possible post scripts, we use another setlocal |
||||||
|
@setlocal |
||||||
|
|
||||||
|
@REM ==== START VALIDATION ==== |
||||||
|
if not "%JAVA_HOME%" == "" goto OkJHome |
||||||
|
|
||||||
|
echo. |
||||||
|
echo Error: JAVA_HOME not found in your environment. >&2 |
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the >&2 |
||||||
|
echo location of your Java installation. >&2 |
||||||
|
echo. |
||||||
|
goto error |
||||||
|
|
||||||
|
:OkJHome |
||||||
|
if exist "%JAVA_HOME%\bin\java.exe" goto init |
||||||
|
|
||||||
|
echo. |
||||||
|
echo Error: JAVA_HOME is set to an invalid directory. >&2 |
||||||
|
echo JAVA_HOME = "%JAVA_HOME%" >&2 |
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the >&2 |
||||||
|
echo location of your Java installation. >&2 |
||||||
|
echo. |
||||||
|
goto error |
||||||
|
|
||||||
|
@REM ==== END VALIDATION ==== |
||||||
|
|
||||||
|
:init |
||||||
|
|
||||||
|
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". |
||||||
|
@REM Fallback to current working directory if not found. |
||||||
|
|
||||||
|
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% |
||||||
|
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir |
||||||
|
|
||||||
|
set EXEC_DIR=%CD% |
||||||
|
set WDIR=%EXEC_DIR% |
||||||
|
:findBaseDir |
||||||
|
IF EXIST "%WDIR%"\.mvn goto baseDirFound |
||||||
|
cd .. |
||||||
|
IF "%WDIR%"=="%CD%" goto baseDirNotFound |
||||||
|
set WDIR=%CD% |
||||||
|
goto findBaseDir |
||||||
|
|
||||||
|
:baseDirFound |
||||||
|
set MAVEN_PROJECTBASEDIR=%WDIR% |
||||||
|
cd "%EXEC_DIR%" |
||||||
|
goto endDetectBaseDir |
||||||
|
|
||||||
|
:baseDirNotFound |
||||||
|
set MAVEN_PROJECTBASEDIR=%EXEC_DIR% |
||||||
|
cd "%EXEC_DIR%" |
||||||
|
|
||||||
|
:endDetectBaseDir |
||||||
|
|
||||||
|
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig |
||||||
|
|
||||||
|
@setlocal EnableExtensions EnableDelayedExpansion |
||||||
|
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a |
||||||
|
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% |
||||||
|
|
||||||
|
:endReadAdditionalConfig |
||||||
|
|
||||||
|
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" |
||||||
|
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" |
||||||
|
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain |
||||||
|
|
||||||
|
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" |
||||||
|
FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( |
||||||
|
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B |
||||||
|
) |
||||||
|
|
||||||
|
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central |
||||||
|
@REM This allows using the maven wrapper in projects that prohibit checking in binary data. |
||||||
|
if exist %WRAPPER_JAR% ( |
||||||
|
echo Found %WRAPPER_JAR% |
||||||
|
) else ( |
||||||
|
echo Couldn't find %WRAPPER_JAR%, downloading it ... |
||||||
|
echo Downloading from: %DOWNLOAD_URL% |
||||||
|
powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" |
||||||
|
echo Finished downloading %WRAPPER_JAR% |
||||||
|
) |
||||||
|
@REM End of extension |
||||||
|
|
||||||
|
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* |
||||||
|
if ERRORLEVEL 1 goto error |
||||||
|
goto end |
||||||
|
|
||||||
|
:error |
||||||
|
set ERROR_CODE=1 |
||||||
|
|
||||||
|
:end |
||||||
|
@endlocal & set ERROR_CODE=%ERROR_CODE% |
||||||
|
|
||||||
|
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost |
||||||
|
@REM check for post script, once with legacy .bat ending and once with .cmd ending |
||||||
|
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" |
||||||
|
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" |
||||||
|
:skipRcPost |
||||||
|
|
||||||
|
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' |
||||||
|
if "%MAVEN_BATCH_PAUSE%" == "on" pause |
||||||
|
|
||||||
|
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% |
||||||
|
|
||||||
|
exit /B %ERROR_CODE% |
@ -0,0 +1,84 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||||
|
<modelVersion>4.0.0</modelVersion> |
||||||
|
<parent> |
||||||
|
<groupId>org.springframework.boot</groupId> |
||||||
|
<artifactId>spring-boot-starter-parent</artifactId> |
||||||
|
<version>2.1.2.RELEASE</version> |
||||||
|
<relativePath/> <!-- lookup parent from repository --> |
||||||
|
</parent> |
||||||
|
<groupId>com.gitee.sop</groupId> |
||||||
|
<artifactId>sop-book-web</artifactId> |
||||||
|
<version>0.0.1-SNAPSHOT</version> |
||||||
|
<name>sop-story</name> |
||||||
|
<description>Demo project for Spring Boot</description> |
||||||
|
|
||||||
|
<properties> |
||||||
|
<java.version>1.8</java.version> |
||||||
|
<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version> |
||||||
|
</properties> |
||||||
|
|
||||||
|
<dependencies> |
||||||
|
<dependency> |
||||||
|
<groupId>com.gitee.sop</groupId> |
||||||
|
<artifactId>sop-book-api</artifactId> |
||||||
|
<version>1.0-SNAPSHOT</version> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>com.gitee.sop</groupId> |
||||||
|
<artifactId>sop-story-api</artifactId> |
||||||
|
<version>1.0-SNAPSHOT</version> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework.cloud</groupId> |
||||||
|
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework.cloud</groupId> |
||||||
|
<artifactId>spring-cloud-starter-openfeign</artifactId> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework.boot</groupId> |
||||||
|
<artifactId>spring-boot-starter-test</artifactId> |
||||||
|
<scope>test</scope> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.projectlombok</groupId> |
||||||
|
<artifactId>lombok</artifactId> |
||||||
|
<version>1.18.4</version> |
||||||
|
<scope>compile</scope> |
||||||
|
</dependency> |
||||||
|
</dependencies> |
||||||
|
|
||||||
|
<dependencyManagement> |
||||||
|
<dependencies> |
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework.cloud</groupId> |
||||||
|
<artifactId>spring-cloud-dependencies</artifactId> |
||||||
|
<version>${spring-cloud.version}</version> |
||||||
|
<type>pom</type> |
||||||
|
<scope>import</scope> |
||||||
|
</dependency> |
||||||
|
</dependencies> |
||||||
|
</dependencyManagement> |
||||||
|
|
||||||
|
<build> |
||||||
|
<plugins> |
||||||
|
<plugin> |
||||||
|
<groupId>org.springframework.boot</groupId> |
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId> |
||||||
|
</plugin> |
||||||
|
</plugins> |
||||||
|
</build> |
||||||
|
|
||||||
|
<repositories> |
||||||
|
<repository> |
||||||
|
<id>spring-milestones</id> |
||||||
|
<name>Spring Milestones</name> |
||||||
|
<url>https://repo.spring.io/milestone</url> |
||||||
|
</repository> |
||||||
|
</repositories> |
||||||
|
|
||||||
|
</project> |
@ -0,0 +1,20 @@ |
|||||||
|
package com.gitee.sop.bookweb; |
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication; |
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication; |
||||||
|
import org.springframework.cloud.client.discovery.EnableDiscoveryClient; |
||||||
|
import org.springframework.cloud.openfeign.EnableFeignClients; |
||||||
|
|
||||||
|
// 允许调用其他服务
|
||||||
|
@EnableFeignClients |
||||||
|
// 服务注册
|
||||||
|
@EnableDiscoveryClient |
||||||
|
@SpringBootApplication |
||||||
|
public class SopBookApplication { |
||||||
|
|
||||||
|
public static void main(String[] args) { |
||||||
|
SpringApplication.run(SopBookApplication.class, args); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
@ -0,0 +1,14 @@ |
|||||||
|
package com.gitee.sop.bookweb.consumer; |
||||||
|
|
||||||
|
import com.gitee.sop.story.api.service.StoryService; |
||||||
|
import org.springframework.cloud.openfeign.FeignClient; |
||||||
|
|
||||||
|
/** |
||||||
|
* 调用story服务 |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
// value对应的spring.application.name
|
||||||
|
@FeignClient("story-service") |
||||||
|
public interface StoryServiceConsumer extends StoryService { |
||||||
|
} |
@ -0,0 +1,74 @@ |
|||||||
|
package com.gitee.sop.bookweb.controller; |
||||||
|
|
||||||
|
import com.thoughtworks.xstream.annotations.XStreamAlias; |
||||||
|
import lombok.Data; |
||||||
|
|
||||||
|
/** |
||||||
|
* 默认的结果封装类. |
||||||
|
* <pre> |
||||||
|
* |
||||||
|
* xml返回结果: |
||||||
|
* <response> |
||||||
|
* <code>50</code> |
||||||
|
* <msg>Remote service error</msg> |
||||||
|
* <sub_code>isv.invalid-parameter</sub_code> |
||||||
|
* <sub_msg>非法参数</sub_msg> |
||||||
|
* </response> |
||||||
|
* 成功情况: |
||||||
|
* <response> |
||||||
|
* <code>0</code> |
||||||
|
* <msg>成功消息</msg> |
||||||
|
* <data> |
||||||
|
* ...返回内容 |
||||||
|
* </data> |
||||||
|
* </response> |
||||||
|
* |
||||||
|
* json返回格式: |
||||||
|
* { |
||||||
|
* "code":"50", |
||||||
|
* "msg":"Remote service error", |
||||||
|
* "sub_code":"isv.invalid-parameter", |
||||||
|
* "sub_msg":"非法参数" |
||||||
|
* } |
||||||
|
* 成功情况: |
||||||
|
* { |
||||||
|
* "code":"0", |
||||||
|
* "msg":"成功消息内容。。。", |
||||||
|
* "data":{ |
||||||
|
* ...返回内容 |
||||||
|
* } |
||||||
|
* } |
||||||
|
* </pre> |
||||||
|
* <p> |
||||||
|
* 字段说明: |
||||||
|
* code:网关异常码 <br> |
||||||
|
* msg:网关异常信息 <br> |
||||||
|
* sub_code:业务异常码 <br> |
||||||
|
* sub_msg:业务异常信息 <br> |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
@XStreamAlias("response") |
||||||
|
@Data |
||||||
|
public class ApiResult { |
||||||
|
|
||||||
|
/** |
||||||
|
* 网关异常信息 |
||||||
|
*/ |
||||||
|
private String msg; |
||||||
|
|
||||||
|
/** |
||||||
|
* 业务异常码 |
||||||
|
*/ |
||||||
|
private String sub_msg; |
||||||
|
|
||||||
|
/** |
||||||
|
* 业务异常信息 |
||||||
|
*/ |
||||||
|
private String sub_code; |
||||||
|
|
||||||
|
/** |
||||||
|
* 返回结果 |
||||||
|
*/ |
||||||
|
private Object data; |
||||||
|
} |
@ -0,0 +1,73 @@ |
|||||||
|
package com.gitee.sop.bookweb.controller; |
||||||
|
|
||||||
|
import com.gitee.book.api.domain.Book; |
||||||
|
import com.gitee.sop.bookweb.consumer.StoryServiceConsumer; |
||||||
|
import com.gitee.sop.bookweb.param.BookParam; |
||||||
|
import com.gitee.sop.story.api.domain.Story; |
||||||
|
import com.hhdd.book.api.service.BookService; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.web.bind.annotation.RequestBody; |
||||||
|
import org.springframework.web.bind.annotation.RequestMapping; |
||||||
|
import org.springframework.web.bind.annotation.RestController; |
||||||
|
|
||||||
|
import java.util.Arrays; |
||||||
|
|
||||||
|
/** |
||||||
|
* book服务 |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
@RestController |
||||||
|
public class BookController implements BookService { |
||||||
|
|
||||||
|
@Autowired |
||||||
|
StoryServiceConsumer storyServiceConsumer; |
||||||
|
|
||||||
|
@Override |
||||||
|
public Book getBook(int id) { |
||||||
|
Book book = new Book(); |
||||||
|
book.setId(id); |
||||||
|
book.setName("汪汪队"); |
||||||
|
return book; |
||||||
|
} |
||||||
|
|
||||||
|
@RequestMapping("listBookAndStory") |
||||||
|
public Object listBookAndStory(int id) { |
||||||
|
Book book = new Book(); |
||||||
|
book.setId(id); |
||||||
|
book.setName("汪汪队"); |
||||||
|
|
||||||
|
// 调用story服务
|
||||||
|
Story story = storyServiceConsumer.getStory(id); |
||||||
|
|
||||||
|
return Arrays.asList(book, story); |
||||||
|
} |
||||||
|
|
||||||
|
@RequestMapping("getBook2") |
||||||
|
public Object getBookError(int id) { |
||||||
|
if (id == 0) { |
||||||
|
throw new RuntimeException("id不能为空"); |
||||||
|
} |
||||||
|
Book book = new Book(); |
||||||
|
book.setId(id); |
||||||
|
book.setName("汪汪队"); |
||||||
|
return Arrays.asList(book); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@RequestMapping("getBook3") |
||||||
|
public Object getBook3(@RequestBody BookParam param) { |
||||||
|
if (param.getId() == 0) { |
||||||
|
throw new RuntimeException("id不能为空"); |
||||||
|
} |
||||||
|
Book book = new Book(); |
||||||
|
book.setId(param.getId()); |
||||||
|
book.setName("小马宝莉"); |
||||||
|
|
||||||
|
ApiResult apiResult = new ApiResult(); |
||||||
|
apiResult.setData(book); |
||||||
|
return apiResult; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
package com.gitee.sop.bookweb.param; |
||||||
|
|
||||||
|
import lombok.Data; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
@Data |
||||||
|
public class BookParam { |
||||||
|
private int id; |
||||||
|
} |
@ -0,0 +1,6 @@ |
|||||||
|
server.port=3333 |
||||||
|
spring.application.name=book-service |
||||||
|
eureka.host=localhost |
||||||
|
eureka.port=1111 |
||||||
|
eureka.client.serviceUrl.defaultZone=http://${eureka.host}:${eureka.port}/eureka/ |
||||||
|
|
@ -0,0 +1,17 @@ |
|||||||
|
package com.gitee.sop.bookweb; |
||||||
|
|
||||||
|
import org.junit.Test; |
||||||
|
import org.junit.runner.RunWith; |
||||||
|
import org.springframework.boot.test.context.SpringBootTest; |
||||||
|
import org.springframework.test.context.junit4.SpringRunner; |
||||||
|
|
||||||
|
@RunWith(SpringRunner.class) |
||||||
|
@SpringBootTest |
||||||
|
public class SopBookApplicationTests { |
||||||
|
|
||||||
|
@Test |
||||||
|
public void contextLoads() { |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
@ -0,0 +1,25 @@ |
|||||||
|
/target/ |
||||||
|
!.mvn/wrapper/maven-wrapper.jar |
||||||
|
|
||||||
|
### STS ### |
||||||
|
.apt_generated |
||||||
|
.classpath |
||||||
|
.factorypath |
||||||
|
.project |
||||||
|
.settings |
||||||
|
.springBeans |
||||||
|
.sts4-cache |
||||||
|
|
||||||
|
### IntelliJ IDEA ### |
||||||
|
.idea |
||||||
|
*.iws |
||||||
|
*.iml |
||||||
|
*.ipr |
||||||
|
|
||||||
|
### NetBeans ### |
||||||
|
/nbproject/private/ |
||||||
|
/nbbuild/ |
||||||
|
/dist/ |
||||||
|
/nbdist/ |
||||||
|
/.nb-gradle/ |
||||||
|
/build/ |
@ -0,0 +1,131 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||||
|
<modelVersion>4.0.0</modelVersion> |
||||||
|
<parent> |
||||||
|
<groupId>org.springframework.boot</groupId> |
||||||
|
<artifactId>spring-boot-starter-parent</artifactId> |
||||||
|
<version>2.1.2.RELEASE</version> |
||||||
|
<relativePath/> <!-- lookup parent from repository --> |
||||||
|
</parent> |
||||||
|
<groupId>com.gitee.sop</groupId> |
||||||
|
<artifactId>sop-gateway-common</artifactId> |
||||||
|
<version>1.0.0-SNAPSHOT</version> |
||||||
|
<name>sop-gateway-common</name> |
||||||
|
<description>sop-gateway-common</description> |
||||||
|
|
||||||
|
<properties> |
||||||
|
<java.version>1.8</java.version> |
||||||
|
<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version> |
||||||
|
<!-- Logging --> |
||||||
|
<logback.version>1.0.13</logback.version> |
||||||
|
<slf4j.version>1.7.5</slf4j.version> |
||||||
|
|
||||||
|
<!-- Test --> |
||||||
|
<junit.version>4.11</junit.version> |
||||||
|
|
||||||
|
<fastjson.version>1.2.15</fastjson.version> |
||||||
|
<commons-io.version>2.5</commons-io.version> |
||||||
|
<commons-fileupload.version>1.3.3</commons-fileupload.version> |
||||||
|
<commons-collection.version>3.2.2</commons-collection.version> |
||||||
|
<xstream.version>1.4.7</xstream.version> |
||||||
|
<velocity.version>1.7</velocity.version> |
||||||
|
<hibernate-validator.version>6.0.10.Final</hibernate-validator.version> |
||||||
|
<oltu.version>0.31</oltu.version> |
||||||
|
<jwt.version>3.2.0</jwt.version> |
||||||
|
<guava.version>18.0</guava.version> |
||||||
|
<spring-data-redis.version>1.8.8.RELEASE</spring-data-redis.version> |
||||||
|
<netty.version>4.1.25.Final</netty.version> |
||||||
|
<jboss-marshalling-serial.version>2.0.5.Final</jboss-marshalling-serial.version> |
||||||
|
<itextpdf.version>5.5.12</itextpdf.version> |
||||||
|
<xmlworker.version>5.5.8</xmlworker.version> |
||||||
|
<itext-asian.version>5.2.0</itext-asian.version> |
||||||
|
<asm.version>6.2</asm.version> |
||||||
|
<commons-codec.version>1.11</commons-codec.version> |
||||||
|
</properties> |
||||||
|
|
||||||
|
<dependencies> |
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework.cloud</groupId> |
||||||
|
<artifactId>spring-cloud-starter-netflix-zuul</artifactId> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework.boot</groupId> |
||||||
|
<artifactId>spring-boot-starter-test</artifactId> |
||||||
|
<scope>test</scope> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>com.thoughtworks.xstream</groupId> |
||||||
|
<artifactId>xstream</artifactId> |
||||||
|
<version>${xstream.version}</version> |
||||||
|
<scope>compile</scope> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>com.alibaba</groupId> |
||||||
|
<artifactId>fastjson</artifactId> |
||||||
|
<version>${fastjson.version}</version> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework.boot</groupId> |
||||||
|
<artifactId>spring-boot-starter-data-redis</artifactId> |
||||||
|
</dependency> |
||||||
|
<!-- commons --> |
||||||
|
<dependency> |
||||||
|
<groupId>commons-collections</groupId> |
||||||
|
<artifactId>commons-collections</artifactId> |
||||||
|
<version>${commons-collection.version}</version> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>commons-io</groupId> |
||||||
|
<artifactId>commons-io</artifactId> |
||||||
|
<version>${commons-io.version}</version> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>commons-fileupload</groupId> |
||||||
|
<artifactId>commons-fileupload</artifactId> |
||||||
|
<version>${commons-fileupload.version}</version> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>commons-codec</groupId> |
||||||
|
<artifactId>commons-codec</artifactId> |
||||||
|
<version>${commons-codec.version}</version> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.projectlombok</groupId> |
||||||
|
<artifactId>lombok</artifactId> |
||||||
|
<version>1.18.4</version> |
||||||
|
<scope>provided</scope> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
</dependencies> |
||||||
|
|
||||||
|
<dependencyManagement> |
||||||
|
<dependencies> |
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework.cloud</groupId> |
||||||
|
<artifactId>spring-cloud-dependencies</artifactId> |
||||||
|
<version>${spring-cloud.version}</version> |
||||||
|
<type>pom</type> |
||||||
|
<scope>import</scope> |
||||||
|
</dependency> |
||||||
|
</dependencies> |
||||||
|
</dependencyManagement> |
||||||
|
|
||||||
|
<build> |
||||||
|
<plugins> |
||||||
|
<plugin> |
||||||
|
<groupId>org.springframework.boot</groupId> |
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId> |
||||||
|
</plugin> |
||||||
|
</plugins> |
||||||
|
</build> |
||||||
|
|
||||||
|
<repositories> |
||||||
|
<repository> |
||||||
|
<id>spring-milestones</id> |
||||||
|
<name>Spring Milestones</name> |
||||||
|
<url>https://repo.spring.io/milestone</url> |
||||||
|
</repository> |
||||||
|
</repositories> |
||||||
|
|
||||||
|
</project> |
@ -0,0 +1,3 @@ |
|||||||
|
# sop-gateway-common |
||||||
|
|
||||||
|
zuul通用组件,主要封装网关的一些功能。 |
@ -0,0 +1,116 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.bean; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.configuration.BaseZuulController; |
||||||
|
import com.gitee.sop.gatewaycommon.param.ApiParamParser; |
||||||
|
import com.gitee.sop.gatewaycommon.param.ParamParser; |
||||||
|
import com.gitee.sop.gatewaycommon.result.ApiResultExecutor; |
||||||
|
import com.gitee.sop.gatewaycommon.result.JsonResultSerializer; |
||||||
|
import com.gitee.sop.gatewaycommon.result.ResultExecutor; |
||||||
|
import com.gitee.sop.gatewaycommon.result.ResultSerializer; |
||||||
|
import com.gitee.sop.gatewaycommon.result.XmlResultSerializer; |
||||||
|
import com.gitee.sop.gatewaycommon.secret.AppSecretManager; |
||||||
|
import com.gitee.sop.gatewaycommon.secret.CacheAppSecretManager; |
||||||
|
import com.gitee.sop.gatewaycommon.session.ApiSessionManager; |
||||||
|
import com.gitee.sop.gatewaycommon.session.SessionManager; |
||||||
|
import com.gitee.sop.gatewaycommon.validate.ApiEncrypter; |
||||||
|
import com.gitee.sop.gatewaycommon.validate.ApiSigner; |
||||||
|
import com.gitee.sop.gatewaycommon.validate.ApiValidator; |
||||||
|
import com.gitee.sop.gatewaycommon.validate.Encrypter; |
||||||
|
import com.gitee.sop.gatewaycommon.validate.Signer; |
||||||
|
import com.gitee.sop.gatewaycommon.validate.Validator; |
||||||
|
import lombok.Data; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
@Data |
||||||
|
public class ApiConfig { |
||||||
|
|
||||||
|
private static ApiConfig instance = new ApiConfig(); |
||||||
|
|
||||||
|
private ApiConfig() { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 合并结果处理 |
||||||
|
*/ |
||||||
|
private ResultExecutor resultExecutor = new ApiResultExecutor(); |
||||||
|
|
||||||
|
/** |
||||||
|
* json序列化 |
||||||
|
*/ |
||||||
|
private ResultSerializer jsonResultSerializer = new JsonResultSerializer(); |
||||||
|
/** |
||||||
|
* xml序列化 |
||||||
|
*/ |
||||||
|
private ResultSerializer xmlResultSerializer = new XmlResultSerializer(); |
||||||
|
|
||||||
|
/** |
||||||
|
* app秘钥管理 |
||||||
|
*/ |
||||||
|
private AppSecretManager appSecretManager = new CacheAppSecretManager(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 加密工具 |
||||||
|
*/ |
||||||
|
private Encrypter encrypter = new ApiEncrypter(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 签名工具 |
||||||
|
*/ |
||||||
|
private Signer signer = new ApiSigner(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 参数解析 |
||||||
|
*/ |
||||||
|
private ParamParser paramParser = new ApiParamParser(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 验证 |
||||||
|
*/ |
||||||
|
private Validator validator = new ApiValidator(); |
||||||
|
|
||||||
|
/** |
||||||
|
* session管理 |
||||||
|
*/ |
||||||
|
private SessionManager sessionManager = new ApiSessionManager(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 错误模块 |
||||||
|
*/ |
||||||
|
private List<String> i18nModules = new ArrayList<String>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 基础Controller |
||||||
|
*/ |
||||||
|
private BaseZuulController baseZuulController = new BaseZuulController(); |
||||||
|
|
||||||
|
|
||||||
|
// -------- fields ---------
|
||||||
|
/** |
||||||
|
* 忽略验证 |
||||||
|
*/ |
||||||
|
private boolean ignoreValidate; |
||||||
|
|
||||||
|
/** |
||||||
|
* 超时时间 |
||||||
|
*/ |
||||||
|
private int timeoutSeconds = 60 * 5; |
||||||
|
|
||||||
|
public void addAppSecret(Map<String, String> appSecretPair) { |
||||||
|
this.appSecretManager.addAppSecret(appSecretPair); |
||||||
|
} |
||||||
|
|
||||||
|
public static ApiConfig getInstance() { |
||||||
|
return instance; |
||||||
|
} |
||||||
|
|
||||||
|
public static void setInstance(ApiConfig apiConfig) { |
||||||
|
instance = apiConfig; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,186 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.bean; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.param.UploadContext; |
||||||
|
import com.netflix.zuul.context.RequestContext; |
||||||
|
import com.gitee.sop.gatewaycommon.param.ApiParam; |
||||||
|
import com.gitee.sop.gatewaycommon.session.SessionManager; |
||||||
|
|
||||||
|
import javax.servlet.ServletContext; |
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
import javax.servlet.http.HttpServletResponse; |
||||||
|
import javax.servlet.http.HttpSession; |
||||||
|
import java.util.Locale; |
||||||
|
|
||||||
|
/** |
||||||
|
* 应用上下文,方便获取信息 |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
* |
||||||
|
*/ |
||||||
|
public class ApiContext { |
||||||
|
private static final String ATTR_PARAM = "zuul.common.api.param"; |
||||||
|
private static final String ATTR_UPLOAD_CONTEXT = "zuul.common.api.upload_context"; |
||||||
|
|
||||||
|
private ApiContext(){} |
||||||
|
|
||||||
|
private static void setAttr(String name, Object val) { |
||||||
|
HttpServletRequest request = getRequest(); |
||||||
|
if (request != null) { |
||||||
|
request.setAttribute(name, val); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static Object getAttr(String name) { |
||||||
|
HttpServletRequest request = getRequest(); |
||||||
|
if (request == null) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
return request.getAttribute(name); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取随机码 |
||||||
|
* @return 返回随机码 |
||||||
|
*/ |
||||||
|
public static String getRandomKey() { |
||||||
|
HttpSession session = getSession(); |
||||||
|
if (session == null) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
return (String) session.getAttribute(SopConstants.RANDOM_KEY_NAME); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 获取HttpServletRequest |
||||||
|
* |
||||||
|
* @return HttpServletRequest |
||||||
|
*/ |
||||||
|
public static HttpServletRequest getRequest() { |
||||||
|
return RequestContext.getCurrentContext().getRequest(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 返回默认的HttpServletRequest.getSession(); |
||||||
|
* |
||||||
|
* @return 没有返回null |
||||||
|
*/ |
||||||
|
public static HttpSession getSession() { |
||||||
|
HttpServletRequest req = getRequest(); |
||||||
|
if (req == null) { |
||||||
|
return null; |
||||||
|
} else { |
||||||
|
return req.getSession(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取session管理器 |
||||||
|
* @return 返回SessionManager |
||||||
|
*/ |
||||||
|
public static SessionManager getSessionManager() { |
||||||
|
return getApiConfig().getSessionManager(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 返回自定义的session,被SessionManager管理 |
||||||
|
* |
||||||
|
* @return 如果sessionId为null,则返回null |
||||||
|
*/ |
||||||
|
public static HttpSession getManagedSession() { |
||||||
|
String sessionId = getSessionId(); |
||||||
|
if (sessionId != null) { |
||||||
|
return getSessionManager().getSession(sessionId); |
||||||
|
} else { |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 同getSessionId() |
||||||
|
* @return 返回accessToken,没有返回null |
||||||
|
*/ |
||||||
|
public static String getAccessToken() { |
||||||
|
return getSessionId(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 获取登陆的token |
||||||
|
* |
||||||
|
* @return 没有返回null |
||||||
|
*/ |
||||||
|
public static String getSessionId() { |
||||||
|
ApiParam apiParam = getApiParam(); |
||||||
|
if (apiParam == null) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
return apiParam.fetchAccessToken(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取本地化,从HttpServletRequest中获取,没有则返回Locale.SIMPLIFIED_CHINESE |
||||||
|
* |
||||||
|
* @return Locale |
||||||
|
*/ |
||||||
|
public static Locale getLocale() { |
||||||
|
HttpServletRequest req = getRequest(); |
||||||
|
if (req == null) { |
||||||
|
return Locale.SIMPLIFIED_CHINESE; |
||||||
|
} |
||||||
|
return req.getLocale(); |
||||||
|
} |
||||||
|
|
||||||
|
public static void setApiParam(ApiParam apiParam) { |
||||||
|
setAttr(ATTR_PARAM, apiParam); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取系统参数 |
||||||
|
* |
||||||
|
* @return 返回ApiParam |
||||||
|
*/ |
||||||
|
public static ApiParam getApiParam() { |
||||||
|
return (ApiParam) getAttr(ATTR_PARAM); |
||||||
|
} |
||||||
|
|
||||||
|
public static ApiConfig getApiConfig() { |
||||||
|
return ApiConfig.getInstance(); |
||||||
|
} |
||||||
|
|
||||||
|
public static void setApiConfig(ApiConfig apiConfig) { |
||||||
|
ApiConfig.setInstance(apiConfig); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public static ServletContext getServletContext() { |
||||||
|
ServletContext ctx = null; |
||||||
|
HttpSession session = getSession(); |
||||||
|
if (session != null) { |
||||||
|
ctx = session.getServletContext(); |
||||||
|
} |
||||||
|
return ctx; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取上传文件,如果客户端有文件上传,从这里取。 |
||||||
|
* @return 如果没有文件上传,返回null |
||||||
|
*/ |
||||||
|
public static UploadContext getUploadContext() { |
||||||
|
return (UploadContext) getAttr(ATTR_UPLOAD_CONTEXT); |
||||||
|
} |
||||||
|
|
||||||
|
public static void setUploadContext(UploadContext uploadCtx) { |
||||||
|
setAttr(ATTR_UPLOAD_CONTEXT, uploadCtx); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取response |
||||||
|
* @return 返回response |
||||||
|
*/ |
||||||
|
public static HttpServletResponse getResponse() { |
||||||
|
return RequestContext.getCurrentContext().getResponse(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.bean; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public enum RequestMode { |
||||||
|
|
||||||
|
/** |
||||||
|
* 数字签名请求模式 |
||||||
|
*/ |
||||||
|
SIGNATURE, |
||||||
|
/** |
||||||
|
* 公私钥加密模式,这样请求和返回的数据都经过加密处理 |
||||||
|
*/ |
||||||
|
ENCRYPT |
||||||
|
|
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.bean; |
||||||
|
|
||||||
|
import lombok.Data; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
@Data |
||||||
|
public class ServiceApiInfo { |
||||||
|
private String md5; |
||||||
|
private String appName; |
||||||
|
private List<ApiMeta> apis; |
||||||
|
|
||||||
|
@Data |
||||||
|
public static class ApiMeta { |
||||||
|
/** 接口名 */ |
||||||
|
private String name; |
||||||
|
/** 请求path */ |
||||||
|
private String path; |
||||||
|
/** 版本号 */ |
||||||
|
private String version; |
||||||
|
|
||||||
|
public String fetchNameVersion() { |
||||||
|
return name + version; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.bean; |
||||||
|
|
||||||
|
import java.nio.charset.Charset; |
||||||
|
import java.nio.charset.StandardCharsets; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class SopConstants { |
||||||
|
|
||||||
|
public static final String NULL = "null"; |
||||||
|
public static final String RANDOM_KEY_NAME = "ssl_randomKey"; |
||||||
|
public static final Charset CHARSET_UTF8 = StandardCharsets.UTF_8; |
||||||
|
public static final String FORMAT_JSON = "json"; |
||||||
|
public static final String FORMAT_XML = "xml"; |
||||||
|
public static final String AUTHORIZATION = "Authorization"; |
||||||
|
public static final String PREFIX_BEARER = "Bearer "; |
||||||
|
public static final String YMDHMS = "yyyy-MM-dd HH:mm:ss"; |
||||||
|
|
||||||
|
public static final String DEFAULT_SIGN_METHOD = "md5"; |
||||||
|
|
||||||
|
public static final String CONTENT_TYPE_NAME = "Content-Type"; |
||||||
|
public static final String CONTENT_TYPE_JSON = "application/json;charset=UTF-8"; |
||||||
|
|
||||||
|
public static final String LINE = "\n"; |
||||||
|
|
||||||
|
public static final String EMPTY_JSON = "{}"; |
||||||
|
|
||||||
|
public static final String SORT_DESC = "DESC"; |
||||||
|
|
||||||
|
public static final String REST_PARAM_NAME = "_REST_PARAM_NAME_"; |
||||||
|
public static final String REST_PARAM_VERSION = "_REST_PARAM_VERSION_"; |
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.configuration; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.bean.ApiContext; |
||||||
|
import com.gitee.sop.gatewaycommon.validate.alipay.AlipaySigner; |
||||||
|
|
||||||
|
/** |
||||||
|
* 具备支付宝开放平台能力配置 https://docs.open.alipay.com/api
|
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class AlipayZuulConfiguration extends BaseZuulConfiguration { |
||||||
|
|
||||||
|
static { |
||||||
|
ApiContext.getApiConfig().setSigner(new AlipaySigner()); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,112 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.configuration; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.bean.ApiContext; |
||||||
|
import com.gitee.sop.gatewaycommon.filter.ErrorFilter; |
||||||
|
import com.gitee.sop.gatewaycommon.filter.PostResultFilter; |
||||||
|
import com.gitee.sop.gatewaycommon.filter.PreValidateFilter; |
||||||
|
import com.gitee.sop.gatewaycommon.manager.ApiMetaChangeListener; |
||||||
|
import com.gitee.sop.gatewaycommon.manager.ApiMetaContext; |
||||||
|
import com.gitee.sop.gatewaycommon.manager.ApiMetaManager; |
||||||
|
import com.gitee.sop.gatewaycommon.manager.DefaultApiMetaContext; |
||||||
|
import com.gitee.sop.gatewaycommon.manager.DefaultApiMetaManager; |
||||||
|
import com.gitee.sop.gatewaycommon.manager.SopRouteLocator; |
||||||
|
import com.gitee.sop.gatewaycommon.message.ErrorFactory; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.boot.autoconfigure.web.ServerProperties; |
||||||
|
import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper; |
||||||
|
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties; |
||||||
|
import org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter; |
||||||
|
import org.springframework.context.annotation.Bean; |
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate; |
||||||
|
import org.springframework.data.redis.listener.PatternTopic; |
||||||
|
import org.springframework.data.redis.listener.RedisMessageListenerContainer; |
||||||
|
|
||||||
|
import javax.annotation.PostConstruct; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class BaseZuulConfiguration { |
||||||
|
|
||||||
|
public static final String API_CHANGE_CHANNEL = "channel.sop.api.change"; |
||||||
|
|
||||||
|
|
||||||
|
@Autowired |
||||||
|
protected ZuulProperties zuulProperties; |
||||||
|
|
||||||
|
@Autowired |
||||||
|
protected ServerProperties server; |
||||||
|
|
||||||
|
@Autowired |
||||||
|
protected ApiMetaManager apiMetaManager; |
||||||
|
|
||||||
|
@Bean |
||||||
|
public ApiMetaContext apiMetaContext() { |
||||||
|
return new DefaultApiMetaContext(); |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
public ApiMetaManager apiMetaManager(StringRedisTemplate stringRedisTemplate, ApiMetaContext apiMetaContext) { |
||||||
|
return new DefaultApiMetaManager(stringRedisTemplate, apiMetaContext); |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
PreValidateFilter preValidateFilter() { |
||||||
|
return new PreValidateFilter(); |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
public PreDecorationFilter preDecorationFilter(ApiMetaContext apiMetaContext, ProxyRequestHelper proxyRequestHelper) { |
||||||
|
SopRouteLocator routeLocator = new SopRouteLocator(apiMetaContext); |
||||||
|
return new PreDecorationFilter(routeLocator, |
||||||
|
this.server.getServlet().getContextPath(), |
||||||
|
this.zuulProperties, |
||||||
|
proxyRequestHelper); |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
ErrorFilter errorFilter() { |
||||||
|
return new ErrorFilter(); |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
PostResultFilter postResultFilter() { |
||||||
|
return new PostResultFilter(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 配置redis事件订阅 |
||||||
|
* |
||||||
|
* @param apiMetaManager |
||||||
|
* @param redisTemplate |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
@Bean |
||||||
|
RedisMessageListenerContainer container(ApiMetaManager apiMetaManager, StringRedisTemplate redisTemplate) { |
||||||
|
RedisMessageListenerContainer container = new RedisMessageListenerContainer(); |
||||||
|
container.setConnectionFactory(redisTemplate.getConnectionFactory()); |
||||||
|
ApiMetaChangeListener apiMetaChangeListener = new ApiMetaChangeListener(apiMetaManager, redisTemplate); |
||||||
|
container.addMessageListener(apiMetaChangeListener, new PatternTopic(API_CHANGE_CHANNEL)); |
||||||
|
return container; |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
BaseZuulController baseZuulController() { |
||||||
|
return ApiContext.getApiConfig().getBaseZuulController(); |
||||||
|
} |
||||||
|
|
||||||
|
@PostConstruct |
||||||
|
public void after() { |
||||||
|
doAfter(); |
||||||
|
} |
||||||
|
|
||||||
|
protected void doAfter() { |
||||||
|
initMessage(); |
||||||
|
apiMetaManager.refresh(); |
||||||
|
} |
||||||
|
|
||||||
|
protected void initMessage() { |
||||||
|
ErrorFactory.initMessageSource(ApiContext.getApiConfig().getI18nModules()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,44 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.configuration; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.bean.ApiContext; |
||||||
|
import com.gitee.sop.gatewaycommon.result.ResultExecutor; |
||||||
|
import com.netflix.zuul.context.RequestContext; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.springframework.boot.web.servlet.error.ErrorController; |
||||||
|
import org.springframework.stereotype.Controller; |
||||||
|
import org.springframework.web.bind.annotation.RequestMapping; |
||||||
|
import org.springframework.web.bind.annotation.ResponseBody; |
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
import javax.servlet.http.HttpServletResponse; |
||||||
|
|
||||||
|
/** |
||||||
|
* 处理网关自身异常 |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
@Controller |
||||||
|
@Slf4j |
||||||
|
public class BaseZuulController implements ErrorController { |
||||||
|
|
||||||
|
public static final String ERROR_PATH = "/error"; |
||||||
|
|
||||||
|
// 错误最终会到这里来
|
||||||
|
@RequestMapping(ERROR_PATH) |
||||||
|
@ResponseBody |
||||||
|
public Object error(HttpServletRequest request, HttpServletResponse response) { |
||||||
|
RequestContext ctx = RequestContext.getCurrentContext(); |
||||||
|
Throwable throwable = ctx.getThrowable(); |
||||||
|
return this.buildResult(throwable); |
||||||
|
} |
||||||
|
|
||||||
|
protected Object buildResult(Throwable throwable) { |
||||||
|
ResultExecutor resultExecutor = ApiContext.getApiConfig().getResultExecutor(); |
||||||
|
return resultExecutor.mergeError(throwable); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getErrorPath() { |
||||||
|
return ERROR_PATH; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.configuration; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.bean.ApiContext; |
||||||
|
import com.gitee.sop.gatewaycommon.param.ParamNames; |
||||||
|
import com.gitee.sop.gatewaycommon.validate.taobao.TaobaoSigner; |
||||||
|
|
||||||
|
/** |
||||||
|
* 具备淘宝开放平台能力配置 |
||||||
|
* 淘宝开放平台:http://open.taobao.com/doc.htm
|
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class TaobaoZuulConfiguration extends BaseZuulConfiguration { |
||||||
|
|
||||||
|
static { |
||||||
|
ParamNames.APP_KEY_NAME = "app_key"; |
||||||
|
ParamNames.SIGN_TYPE_NAME = "sign_method"; |
||||||
|
ParamNames.VERSION_NAME = "v"; |
||||||
|
ParamNames.APP_AUTH_TOKEN_NAME = "session"; |
||||||
|
|
||||||
|
ApiContext.getApiConfig().setSigner(new TaobaoSigner()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.easyopen; |
||||||
|
|
||||||
|
import org.springframework.cloud.netflix.zuul.filters.Route; |
||||||
|
|
||||||
|
import java.util.Collections; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class EasyopenRoute extends Route { |
||||||
|
public EasyopenRoute(String id, String path, String location, String prefix, Boolean retryable, Set<String> ignoredHeaders) { |
||||||
|
super(id, path, location, prefix, retryable, ignoredHeaders); |
||||||
|
} |
||||||
|
|
||||||
|
public EasyopenRoute(String id, String location) { |
||||||
|
this(id, "/" + location + "/", location, "", false, Collections.emptySet()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,74 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.easyopen; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.filter.ErrorFilter; |
||||||
|
import com.gitee.sop.gatewaycommon.filter.PostResultFilter; |
||||||
|
import com.gitee.sop.gatewaycommon.easyopen.filter.PostEasyopenResultFilter; |
||||||
|
import com.gitee.sop.gatewaycommon.filter.PreValidateFilter; |
||||||
|
import com.gitee.sop.gatewaycommon.manager.ApiMetaContext; |
||||||
|
import com.gitee.sop.gatewaycommon.manager.ApiMetaManager; |
||||||
|
import com.gitee.sop.gatewaycommon.manager.DefaultApiMetaContext; |
||||||
|
import com.gitee.sop.gatewaycommon.manager.DefaultApiMetaManager; |
||||||
|
import com.gitee.sop.gatewaycommon.manager.SopRouteLocator; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.boot.autoconfigure.web.ServerProperties; |
||||||
|
import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper; |
||||||
|
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties; |
||||||
|
import org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter; |
||||||
|
import org.springframework.context.annotation.Bean; |
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate; |
||||||
|
|
||||||
|
import javax.annotation.PostConstruct; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class EasyopenZuulConfig { |
||||||
|
|
||||||
|
@Autowired |
||||||
|
protected ZuulProperties zuulProperties; |
||||||
|
|
||||||
|
@Autowired |
||||||
|
protected ServerProperties server; |
||||||
|
|
||||||
|
@Autowired |
||||||
|
protected ApiMetaManager apiMetaManager; |
||||||
|
|
||||||
|
@Bean |
||||||
|
public ApiMetaContext apiMetaContext() { |
||||||
|
return new DefaultApiMetaContext(); |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
public ApiMetaManager apiMetaManager(StringRedisTemplate stringRedisTemplate, ApiMetaContext apiMetaContext) { |
||||||
|
return new DefaultApiMetaManager(stringRedisTemplate, apiMetaContext); |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
public PreDecorationFilter preDecorationFilter(ApiMetaContext apiMetaContext, ProxyRequestHelper proxyRequestHelper) { |
||||||
|
SopRouteLocator routeLocator = new SopRouteLocator(apiMetaContext); |
||||||
|
return new PreDecorationFilter(routeLocator, |
||||||
|
this.server.getServlet().getContextPath(), |
||||||
|
this.zuulProperties, |
||||||
|
proxyRequestHelper); |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
PreValidateFilter preValidateFilter() { |
||||||
|
return new PreValidateFilter(); |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
ErrorFilter errorFilter() { |
||||||
|
return new ErrorFilter(); |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
PostResultFilter postResultFilter() { |
||||||
|
return new PostEasyopenResultFilter(); |
||||||
|
} |
||||||
|
|
||||||
|
@PostConstruct |
||||||
|
public void after() { |
||||||
|
apiMetaManager.refresh(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,46 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.easyopen.filter; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.filter.PostResultFilter; |
||||||
|
|
||||||
|
/** |
||||||
|
* 合并微服务结果,统一返回格式 |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class PostEasyopenResultFilter extends PostResultFilter { |
||||||
|
private static final String EASYOPEN_SUCCESS_CODE = "0"; |
||||||
|
//
|
||||||
|
// protected Result processServiceResult(InputStream responseDataStream, ResultBuilder resultBuilder) throws IOException {
|
||||||
|
// String responseBody = IOUtils.toString(responseDataStream, SopConstants.CHARSET_UTF8);
|
||||||
|
// ApiResult result = this.parseApiResult(responseBody);
|
||||||
|
// Result finalResult;
|
||||||
|
// if (EASYOPEN_SUCCESS_CODE.equals(result.getCode())) {
|
||||||
|
// finalResult = resultBuilder.buildSuccessResult(GATEWAY_SUCCESS_CODE, null, result.getData());
|
||||||
|
// } else {
|
||||||
|
// // 业务出错
|
||||||
|
// finalResult = resultBuilder.buildServiceError(result.getCode(), result.getMsg());
|
||||||
|
// }
|
||||||
|
// return finalResult;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// protected ApiResult parseApiResult(String responseBody) {
|
||||||
|
// ApiParam apiParam = ApiContext.getApiParam();
|
||||||
|
// String format = apiParam.fetchFormat();
|
||||||
|
// return SopConstants.FORMAT_XML.equalsIgnoreCase(format)
|
||||||
|
// ? XmlUtil.unserialize(responseBody, ApiResult.class)
|
||||||
|
// : JSON.parseObject(responseBody, ApiResult.class);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// protected ResultSerializer buildResultSerializer(HttpServletRequest request, ApiConfig apiConfig) {
|
||||||
|
// ApiParam apiParam = ApiContext.getApiParam();
|
||||||
|
// String format = apiParam.fetchFormat();
|
||||||
|
// if (SopConstants.FORMAT_JSON.equalsIgnoreCase(format)) {
|
||||||
|
// return apiConfig.getJsonResultSerializer();
|
||||||
|
// } else if (SopConstants.FORMAT_XML.equalsIgnoreCase(format)) {
|
||||||
|
// // xml格式输出
|
||||||
|
// return apiConfig.getXmlResultSerializer();
|
||||||
|
// } else {
|
||||||
|
// throw ErrorEnum.isv_invalid_format.getErrorMeta().getException();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.exception; |
||||||
|
|
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.message.Error; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class ApiException extends RuntimeException { |
||||||
|
|
||||||
|
private Error error; |
||||||
|
|
||||||
|
public ApiException(Throwable cause, Error error) { |
||||||
|
super(cause); |
||||||
|
this.error = error; |
||||||
|
} |
||||||
|
|
||||||
|
public ApiException(Error error) { |
||||||
|
super(error.toString()); |
||||||
|
this.error = error; |
||||||
|
} |
||||||
|
|
||||||
|
public Error getError() { |
||||||
|
return error; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,96 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.filter; |
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON; |
||||||
|
import com.gitee.sop.gatewaycommon.result.ApiResult; |
||||||
|
import com.netflix.zuul.ZuulFilter; |
||||||
|
import com.netflix.zuul.context.RequestContext; |
||||||
|
import com.netflix.zuul.exception.ZuulException; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public abstract class BaseZuulFilter extends ZuulFilter { |
||||||
|
|
||||||
|
protected Logger log = LoggerFactory.getLogger(getClass()); |
||||||
|
|
||||||
|
private Integer filterOrder; |
||||||
|
|
||||||
|
protected abstract FilterType getFilterType(); |
||||||
|
|
||||||
|
protected abstract int getFilterOrder(); |
||||||
|
|
||||||
|
protected abstract Object doRun(RequestContext requestContext) throws ZuulException; |
||||||
|
|
||||||
|
/** |
||||||
|
* 设置过滤器顺序 |
||||||
|
* |
||||||
|
* @param filterOrder 顺序,值越小优先执行 |
||||||
|
* @return 返回自身对象 |
||||||
|
*/ |
||||||
|
public BaseZuulFilter order(int filterOrder) { |
||||||
|
this.filterOrder = filterOrder; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int filterOrder() { |
||||||
|
return filterOrder != null ? filterOrder : this.getFilterOrder(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String filterType() { |
||||||
|
return this.getFilterType().getType(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean shouldFilter() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object run() throws ZuulException { |
||||||
|
return this.doRun(RequestContext.getCurrentContext()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 过滤该请求,不往下级服务去转发请求,到此结束。并填充responseBody |
||||||
|
* |
||||||
|
* @param requestContext |
||||||
|
* @param result |
||||||
|
*/ |
||||||
|
public static void stopRouteAndReturn(RequestContext requestContext, Object result) { |
||||||
|
requestContext.setSendZuulResponse(false); |
||||||
|
requestContext.setResponseBody(JSON.toJSONString(result)); |
||||||
|
} |
||||||
|
|
||||||
|
public static void main(String[] args) { |
||||||
|
System.out.println(JSON.toJSONString(new ApiResult())); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* to classify a filter by type. Standard types in Zuul are "pre" for pre-routing filtering, |
||||||
|
* "route" for routing to an origin, "post" for post-routing filters, "error" for error handling. |
||||||
|
* We also support a "static" type for static responses see StaticResponseFilter. |
||||||
|
* Any filterType made be created or added and doRun by calling FilterProcessor.runFilters(type) |
||||||
|
*/ |
||||||
|
public enum FilterType { |
||||||
|
PRE("pre"), |
||||||
|
ROUTE("route"), |
||||||
|
POST("post"), |
||||||
|
ERROR("error"), |
||||||
|
STATIC("static"), |
||||||
|
; |
||||||
|
|
||||||
|
FilterType(String type) { |
||||||
|
this.type = type; |
||||||
|
} |
||||||
|
|
||||||
|
private String type; |
||||||
|
|
||||||
|
public String getType() { |
||||||
|
return type; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,59 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.filter; |
||||||
|
|
||||||
|
import com.netflix.zuul.FilterProcessor; |
||||||
|
import com.netflix.zuul.ZuulFilter; |
||||||
|
import com.netflix.zuul.context.RequestContext; |
||||||
|
import com.netflix.zuul.exception.ZuulException; |
||||||
|
import org.springframework.cloud.netflix.zuul.filters.post.SendErrorFilter; |
||||||
|
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class ErrorFilter extends SendErrorFilter { |
||||||
|
|
||||||
|
public static final String FAILED_FILTER = "failed.filter"; |
||||||
|
|
||||||
|
private int filterOrder = 10; |
||||||
|
|
||||||
|
public ErrorFilter() { |
||||||
|
initFilterProcessor(); |
||||||
|
} |
||||||
|
|
||||||
|
public void initFilterProcessor() { |
||||||
|
FilterProcessor instance = FilterProcessor.getInstance(); |
||||||
|
if (!(instance instanceof MyFilterProcessor)) { |
||||||
|
FilterProcessor.setProcessor(new MyFilterProcessor()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int filterOrder() { |
||||||
|
return filterOrder; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean shouldFilter() { |
||||||
|
// 判断:仅处理来自post过滤器引起的异常
|
||||||
|
RequestContext ctx = RequestContext.getCurrentContext(); |
||||||
|
ZuulFilter failedFilter = (ZuulFilter) ctx.get(FAILED_FILTER); |
||||||
|
return failedFilter != null && failedFilter.filterType().equals(FilterConstants.POST_TYPE); |
||||||
|
} |
||||||
|
|
||||||
|
public static class MyFilterProcessor extends FilterProcessor { |
||||||
|
@Override |
||||||
|
public Object processZuulFilter(ZuulFilter filter) throws ZuulException { |
||||||
|
try { |
||||||
|
return super.processZuulFilter(filter); |
||||||
|
} catch (ZuulException e) { |
||||||
|
RequestContext ctx = RequestContext.getCurrentContext(); |
||||||
|
ctx.set(FAILED_FILTER, filter); |
||||||
|
throw e; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void setFilterOrder(int filterOrder) { |
||||||
|
this.filterOrder = filterOrder; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.filter; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.bean.ApiConfig; |
||||||
|
import com.gitee.sop.gatewaycommon.bean.ApiContext; |
||||||
|
import com.gitee.sop.gatewaycommon.bean.SopConstants; |
||||||
|
import com.gitee.sop.gatewaycommon.result.ResultExecutor; |
||||||
|
import com.netflix.zuul.context.RequestContext; |
||||||
|
import com.netflix.zuul.exception.ZuulException; |
||||||
|
import org.apache.commons.io.IOUtils; |
||||||
|
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants; |
||||||
|
|
||||||
|
import java.io.InputStream; |
||||||
|
|
||||||
|
/** |
||||||
|
* 合并微服务结果,统一返回格式 |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class PostResultFilter extends BaseZuulFilter { |
||||||
|
|
||||||
|
@Override |
||||||
|
protected FilterType getFilterType() { |
||||||
|
return FilterType.POST; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected int getFilterOrder() { |
||||||
|
return FilterConstants.SEND_RESPONSE_FILTER_ORDER - 1; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected Object doRun(RequestContext requestContext) throws ZuulException { |
||||||
|
int responseStatusCode = requestContext.getResponseStatusCode(); |
||||||
|
InputStream responseDataStream = requestContext.getResponseDataStream(); |
||||||
|
ApiConfig apiConfig = ApiContext.getApiConfig(); |
||||||
|
ResultExecutor resultExecutor = apiConfig.getResultExecutor(); |
||||||
|
String serviceResult; |
||||||
|
try { |
||||||
|
serviceResult = IOUtils.toString(responseDataStream, SopConstants.CHARSET_UTF8); |
||||||
|
} catch (Exception e) { |
||||||
|
log.error("业务方无数据返回", e); |
||||||
|
serviceResult = SopConstants.EMPTY_JSON; |
||||||
|
} |
||||||
|
String finalResult = resultExecutor.mergeResult(responseStatusCode, serviceResult); |
||||||
|
requestContext.setResponseBody(finalResult); |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,41 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.filter; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.message.ErrorEnum; |
||||||
|
import com.netflix.zuul.context.RequestContext; |
||||||
|
import com.netflix.zuul.exception.ZuulException; |
||||||
|
import org.apache.commons.lang.StringUtils; |
||||||
|
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants; |
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class PreTokenFilter extends BaseZuulFilter { |
||||||
|
|
||||||
|
@Override |
||||||
|
protected FilterType getFilterType() { |
||||||
|
return FilterType.PRE; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected int getFilterOrder() { |
||||||
|
return FilterConstants.PRE_DECORATION_FILTER_ORDER + 1; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected Object doRun(RequestContext ctx) throws ZuulException { |
||||||
|
Object serviceId = ctx.get(FilterConstants.SERVICE_ID_KEY); |
||||||
|
log.info("serviceId:{}", serviceId); |
||||||
|
HttpServletRequest request = ctx.getRequest(); |
||||||
|
|
||||||
|
log.info("send {} request to {}", request.getMethod(), request.getRequestURL().toString()); |
||||||
|
|
||||||
|
String accessToken = request.getParameter("access_token"); |
||||||
|
if (StringUtils.isBlank(accessToken)) { |
||||||
|
throw ErrorEnum.AOP_INVALID_APP_AUTH_TOKEN.getErrorMeta().getException(); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.filter; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.bean.ApiContext; |
||||||
|
import com.gitee.sop.gatewaycommon.bean.ApiConfig; |
||||||
|
import com.gitee.sop.gatewaycommon.exception.ApiException; |
||||||
|
import com.gitee.sop.gatewaycommon.param.ApiParam; |
||||||
|
import com.gitee.sop.gatewaycommon.validate.Validator; |
||||||
|
import com.netflix.zuul.context.RequestContext; |
||||||
|
import com.netflix.zuul.exception.ZuulException; |
||||||
|
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants; |
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
|
||||||
|
/** |
||||||
|
* 前置校验 |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class PreValidateFilter extends BaseZuulFilter { |
||||||
|
@Override |
||||||
|
protected FilterType getFilterType() { |
||||||
|
return FilterType.PRE; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected int getFilterOrder() { |
||||||
|
// 在org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter前面
|
||||||
|
return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected Object doRun(RequestContext requestContext) throws ZuulException { |
||||||
|
HttpServletRequest request = requestContext.getRequest(); |
||||||
|
ApiConfig apiConfig = ApiContext.getApiConfig(); |
||||||
|
// 解析参数
|
||||||
|
ApiParam param = apiConfig.getParamParser().parse(request); |
||||||
|
ApiContext.setApiParam(param); |
||||||
|
// 验证操作,这里有负责验证签名参数
|
||||||
|
Validator validator = apiConfig.getValidator(); |
||||||
|
try { |
||||||
|
validator.validate(param); |
||||||
|
} catch (ApiException e) { |
||||||
|
log.error("签名验证失败,params:{}", param.toJSONString(), e); |
||||||
|
throw e; |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.manager; |
||||||
|
|
||||||
|
import org.springframework.data.redis.connection.Message; |
||||||
|
import org.springframework.data.redis.connection.MessageListener; |
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate; |
||||||
|
import org.springframework.data.redis.serializer.RedisSerializer; |
||||||
|
|
||||||
|
public class ApiMetaChangeListener implements MessageListener { |
||||||
|
|
||||||
|
private ApiMetaManager apiMetaManager; |
||||||
|
private StringRedisTemplate redisTemplate; |
||||||
|
|
||||||
|
public ApiMetaChangeListener(ApiMetaManager apiMetaManager, StringRedisTemplate redisTemplate) { |
||||||
|
this.apiMetaManager = apiMetaManager; |
||||||
|
this.redisTemplate = redisTemplate; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onMessage(Message message, byte[] bytes) { |
||||||
|
RedisSerializer<String> stringSerializer = redisTemplate.getStringSerializer(); |
||||||
|
String msg = stringSerializer.deserialize(message.getBody()); |
||||||
|
this.apiMetaManager.onChange(msg); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.manager; |
||||||
|
|
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class ApiMetaConfig { |
||||||
|
|
||||||
|
private StringRedisTemplate redisTemplate; |
||||||
|
|
||||||
|
public ApiMetaConfig(StringRedisTemplate redisTemplate) { |
||||||
|
this.redisTemplate = redisTemplate; |
||||||
|
} |
||||||
|
|
||||||
|
public void loadApiMetas() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.manager; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.bean.ServiceApiInfo; |
||||||
|
import org.springframework.cloud.netflix.zuul.filters.Route; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public interface ApiMetaContext { |
||||||
|
void reload(String serviceId, ServiceApiInfo serviceApiInfo); |
||||||
|
|
||||||
|
Route getRoute(String nameVersion); |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.manager; |
||||||
|
|
||||||
|
/** |
||||||
|
* 管理各服务接口信息 |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public interface ApiMetaManager { |
||||||
|
|
||||||
|
String API_STORE_KEY = "com.gitee.sop.api"; |
||||||
|
|
||||||
|
/** |
||||||
|
* 刷新素有的微服务接口信息 |
||||||
|
*/ |
||||||
|
void refresh(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 某个服务接口更改时触发 |
||||||
|
* @param serviceApiInfoJson 接口信息 |
||||||
|
*/ |
||||||
|
void onChange(String serviceApiInfoJson); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,62 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.manager; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.bean.ServiceApiInfo; |
||||||
|
import com.gitee.sop.gatewaycommon.message.ErrorEnum; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.springframework.cloud.netflix.zuul.filters.Route; |
||||||
|
|
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Iterator; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
public class DefaultApiMetaContext implements ApiMetaContext { |
||||||
|
|
||||||
|
// key:nameVersion
|
||||||
|
private Map<String, Route> nameVersionServiceIdMap = new HashMap<>(128); |
||||||
|
|
||||||
|
// key: serviceId , value: md5
|
||||||
|
private Map<String, String> serviceIdMd5Map = new HashMap<>(16); |
||||||
|
|
||||||
|
@Override |
||||||
|
public void reload(String serviceId, ServiceApiInfo serviceApiInfo) { |
||||||
|
String md5 = serviceIdMd5Map.get(serviceId); |
||||||
|
if (md5 != null && md5.equals(serviceApiInfo.getMd5())) { |
||||||
|
log.info("MD5相同,无需更改本地接口信息,appName:{}, md5:{}", serviceApiInfo.getAppName(), serviceApiInfo.getMd5()); |
||||||
|
return; |
||||||
|
} |
||||||
|
log.info("更新本地接口信息,appName:{}, md5:{}", serviceApiInfo.getAppName(), serviceApiInfo.getMd5()); |
||||||
|
// 移除原来的
|
||||||
|
Iterator<Map.Entry<String, Route>> iterator = nameVersionServiceIdMap.entrySet().iterator(); |
||||||
|
while (iterator.hasNext()) { |
||||||
|
Map.Entry<String, Route> entry = iterator.next(); |
||||||
|
if (entry.getValue().getLocation().equals(serviceId)) { |
||||||
|
iterator.remove(); |
||||||
|
} |
||||||
|
} |
||||||
|
List<ServiceApiInfo.ApiMeta> apis = serviceApiInfo.getApis(); |
||||||
|
for (ServiceApiInfo.ApiMeta apiMeta : apis) { |
||||||
|
Route route = this.buildRoute(serviceId, apiMeta); |
||||||
|
nameVersionServiceIdMap.put(apiMeta.fetchNameVersion(), route); |
||||||
|
} |
||||||
|
serviceIdMd5Map.put(serviceId, serviceApiInfo.getMd5()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Route getRoute(String nameVersion) { |
||||||
|
Route route = nameVersionServiceIdMap.get(nameVersion); |
||||||
|
if (route == null) { |
||||||
|
throw ErrorEnum.ISV_INVALID_METHOD.getErrorMeta().getException(); |
||||||
|
} |
||||||
|
return route; |
||||||
|
} |
||||||
|
|
||||||
|
protected Route buildRoute(String serviceId, ServiceApiInfo.ApiMeta apiMeta) { |
||||||
|
return new Route(apiMeta.getName(), apiMeta.getPath(), serviceId, null, false, null); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,51 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.manager; |
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON; |
||||||
|
import com.gitee.sop.gatewaycommon.bean.ServiceApiInfo; |
||||||
|
import lombok.Getter; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate; |
||||||
|
|
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* 保存在redis中,结构为HSET。格式如下: |
||||||
|
* <pre> |
||||||
|
* com.gitee.sop.api |
||||||
|
* <serviceId>:{ md5:"xxx", apis:[{name:"", version:""}] } |
||||||
|
* </pre> |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
@Getter |
||||||
|
@Slf4j |
||||||
|
public class DefaultApiMetaManager implements ApiMetaManager { |
||||||
|
|
||||||
|
private StringRedisTemplate redisTemplate; |
||||||
|
private ApiMetaContext apiMetaContext; |
||||||
|
|
||||||
|
public DefaultApiMetaManager(StringRedisTemplate redisTemplate, ApiMetaContext apiMetaContext) { |
||||||
|
this.redisTemplate = redisTemplate; |
||||||
|
this.apiMetaContext = apiMetaContext; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void refresh() { |
||||||
|
log.info("刷新本地接口信息"); |
||||||
|
Map<Object, Object> entries = redisTemplate.opsForHash().entries(API_STORE_KEY); |
||||||
|
for (Map.Entry<Object, Object> entry : entries.entrySet()) { |
||||||
|
log.info("更新微服务接口,appName:{}", entry.getKey()); |
||||||
|
String serviceId = entry.getKey().toString(); |
||||||
|
String serviceApiInfoJson = entry.getValue().toString(); |
||||||
|
ServiceApiInfo serviceApiInfo = JSON.parseObject(serviceApiInfoJson, ServiceApiInfo.class); |
||||||
|
apiMetaContext.reload(serviceId, serviceApiInfo); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onChange(String serviceApiInfoJson) { |
||||||
|
ServiceApiInfo serviceApiInfo = JSON.parseObject(serviceApiInfoJson, ServiceApiInfo.class); |
||||||
|
log.info("Redis订阅推送接口信息,appName:{}, md5:{}", serviceApiInfo.getAppName(), serviceApiInfo.getMd5()); |
||||||
|
this.apiMetaContext.reload(serviceApiInfo.getAppName(), serviceApiInfo); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,45 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.manager; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.bean.ApiContext; |
||||||
|
import com.gitee.sop.gatewaycommon.param.ApiParam; |
||||||
|
import org.springframework.cloud.netflix.zuul.filters.Route; |
||||||
|
import org.springframework.cloud.netflix.zuul.filters.RouteLocator; |
||||||
|
import org.springframework.core.Ordered; |
||||||
|
|
||||||
|
import java.util.Collection; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class SopRouteLocator implements RouteLocator, Ordered { |
||||||
|
|
||||||
|
private ApiMetaContext apiMetaContext; |
||||||
|
|
||||||
|
public SopRouteLocator(ApiMetaContext apiMetaContext) { |
||||||
|
this.apiMetaContext = apiMetaContext; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Collection<String> getIgnoredPaths() { |
||||||
|
return Collections.emptyList(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public List<Route> getRoutes() { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Route getMatchingRoute(String path) { |
||||||
|
ApiParam param = ApiContext.getApiParam(); |
||||||
|
String nameVersion = param.fetchNameVersion(); |
||||||
|
return apiMetaContext.getRoute(nameVersion); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getOrder() { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.message; |
||||||
|
|
||||||
|
/** |
||||||
|
* 定义错误返回 |
||||||
|
* code(返回码) |
||||||
|
* msg(返回码描述) |
||||||
|
* sub_code(明细返回码) |
||||||
|
* sub_msg(明细返回码描述) |
||||||
|
* 解决方案 |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public interface Error { |
||||||
|
/** |
||||||
|
* 获取网关状态码 |
||||||
|
* |
||||||
|
* @return 返回状态码 |
||||||
|
*/ |
||||||
|
String getCode(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取网关错误信息 |
||||||
|
* |
||||||
|
* @return 返回错误信息 |
||||||
|
*/ |
||||||
|
String getMsg(); |
||||||
|
|
||||||
|
/** |
||||||
|
* sub_code(明细返回码) |
||||||
|
* @return sub_code(明细返回码) |
||||||
|
*/ |
||||||
|
String getSub_code(); |
||||||
|
|
||||||
|
/** |
||||||
|
* sub_msg(明细返回码描述) |
||||||
|
* @return sub_msg(明细返回码描述) |
||||||
|
*/ |
||||||
|
String getSub_msg(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 解决方案 |
||||||
|
* @return 解决方案 |
||||||
|
*/ |
||||||
|
String getSolution(); |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,66 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.message; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public enum ErrorEnum { |
||||||
|
SUCCESS("10000", ""), |
||||||
|
|
||||||
|
ISP_UNKNOW_ERROR("20000", "isp.unknow-error"), |
||||||
|
AOP_UNKNOW_ERROR("20000", "aop.unknow-error"), |
||||||
|
|
||||||
|
AOP_INVALID_AUTH_TOKEN("20001", "aop.invalid-auth-token"), |
||||||
|
AOP_INVALID_AUTH_OKEN("20001", "aop.invalid-auth-token"), |
||||||
|
AOP_AUTH_TOKEN_TIME_OUT("20001", "aop.auth-token-time-out"), |
||||||
|
AOP_INVALID_APP_AUTH_TOKEN("20001", "aop.invalid-app-auth-token"), |
||||||
|
AOP_INVALID_APP_AUTH_TOKEN_NO_API("20001", "aop.invalid-app-auth-token-no-api"), |
||||||
|
AOP_APP_AUTH_TOKEN_TIME_OUT("20001", "aop.app-auth-token-time-out"), |
||||||
|
AOP_NO_PRODUCT_REG_BY_PARTNER("20001", "aop.no-product-reg-by-partner"), |
||||||
|
|
||||||
|
ISV_MISSING_METHOD("40001", "isv.missing-method"), |
||||||
|
ISV_MISSING_SIGNATURE("40001", "isv.missing-signature"), |
||||||
|
ISV_MISSING_SIGNATURE_TYPE("40001", "isv.missing-signature-type"), |
||||||
|
ISV_MISSING_SIGNATURE_KEY("40001", "isv.missing-signature-key"), |
||||||
|
ISV_MISSING_APP_ID("40001", "isv.missing-app-id"), |
||||||
|
ISV_MISSING_TIMESTAMP("40001", "isv.missing-timestamp"), |
||||||
|
ISV_MISSING_VERSION("40001", "isv.missing-version"), |
||||||
|
ISV_DECRYPTION_ERROR_MISSING_ENCRYPT_TYPE("40001", "isv.decryption-error-missing-encrypt-type"), |
||||||
|
|
||||||
|
ISV_INVALID_PARAMETER("40002", "isv.invalid-parameter"), |
||||||
|
ISV_UPLOAD_FAIL("40002", "isv.upload-fail"), |
||||||
|
ISV_INVALID_FILE_EXTENSION("40002", "isv.invalid-file-extension"), |
||||||
|
ISV_INVALID_FILE_SIZE("40002", "isv.invalid-file-size"), |
||||||
|
ISV_INVALID_METHOD("40002", "isv.invalid-method"), |
||||||
|
ISV_INVALID_FORMAT("40002", "isv.invalid-format"), |
||||||
|
ISV_INVALID_SIGNATURE_TYPE("40002", "isv.invalid-signature-type"), |
||||||
|
ISV_INVALID_SIGNATURE("40002", "isv.invalid-signature"), |
||||||
|
ISV_INVALID_ENCRYPT_TYPE("40002", "isv.invalid-encrypt-type"), |
||||||
|
ISV_INVALID_ENCRYPT("40002", "isv.invalid-encrypt"), |
||||||
|
ISV_INVALID_APP_ID("40002", "isv.invalid-app-id"), |
||||||
|
ISV_INVALID_TIMESTAMP("40002", "isv.invalid-timestamp"), |
||||||
|
ISV_INVALID_CHARSET("40002", "isv.invalid-charset"), |
||||||
|
ISV_INVALID_DIGEST("40002", "isv.invalid-digest"), |
||||||
|
ISV_DECRYPTION_ERROR_NOT_VALID_ENCRYPT_TYPE("40002", "isv.decryption-error-not-valid-encrypt-type"), |
||||||
|
ISV_DECRYPTION_ERROR_NOT_VALID_ENCRYPT_KEY("40002", "isv.decryption-error-not-valid-encrypt-key"), |
||||||
|
ISV_DECRYPTION_ERROR_UNKNOWN("40002", "isv.decryption-error-unknown"), |
||||||
|
ISV_MISSING_SIGNATURE_CONFIG("40002", "isv.missing-signature-config"), |
||||||
|
ISV_NOT_SUPPORT_APP_AUTH("40002", "isv.not-support-app-auth"), |
||||||
|
ISV_SUSPECTED_ATTACK("40002", "isv.suspected-attack"), |
||||||
|
ISV_INVALID_CONTENT_TYPE("40002", "isv.invalid-content-type"), |
||||||
|
|
||||||
|
BIZ_ERROR("40004", ""), |
||||||
|
|
||||||
|
ISV_INSUFFICIENT_ISV_PERMISSIONS("40006", "isv.insufficient-isv-permissions"), |
||||||
|
ISV_INSUFFICIENT_USER_PERMISSIONS("40006", "isv.insufficient-user-permissions"), |
||||||
|
|
||||||
|
; |
||||||
|
private ErrorMeta errorMeta; |
||||||
|
|
||||||
|
ErrorEnum(String code, String sub_code) { |
||||||
|
this.errorMeta = new ErrorMeta("open.error_", code, sub_code); |
||||||
|
} |
||||||
|
|
||||||
|
public ErrorMeta getErrorMeta() { |
||||||
|
return errorMeta; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,120 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.message; |
||||||
|
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.springframework.context.support.MessageSourceAccessor; |
||||||
|
import org.springframework.context.support.ResourceBundleMessageSource; |
||||||
|
import org.springframework.util.Assert; |
||||||
|
import org.springframework.util.StringUtils; |
||||||
|
|
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.HashSet; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Locale; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
/** |
||||||
|
* 负责构建错误消息 |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
public class ErrorFactory { |
||||||
|
|
||||||
|
public static final String SYS_ERR = "系统错误"; |
||||||
|
|
||||||
|
private static final String I18N_OPEN_ERROR = "i18n/open/error"; |
||||||
|
public static final String UNDERLINE = "_"; |
||||||
|
|
||||||
|
private static Set<String> noModuleCache = new HashSet<String>(); |
||||||
|
|
||||||
|
private static Map<String, Error> errorCache = new HashMap<>(64); |
||||||
|
|
||||||
|
/** |
||||||
|
* 错误信息的国际化信息 |
||||||
|
*/ |
||||||
|
private static MessageSourceAccessor errorMessageSourceAccessor; |
||||||
|
|
||||||
|
/** |
||||||
|
* 设置国际化资源信息 |
||||||
|
*/ |
||||||
|
public static void initMessageSource(List<String> isvModules) { |
||||||
|
HashSet<String> baseNamesSet = new HashSet<String>(); |
||||||
|
baseNamesSet.add(I18N_OPEN_ERROR); |
||||||
|
|
||||||
|
if (!isvModules.isEmpty()) { |
||||||
|
baseNamesSet.addAll(isvModules); |
||||||
|
} |
||||||
|
|
||||||
|
String[] totalBaseNames = baseNamesSet.toArray(new String[0]); |
||||||
|
|
||||||
|
log.info("加载错误码国际化资源:{}", StringUtils.arrayToCommaDelimitedString(totalBaseNames)); |
||||||
|
ResourceBundleMessageSource bundleMessageSource = new ResourceBundleMessageSource(); |
||||||
|
bundleMessageSource.setBasenames(totalBaseNames); |
||||||
|
MessageSourceAccessor messageSourceAccessor = new MessageSourceAccessor(bundleMessageSource); |
||||||
|
setErrorMessageSourceAccessor(messageSourceAccessor); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 通过ErrorMeta,Locale,params构建国际化错误消息 |
||||||
|
* |
||||||
|
* @param errorMeta 错误信息 |
||||||
|
* @param locale 本地化 |
||||||
|
* @param params 参数 |
||||||
|
* @return 如果没有配置国际化消息,则直接返回errorMeta中的信息 |
||||||
|
*/ |
||||||
|
public static Error getError(ErrorMeta errorMeta, Locale locale, Object... params) { |
||||||
|
String key = errorMeta.getModulePrefix() + errorMeta.getCode() + errorMeta.getSubCode() + locale.toString(); |
||||||
|
Error error = errorCache.get(key); |
||||||
|
if (error == null) { |
||||||
|
Assert.notNull(locale, "未设置Locale"); |
||||||
|
String modulePrefix = errorMeta.getModulePrefix(); |
||||||
|
String code = errorMeta.getCode(); |
||||||
|
// open.error_20000=Service is temporarily unavailable
|
||||||
|
String msg = getErrorMessage(modulePrefix + code, locale); |
||||||
|
String subCode = errorMeta.getSubCode(); |
||||||
|
// open.error_20000_isp.unknow-error=Service is temporarily unavailable
|
||||||
|
String subMsg = getErrorMessage(modulePrefix + code + UNDERLINE + subCode, locale, params); |
||||||
|
if (StringUtils.isEmpty(msg)) { |
||||||
|
msg = SYS_ERR; |
||||||
|
} |
||||||
|
if (StringUtils.isEmpty(subMsg)) { |
||||||
|
subMsg = SYS_ERR; |
||||||
|
} |
||||||
|
// solution暂未实现,如果要实现,可以这样配置:
|
||||||
|
// open.error_20000_isp.unknow-error_solution=Service is temporarily unavailable
|
||||||
|
// String solution = getErrorMessage(modulePrefix + code + UNDERLINE + subCode + "_solution", locale, params);
|
||||||
|
error = new ErrorImpl(code, msg, subCode, subMsg, null); |
||||||
|
errorCache.put(key, error); |
||||||
|
} |
||||||
|
return error; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public static void setErrorMessageSourceAccessor(MessageSourceAccessor errorMessageSourceAccessor) { |
||||||
|
ErrorFactory.errorMessageSourceAccessor = errorMessageSourceAccessor; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 返回本地化信息 |
||||||
|
* |
||||||
|
* @param module 错误模块 |
||||||
|
* @param locale 本地化 |
||||||
|
* @param params 参数 |
||||||
|
* @return 返回信息 |
||||||
|
*/ |
||||||
|
public static String getErrorMessage(String module, Locale locale, Object... params) { |
||||||
|
if (noModuleCache.contains(module)) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
try { |
||||||
|
return errorMessageSourceAccessor.getMessage(module, params, locale); |
||||||
|
} catch (Exception e) { |
||||||
|
noModuleCache.add(module); |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.message; |
||||||
|
|
||||||
|
import lombok.Data; |
||||||
|
|
||||||
|
@Data |
||||||
|
public class ErrorImpl implements Error { |
||||||
|
private String code; |
||||||
|
private String msg; |
||||||
|
private String sub_code; |
||||||
|
private String sub_msg; |
||||||
|
private String solution; |
||||||
|
|
||||||
|
public ErrorImpl() { |
||||||
|
} |
||||||
|
|
||||||
|
public ErrorImpl(String code, String msg, String sub_code, String sub_msg, String solution) { |
||||||
|
this.code = code; |
||||||
|
this.msg = msg; |
||||||
|
this.sub_code = sub_code; |
||||||
|
this.sub_msg = sub_msg; |
||||||
|
this.solution = solution; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,47 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.message; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.bean.ApiContext; |
||||||
|
import com.gitee.sop.gatewaycommon.exception.ApiException; |
||||||
|
import lombok.Getter; |
||||||
|
|
||||||
|
/** |
||||||
|
* 错误对象 |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
@Getter |
||||||
|
public class ErrorMeta { |
||||||
|
|
||||||
|
private String modulePrefix; |
||||||
|
private String code; |
||||||
|
private String subCode; |
||||||
|
|
||||||
|
public ErrorMeta(String modulePrefix, String code, String subCode) { |
||||||
|
this.modulePrefix = modulePrefix; |
||||||
|
this.code = code; |
||||||
|
this.subCode = subCode; |
||||||
|
} |
||||||
|
|
||||||
|
public Error getError() { |
||||||
|
return ErrorFactory.getError(this, ApiContext.getLocale()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 返回网关exception |
||||||
|
* |
||||||
|
* @param params 参数 |
||||||
|
* @return 返回exception |
||||||
|
*/ |
||||||
|
public ApiException getException(Object... params) { |
||||||
|
if (params != null && params.length == 1) { |
||||||
|
Object param = params[0]; |
||||||
|
if (param instanceof Throwable) { |
||||||
|
Error error = ErrorFactory.getError(this, ApiContext.getLocale()); |
||||||
|
return new ApiException((Throwable) param, error); |
||||||
|
} |
||||||
|
} |
||||||
|
Error error = ErrorFactory.getError(this, ApiContext.getLocale(), params); |
||||||
|
return new ApiException(error); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,197 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.param; |
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONObject; |
||||||
|
import com.gitee.sop.gatewaycommon.bean.SopConstants; |
||||||
|
import com.netflix.zuul.context.RequestContext; |
||||||
|
import org.apache.commons.lang.StringUtils; |
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* 客户端传来的参数放在这里. |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class ApiParam extends JSONObject implements Param { |
||||||
|
public ApiParam(Map<String, Object> map) { |
||||||
|
super(map); |
||||||
|
} |
||||||
|
|
||||||
|
private boolean ignoreSign; |
||||||
|
private boolean ignoreValidate; |
||||||
|
|
||||||
|
private String restName; |
||||||
|
private String restVersion; |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取sign,并从param中删除 |
||||||
|
* |
||||||
|
* @return 返回sign内容 |
||||||
|
*/ |
||||||
|
public String fetchSignAndRemove() { |
||||||
|
String sign = this.fetchSign(); |
||||||
|
this.remove(ParamNames.SIGN_NAME); |
||||||
|
return sign; |
||||||
|
} |
||||||
|
|
||||||
|
public HttpServletRequest fetchRequest() { |
||||||
|
return RequestContext.getCurrentContext().getRequest(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 是否忽略验证签名 |
||||||
|
* |
||||||
|
* @return 返回true,忽略签名 |
||||||
|
*/ |
||||||
|
public boolean fetchIgnoreSign() { |
||||||
|
return ignoreSign; |
||||||
|
} |
||||||
|
|
||||||
|
public void setIgnoreSign(boolean ignoreSign) { |
||||||
|
this.ignoreSign = ignoreSign; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean fetchIgnoreValidate() { |
||||||
|
return ignoreValidate; |
||||||
|
} |
||||||
|
|
||||||
|
public void setIgnoreValidate(boolean ignoreValidate) { |
||||||
|
this.ignoreValidate = ignoreValidate; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 接口名,如:goods.list |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public String fetchName() { |
||||||
|
String name = getString(ParamNames.API_NAME); |
||||||
|
if (name == null) { |
||||||
|
name = this.restName; |
||||||
|
} |
||||||
|
return name; |
||||||
|
} |
||||||
|
|
||||||
|
public void setName(String name) { |
||||||
|
this.restName = name; |
||||||
|
} |
||||||
|
|
||||||
|
public String fetchNameVersion() { |
||||||
|
return buildNameVersion(this.fetchName(), this.fetchVersion()); |
||||||
|
} |
||||||
|
|
||||||
|
public static String buildNameVersion(String name, String version) { |
||||||
|
if (StringUtils.isEmpty(version)) { |
||||||
|
return name; |
||||||
|
} else { |
||||||
|
return name + version; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 版本号 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public String fetchVersion() { |
||||||
|
String version = getString(ParamNames.VERSION_NAME); |
||||||
|
if (version == null) { |
||||||
|
version = this.restVersion; |
||||||
|
} |
||||||
|
return version; |
||||||
|
} |
||||||
|
|
||||||
|
public void setVersion(String version) { |
||||||
|
this.restVersion = version; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 接入应用ID |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public String fetchAppKey() { |
||||||
|
return getString(ParamNames.APP_KEY_NAME); |
||||||
|
} |
||||||
|
|
||||||
|
public void setAppKey(String appKey) { |
||||||
|
put(ParamNames.APP_KEY_NAME, appKey); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 参数,urlencode后的 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public String fetchData() { |
||||||
|
return getString(ParamNames.BIZ_CONTENT_NAME); |
||||||
|
} |
||||||
|
|
||||||
|
public void setData(String json) { |
||||||
|
put(ParamNames.BIZ_CONTENT_NAME, json); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 时间戳,格式为yyyy-MM-dd HH:mm:ss,例如:2015-01-01 12:00:00 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public String fetchTimestamp() { |
||||||
|
return getString(ParamNames.TIMESTAMP_NAME); |
||||||
|
} |
||||||
|
|
||||||
|
public void setTimestamp(String timestamp) { |
||||||
|
put(ParamNames.TIMESTAMP_NAME, timestamp); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 签名串 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public String fetchSign() { |
||||||
|
return getString(ParamNames.SIGN_NAME); |
||||||
|
} |
||||||
|
|
||||||
|
public void setSign(String sign) { |
||||||
|
put(ParamNames.SIGN_NAME, sign); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String fetchFormat() { |
||||||
|
String format = getString(ParamNames.FORMAT_NAME); |
||||||
|
if (format == null || "".equals(format)) { |
||||||
|
return SopConstants.FORMAT_JSON; |
||||||
|
} |
||||||
|
return format; |
||||||
|
} |
||||||
|
|
||||||
|
public void setFormat(String format) { |
||||||
|
put(ParamNames.FORMAT_NAME, format); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String fetchAccessToken() { |
||||||
|
return getString(ParamNames.APP_AUTH_TOKEN_NAME); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String fetchSignMethod() { |
||||||
|
String signMethod = getString(ParamNames.SIGN_TYPE_NAME); |
||||||
|
if (signMethod == null) { |
||||||
|
return SopConstants.DEFAULT_SIGN_METHOD; |
||||||
|
} else { |
||||||
|
return signMethod; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String fetchCharset() { |
||||||
|
return getString(ParamNames.CHARSET_NAME); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public ApiParam clone() { |
||||||
|
ApiParam param = new ApiParam(this); |
||||||
|
param.ignoreSign = this.ignoreSign; |
||||||
|
param.ignoreValidate = this.ignoreValidate; |
||||||
|
return param; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,104 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.param; |
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON; |
||||||
|
import com.gitee.sop.gatewaycommon.bean.ApiContext; |
||||||
|
import com.gitee.sop.gatewaycommon.message.ErrorEnum; |
||||||
|
import com.gitee.sop.gatewaycommon.util.RequestUtil; |
||||||
|
import com.netflix.zuul.http.HttpServletRequestWrapper; |
||||||
|
import org.springframework.http.MediaType; |
||||||
|
import org.springframework.web.multipart.MultipartFile; |
||||||
|
import org.springframework.web.multipart.MultipartHttpServletRequest; |
||||||
|
import org.springframework.web.multipart.commons.CommonsMultipartResolver; |
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
/** |
||||||
|
* 参数解析默认实现 |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class ApiParamParser implements ParamParser { |
||||||
|
|
||||||
|
private static final String CONTENT_TYPE_MULTIPART = MediaType.MULTIPART_FORM_DATA_VALUE; |
||||||
|
private static final String CONTENT_TYPE_JSON = MediaType.APPLICATION_JSON_VALUE; |
||||||
|
private static final String CONTENT_TYPE_TEXT = MediaType.TEXT_PLAIN_VALUE; |
||||||
|
|
||||||
|
@Override |
||||||
|
public ApiParam parse(HttpServletRequest request) { |
||||||
|
try { |
||||||
|
Map<String, Object> params = this.getJson(request); |
||||||
|
return new ApiParam(params); |
||||||
|
} catch (Exception e) { |
||||||
|
throw ErrorEnum.ISV_INVALID_PARAMETER.getErrorMeta().getException(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public Map<String, Object> getJson(HttpServletRequest request) throws Exception { |
||||||
|
// zuul会做一层包装
|
||||||
|
if (request instanceof HttpServletRequestWrapper) { |
||||||
|
HttpServletRequestWrapper req = (HttpServletRequestWrapper) request; |
||||||
|
request = req.getRequest(); |
||||||
|
} |
||||||
|
Map<String, Object> params = null; |
||||||
|
|
||||||
|
if (RequestUtil.isGetRequest(request)) { |
||||||
|
params = RequestUtil.convertRequestParamsToMap(request); |
||||||
|
} else { |
||||||
|
String contectType = request.getContentType(); |
||||||
|
|
||||||
|
if (contectType == null) { |
||||||
|
contectType = ""; |
||||||
|
} |
||||||
|
|
||||||
|
contectType = contectType.toLowerCase(); |
||||||
|
|
||||||
|
// json或者纯文本形式
|
||||||
|
if (contectType.contains(CONTENT_TYPE_JSON) || contectType.contains(CONTENT_TYPE_TEXT)) { |
||||||
|
String txt = RequestUtil.getText(request); |
||||||
|
params = JSON.parseObject(txt); |
||||||
|
} else if (contectType.contains(CONTENT_TYPE_MULTIPART)) { |
||||||
|
// 上传文件形式
|
||||||
|
params = this.parseUploadRequest(request); |
||||||
|
} else { |
||||||
|
params = RequestUtil.convertRequestParamsToMap(request); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return params; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 解析文件上传请求 |
||||||
|
* |
||||||
|
* @param request |
||||||
|
* @return 返回json字符串 |
||||||
|
*/ |
||||||
|
protected Map<String, Object> parseUploadRequest(HttpServletRequest request) { |
||||||
|
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver( |
||||||
|
request.getSession().getServletContext()); |
||||||
|
// 检查form中是否有enctype="multipart/form-data"
|
||||||
|
if (multipartResolver.isMultipart(request)) { |
||||||
|
MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request; |
||||||
|
Map<String, MultipartFile> fileMap = multiRequest.getFileMap(); |
||||||
|
Map<String, MultipartFile> finalMap = new HashMap<>(fileMap.size()); |
||||||
|
|
||||||
|
Set<String> keys = fileMap.keySet(); |
||||||
|
for (String name : keys) { |
||||||
|
MultipartFile file = fileMap.get(name); |
||||||
|
if (file.getSize() > 0) { |
||||||
|
finalMap.put(name, file); |
||||||
|
} |
||||||
|
} |
||||||
|
if (finalMap.size() > 0) { |
||||||
|
// 保存上传文件
|
||||||
|
ApiContext.setUploadContext(new ApiUploadContext(finalMap)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return RequestUtil.convertRequestParamsToMap(request); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,42 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.param; |
||||||
|
|
||||||
|
import org.springframework.web.multipart.MultipartFile; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* 存放上传文件 |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class ApiUploadContext implements UploadContext { |
||||||
|
|
||||||
|
private Map<String, MultipartFile> fileMap; |
||||||
|
private List<MultipartFile> allFile; |
||||||
|
|
||||||
|
public ApiUploadContext(Map<String, MultipartFile> map) { |
||||||
|
if (map == null) { |
||||||
|
map = Collections.emptyMap(); |
||||||
|
} |
||||||
|
this.fileMap = map; |
||||||
|
this.allFile = new ArrayList<>(map.values()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public MultipartFile getFile(int index) { |
||||||
|
return this.allFile.get(index); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public MultipartFile getFile(String name) { |
||||||
|
return fileMap.get(name); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public List<MultipartFile> getAllFile() { |
||||||
|
return this.allFile; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,70 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.param; |
||||||
|
|
||||||
|
import java.io.Serializable; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public interface Param extends Serializable { |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取接口名 |
||||||
|
* @return 返回接口名 |
||||||
|
*/ |
||||||
|
String fetchName(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取版本号 |
||||||
|
* @return 返回版本号 |
||||||
|
*/ |
||||||
|
String fetchVersion(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取appKey |
||||||
|
* @return 返回appKey |
||||||
|
*/ |
||||||
|
String fetchAppKey(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取业务参数 |
||||||
|
* @return 返回业务参数 |
||||||
|
*/ |
||||||
|
String fetchData(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取时间戳 |
||||||
|
* @return 返回时间戳 |
||||||
|
*/ |
||||||
|
String fetchTimestamp(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取签名串 |
||||||
|
* @return 返回签名串 |
||||||
|
*/ |
||||||
|
String fetchSign(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取格式化类型 |
||||||
|
* @return 返回格式化类型 |
||||||
|
*/ |
||||||
|
String fetchFormat(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取accessToken |
||||||
|
* @return 返回accessToken |
||||||
|
*/ |
||||||
|
String fetchAccessToken(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取签名方式 |
||||||
|
* @return 返回签名方式 |
||||||
|
*/ |
||||||
|
String fetchSignMethod(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 请求使用的编码格式,如utf-8,gbk,gb2312等 |
||||||
|
* @return 请求使用的编码格式,如utf-8,gbk,gb2312等 |
||||||
|
*/ |
||||||
|
String fetchCharset(); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,46 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.param; |
||||||
|
|
||||||
|
/** |
||||||
|
* 请求参数名定义 |
||||||
|
* |
||||||
|
* 参数 类型 是否必填 最大长度 描述 示例值 |
||||||
|
* app_id String 是 32 支付宝分配给开发者的应用ID 2014072300007148 |
||||||
|
* method String 是 128 接口名称 alipay.trade.fastpay.refund.query |
||||||
|
* format String 否 40 仅支持JSON JSON |
||||||
|
* charset String 是 10 请求使用的编码格式,如utf-8,gbk,gb2312等 utf-8 |
||||||
|
* sign_type String 是 10 商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2 RSA2 |
||||||
|
* sign String 是 344 商户请求参数的签名串,详见签名 详见示例 |
||||||
|
* timestamp String 是 19 发送请求的时间,格式"yyyy-MM-dd HH:mm:ss" 2014-07-24 03:07:50 |
||||||
|
* version String 是 3 调用的接口版本,固定为:1.0 1.0 |
||||||
|
* app_auth_token String 否 40 详见应用授权概述 |
||||||
|
* biz_content String 是 请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递,具体参照各产品快速接入文档 |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class ParamNames { |
||||||
|
/** 分配给开发者的应用ID */ |
||||||
|
public static String APP_KEY_NAME = "app_id"; |
||||||
|
/** 接口名称 */ |
||||||
|
public static String API_NAME = "method"; |
||||||
|
/** 仅支持JSON */ |
||||||
|
public static String FORMAT_NAME = "format"; |
||||||
|
/** 请求使用的编码格式 */ |
||||||
|
public static String CHARSET_NAME = "charset"; |
||||||
|
/** 商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2 */ |
||||||
|
public static String SIGN_TYPE_NAME = "sign_type"; |
||||||
|
/** 商户请求参数的签名串 */ |
||||||
|
public static String SIGN_NAME = "sign"; |
||||||
|
/** 发送请求的时间 */ |
||||||
|
public static String TIMESTAMP_NAME = "timestamp"; |
||||||
|
/** 调用的接口版本 */ |
||||||
|
public static String VERSION_NAME = "version"; |
||||||
|
/** OAuth 2.0授权token */ |
||||||
|
public static String APP_AUTH_TOKEN_NAME = "app_auth_token"; |
||||||
|
/** 请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递,具体参照各产品快速接入文档 */ |
||||||
|
public static String BIZ_CONTENT_NAME = "biz_content"; |
||||||
|
|
||||||
|
/** */ |
||||||
|
public static String TIMESTAMP_PATTERN = "yyyy-MM-dd HH:mm:ss"; |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.param; |
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
|
||||||
|
/** |
||||||
|
* 负责解析参数 |
||||||
|
* @author tanghc |
||||||
|
* |
||||||
|
*/ |
||||||
|
public interface ParamParser { |
||||||
|
|
||||||
|
/** |
||||||
|
* 从request提取参数 |
||||||
|
* @param request |
||||||
|
* @return 返回ApiParam |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
ApiParam parse(HttpServletRequest request); |
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.param; |
||||||
|
|
||||||
|
import org.springframework.web.multipart.MultipartFile; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取上传文件 |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public interface UploadContext { |
||||||
|
/** |
||||||
|
* 根据索引获取上传文件,从0开始 |
||||||
|
* |
||||||
|
* @param index |
||||||
|
* @return 返回上传文件信息 |
||||||
|
*/ |
||||||
|
MultipartFile getFile(int index); |
||||||
|
|
||||||
|
/** |
||||||
|
* 根据表单名获取上传文件 |
||||||
|
* |
||||||
|
* @param name |
||||||
|
* 表单名称 |
||||||
|
* @return 返回上传文件信息 |
||||||
|
*/ |
||||||
|
MultipartFile getFile(String name); |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取所有的上传文件 |
||||||
|
* |
||||||
|
* @return 返回所有的上传文件 |
||||||
|
*/ |
||||||
|
List<MultipartFile> getAllFile(); |
||||||
|
} |
@ -0,0 +1,77 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.result; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.message.ErrorEnum; |
||||||
|
import com.thoughtworks.xstream.annotations.XStreamAlias; |
||||||
|
import lombok.Data; |
||||||
|
|
||||||
|
/** |
||||||
|
* 默认的结果封装类. |
||||||
|
* <pre> |
||||||
|
* |
||||||
|
* xml返回结果: |
||||||
|
* <response> |
||||||
|
* <code>50</code> |
||||||
|
* <msg>Remote service error</msg> |
||||||
|
* <sub_code>isv.invalid-parameter</sub_code> |
||||||
|
* <sub_msg>非法参数</sub_msg> |
||||||
|
* </response> |
||||||
|
* 成功情况: |
||||||
|
* <response> |
||||||
|
* <code>0</code> |
||||||
|
* <msg>成功消息</msg> |
||||||
|
* <data> |
||||||
|
* ...返回内容 |
||||||
|
* </data> |
||||||
|
* </response> |
||||||
|
* |
||||||
|
* json返回格式: |
||||||
|
* { |
||||||
|
* "code":"50", |
||||||
|
* "msg":"Remote service error", |
||||||
|
* "sub_code":"isv.invalid-parameter", |
||||||
|
* "sub_msg":"非法参数" |
||||||
|
* } |
||||||
|
* 成功情况: |
||||||
|
* { |
||||||
|
* "code":"0", |
||||||
|
* "msg":"成功消息内容。。。", |
||||||
|
* "data":{ |
||||||
|
* ...返回内容 |
||||||
|
* } |
||||||
|
* } |
||||||
|
* </pre> |
||||||
|
* <p> |
||||||
|
* 字段说明: |
||||||
|
* code:网关异常码 <br> |
||||||
|
* msg:网关异常信息 <br> |
||||||
|
* sub_code:业务异常码 <br> |
||||||
|
* sub_msg:业务异常信息 <br> |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
@XStreamAlias("response") |
||||||
|
@Data |
||||||
|
public class ApiResult implements Result { |
||||||
|
|
||||||
|
/** |
||||||
|
* 网关异常码,范围0~100 成功返回"0" |
||||||
|
*/ |
||||||
|
private String code = ErrorEnum.SUCCESS.getErrorMeta().getCode(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 网关异常信息 |
||||||
|
*/ |
||||||
|
private String msg; |
||||||
|
|
||||||
|
/** |
||||||
|
* 业务异常码 |
||||||
|
*/ |
||||||
|
private String sub_msg; |
||||||
|
|
||||||
|
/** |
||||||
|
* 业务异常信息 |
||||||
|
*/ |
||||||
|
private String sub_code; |
||||||
|
|
||||||
|
private String sign; |
||||||
|
} |
@ -0,0 +1,125 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.result; |
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON; |
||||||
|
import com.alibaba.fastjson.JSONObject; |
||||||
|
import com.gitee.sop.gatewaycommon.bean.ApiContext; |
||||||
|
import com.gitee.sop.gatewaycommon.exception.ApiException; |
||||||
|
import com.gitee.sop.gatewaycommon.message.Error; |
||||||
|
import com.gitee.sop.gatewaycommon.message.ErrorEnum; |
||||||
|
import com.gitee.sop.gatewaycommon.message.ErrorMeta; |
||||||
|
import com.gitee.sop.gatewaycommon.param.ApiParam; |
||||||
|
import com.gitee.sop.gatewaycommon.param.ParamNames; |
||||||
|
import com.netflix.zuul.exception.ZuulException; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.springframework.http.HttpStatus; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
public class ApiResultExecutor implements ResultExecutor { |
||||||
|
|
||||||
|
private static final ErrorMeta SUCCESS_META = ErrorEnum.SUCCESS.getErrorMeta(); |
||||||
|
private static final ErrorMeta ISP_UNKNOW_ERROR_META = ErrorEnum.ISP_UNKNOW_ERROR.getErrorMeta(); |
||||||
|
private static final ErrorMeta ISP_BIZ_ERROR = ErrorEnum.BIZ_ERROR.getErrorMeta(); |
||||||
|
|
||||||
|
|
||||||
|
public static final int BIZ_ERROR_STATUS = 4000; |
||||||
|
private static final char DOT = '.'; |
||||||
|
private static final char UNDERLINE = '_'; |
||||||
|
public static final String GATEWAY_CODE_NAME = "code"; |
||||||
|
public static final String GATEWAY_MSG_NAME = "msg"; |
||||||
|
public static final String DATA_SUFFIX = "_response"; |
||||||
|
|
||||||
|
@Override |
||||||
|
public String mergeResult(int responseStatus, String responseData) { |
||||||
|
if (responseStatus == HttpStatus.OK.value() || responseStatus == BIZ_ERROR_STATUS) { |
||||||
|
return mergeSuccess(responseStatus, responseData); |
||||||
|
} else { |
||||||
|
// 微服务端有可能返回500错误
|
||||||
|
// {"path":"/book/getBook3","error":"Internal Server Error","message":"id不能为空","timestamp":"2019-02-13T07:41:00.495+0000","status":500}
|
||||||
|
return mergeError(responseData); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String mergeError(Throwable throwable) { |
||||||
|
Error error = null; |
||||||
|
if (throwable instanceof ZuulException) { |
||||||
|
ZuulException ex = (ZuulException) throwable; |
||||||
|
Throwable cause = ex.getCause(); |
||||||
|
if (cause instanceof ApiException) { |
||||||
|
ApiException apiException = (ApiException) cause; |
||||||
|
error = apiException.getError(); |
||||||
|
} |
||||||
|
} |
||||||
|
if (error == null) { |
||||||
|
error = ErrorEnum.AOP_UNKNOW_ERROR.getErrorMeta().getError(); |
||||||
|
} |
||||||
|
JSONObject jsonObject = (JSONObject)JSON.toJSON(error); |
||||||
|
return this.merge(jsonObject); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
成功示例 |
||||||
|
{ |
||||||
|
"alipay_trade_fastpay_refund_query_response": { |
||||||
|
"code": "10000", |
||||||
|
"msg": "Success", |
||||||
|
"trade_no": "2014112611001004680073956707", |
||||||
|
"out_trade_no": "20150320010101001", |
||||||
|
"out_request_no": "20150320010101001", |
||||||
|
"refund_reason": "用户退款请求", |
||||||
|
"total_amount": 100.2, |
||||||
|
"refund_amount": 12.33 |
||||||
|
}, |
||||||
|
"sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE" |
||||||
|
} |
||||||
|
*/ |
||||||
|
public String mergeSuccess(int responseStatus, String serviceResult) { |
||||||
|
JSONObject jsonObjectService; |
||||||
|
// 如果是业务出错
|
||||||
|
if (responseStatus == BIZ_ERROR_STATUS) { |
||||||
|
jsonObjectService = JSON.parseObject(serviceResult); |
||||||
|
jsonObjectService.put(GATEWAY_CODE_NAME, ISP_BIZ_ERROR.getCode()); |
||||||
|
jsonObjectService.put(GATEWAY_MSG_NAME, ISP_BIZ_ERROR.getError().getMsg()); |
||||||
|
} else { |
||||||
|
// 200正常返回
|
||||||
|
jsonObjectService = JSON.parseObject(serviceResult); |
||||||
|
jsonObjectService.put(GATEWAY_CODE_NAME, SUCCESS_META.getCode()); |
||||||
|
jsonObjectService.put(GATEWAY_MSG_NAME, SUCCESS_META.getError().getMsg()); |
||||||
|
} |
||||||
|
return this.merge(jsonObjectService); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
异常示例 |
||||||
|
{ |
||||||
|
"alipay_trade_fastpay_refund_query_response": { |
||||||
|
"code": "20000", |
||||||
|
"msg": "Service Currently Unavailable", |
||||||
|
"sub_code": "isp.unknow-error", |
||||||
|
"sub_msg": "系统繁忙" |
||||||
|
}, |
||||||
|
"sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE" |
||||||
|
} |
||||||
|
*/ |
||||||
|
public String mergeError(String serviceResult) { |
||||||
|
JSONObject jsonObjectService = new JSONObject(); |
||||||
|
jsonObjectService.put(GATEWAY_CODE_NAME, ISP_UNKNOW_ERROR_META.getCode()); |
||||||
|
jsonObjectService.put(GATEWAY_MSG_NAME, ISP_UNKNOW_ERROR_META.getError().getMsg()); |
||||||
|
return this.merge(jsonObjectService); |
||||||
|
} |
||||||
|
|
||||||
|
private String merge(JSONObject jsonObjectService) { |
||||||
|
JSONObject ret = new JSONObject(); |
||||||
|
ApiParam apiParam = ApiContext.getApiParam(); |
||||||
|
// 点换成下划线
|
||||||
|
String apiName = apiParam.fetchName().replace(DOT, UNDERLINE); |
||||||
|
ret.put(apiName + DATA_SUFFIX, jsonObjectService); |
||||||
|
ret.put(ParamNames.SIGN_NAME, apiParam.fetchSign()); |
||||||
|
return ret.toJSONString(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.result; |
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON; |
||||||
|
|
||||||
|
/** |
||||||
|
* 序列化json |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class JsonResultSerializer implements ResultSerializer { |
||||||
|
|
||||||
|
@Override |
||||||
|
public String serialize(Object obj) { |
||||||
|
return JSON.toJSONString(obj); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.result; |
||||||
|
|
||||||
|
import java.io.Serializable; |
||||||
|
|
||||||
|
/** |
||||||
|
* 返回结果,后续自定义的返回类需要实现这个接口。 |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public interface Result extends Serializable { |
||||||
|
|
||||||
|
} |
@ -0,0 +1,27 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.result; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.message.Error; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public interface ResultBuilder { |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 构建网关错误返回结果 |
||||||
|
* |
||||||
|
* @param throwable 异常 |
||||||
|
* @return 返回最终结果 |
||||||
|
*/ |
||||||
|
Result buildGatewayError(Throwable throwable); |
||||||
|
|
||||||
|
/** |
||||||
|
* 构建网关错误返回结果 |
||||||
|
* |
||||||
|
* @param error error |
||||||
|
* @return 返回最终结果 |
||||||
|
*/ |
||||||
|
Result buildGatewayError(Error error); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.result; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public interface ResultExecutor { |
||||||
|
String mergeResult(int responseStatus, String responseData); |
||||||
|
|
||||||
|
String mergeError(Throwable throwable); |
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.result; |
||||||
|
|
||||||
|
/** |
||||||
|
* 对象序列化 |
||||||
|
* @author tanghc |
||||||
|
* |
||||||
|
*/ |
||||||
|
public interface ResultSerializer { |
||||||
|
/** |
||||||
|
* 序列化 |
||||||
|
* |
||||||
|
* @param obj |
||||||
|
* @return 返回序列化后的结果 |
||||||
|
*/ |
||||||
|
String serialize(Object obj); |
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.result; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.util.XmlUtil; |
||||||
|
|
||||||
|
/** |
||||||
|
* 序列化成xml |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class XmlResultSerializer implements ResultSerializer { |
||||||
|
|
||||||
|
@Override |
||||||
|
public String serialize(Object obj) { |
||||||
|
return XmlUtil.serialize(obj); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.secret; |
||||||
|
|
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* 负责秘钥管理 |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public interface AppSecretManager { |
||||||
|
|
||||||
|
/** |
||||||
|
* 初始化秘钥数据 |
||||||
|
* @param appSecretStore |
||||||
|
*/ |
||||||
|
void addAppSecret(Map<String, String> appSecretStore); |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取应用程序的密钥 |
||||||
|
* |
||||||
|
* @param appKey |
||||||
|
* @return 返回秘钥 |
||||||
|
*/ |
||||||
|
String getSecret(String appKey); |
||||||
|
|
||||||
|
/** |
||||||
|
* 是否是合法的appKey |
||||||
|
* |
||||||
|
* @param appKey |
||||||
|
* @return 返回appKey |
||||||
|
*/ |
||||||
|
boolean isValidAppKey(String appKey); |
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.secret; |
||||||
|
|
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* appkey,secret默认管理,简单放在map中,如果要放在redis中,可以参照此方式实现AppSecretManager,然后在ApiConfig中setAppSecretManager() |
||||||
|
* @author tanghc |
||||||
|
* |
||||||
|
*/ |
||||||
|
public class CacheAppSecretManager implements AppSecretManager { |
||||||
|
|
||||||
|
private Map<String, String> secretMap = new HashMap<String, String>(64); |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addAppSecret(Map<String, String> appSecretStore) { |
||||||
|
secretMap.putAll(appSecretStore); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getSecret(String appKey) { |
||||||
|
return secretMap.get(appKey); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isValidAppKey(String appKey) { |
||||||
|
if (appKey == null) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
return getSecret(appKey) != null; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,56 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.secret; |
||||||
|
|
||||||
|
import org.springframework.core.io.DefaultResourceLoader; |
||||||
|
import org.springframework.core.io.Resource; |
||||||
|
import org.springframework.core.io.support.PropertiesLoaderUtils; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Properties; |
||||||
|
|
||||||
|
/** |
||||||
|
* appkey,secret文件管理,功能同CacheAppSecretManager,这个是将appKey,secret放在属性文件中<br> |
||||||
|
* key为appKey,value为secret |
||||||
|
* @author tanghc |
||||||
|
* |
||||||
|
*/ |
||||||
|
public class FileAppSecretManager implements AppSecretManager { |
||||||
|
|
||||||
|
private String appSecretFile = "appSecret.properties"; |
||||||
|
|
||||||
|
private Properties properties; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addAppSecret(Map<String, String> appSecretStore) { |
||||||
|
properties.putAll(appSecretStore); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getSecret(String appKey) { |
||||||
|
if (properties == null) { |
||||||
|
try { |
||||||
|
// 默认加载class根目录的appSecret.properties文件
|
||||||
|
DefaultResourceLoader resourceLoader = new DefaultResourceLoader(); |
||||||
|
Resource resource = resourceLoader.getResource(appSecretFile); |
||||||
|
properties = PropertiesLoaderUtils.loadProperties(resource); |
||||||
|
} catch (IOException e) { |
||||||
|
throw new RuntimeException("在类路径下找不到appSecret.properties的应用密钥的属性文件"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return properties.getProperty(appKey); |
||||||
|
} |
||||||
|
|
||||||
|
public void setAppSecretFile(String appSecretFile) { |
||||||
|
this.appSecretFile = appSecretFile; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isValidAppKey(String appKey) { |
||||||
|
if(appKey == null){ |
||||||
|
return false; |
||||||
|
} |
||||||
|
return getSecret(appKey) != null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,242 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.session; |
||||||
|
|
||||||
|
import org.springframework.util.Assert; |
||||||
|
|
||||||
|
import javax.servlet.ServletContext; |
||||||
|
import javax.servlet.http.HttpSession; |
||||||
|
import javax.servlet.http.HttpSessionBindingEvent; |
||||||
|
import javax.servlet.http.HttpSessionBindingListener; |
||||||
|
import javax.servlet.http.HttpSessionContext; |
||||||
|
import java.io.Serializable; |
||||||
|
import java.util.Enumeration; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Iterator; |
||||||
|
import java.util.LinkedHashMap; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.UUID; |
||||||
|
import java.util.Vector; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
@SuppressWarnings("deprecation") |
||||||
|
public class ApiHttpSession implements HttpSession, Serializable { |
||||||
|
private static final long serialVersionUID = 946272038219216222L; |
||||||
|
|
||||||
|
private final String id; |
||||||
|
|
||||||
|
private final long creationTime = System.currentTimeMillis(); |
||||||
|
|
||||||
|
private int maxInactiveInterval; |
||||||
|
|
||||||
|
private long lastAccessedTime = System.currentTimeMillis(); |
||||||
|
|
||||||
|
private final ServletContext servletContext; |
||||||
|
|
||||||
|
private final Map<String, Object> attributes = new LinkedHashMap<String, Object>(); |
||||||
|
|
||||||
|
private boolean invalid = false; |
||||||
|
|
||||||
|
private boolean isNew = true; |
||||||
|
|
||||||
|
/** |
||||||
|
* Create a new ApiHttpSession |
||||||
|
*/ |
||||||
|
public ApiHttpSession() { |
||||||
|
this(null); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create a new ApiHttpSession. |
||||||
|
* |
||||||
|
* @param servletContext |
||||||
|
* the ServletContext that the session runs in |
||||||
|
*/ |
||||||
|
public ApiHttpSession(ServletContext servletContext) { |
||||||
|
this(servletContext, null); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create a new ApiHttpSession. |
||||||
|
* |
||||||
|
* @param servletContext |
||||||
|
* the ServletContext that the session runs in |
||||||
|
* @param id |
||||||
|
* a unique identifier for this session |
||||||
|
*/ |
||||||
|
public ApiHttpSession(ServletContext servletContext, String id) { |
||||||
|
this.servletContext = servletContext; |
||||||
|
this.id = this.buildId(id); |
||||||
|
} |
||||||
|
|
||||||
|
protected String buildId(String id) { |
||||||
|
return (id != null ? id : UUID.randomUUID().toString().replace("-", "").toUpperCase()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public long getCreationTime() { |
||||||
|
return this.creationTime; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getId() { |
||||||
|
return this.id; |
||||||
|
} |
||||||
|
|
||||||
|
public void access() { |
||||||
|
this.lastAccessedTime = System.currentTimeMillis(); |
||||||
|
this.isNew = false; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public long getLastAccessedTime() { |
||||||
|
return this.lastAccessedTime; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public ServletContext getServletContext() { |
||||||
|
return this.servletContext; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setMaxInactiveInterval(int interval) { |
||||||
|
this.maxInactiveInterval = interval; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getMaxInactiveInterval() { |
||||||
|
return this.maxInactiveInterval; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public HttpSessionContext getSessionContext() { |
||||||
|
throw new UnsupportedOperationException("getSessionContext"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object getAttribute(String name) { |
||||||
|
Assert.notNull(name, "Attribute name must not be null"); |
||||||
|
return this.attributes.get(name); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object getValue(String name) { |
||||||
|
return getAttribute(name); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Enumeration<String> getAttributeNames() { |
||||||
|
return new Vector<String>(this.attributes.keySet()).elements(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String[] getValueNames() { |
||||||
|
return this.attributes.keySet().toArray(new String[this.attributes.size()]); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setAttribute(String name, Object value) { |
||||||
|
Assert.notNull(name, "Attribute name must not be null"); |
||||||
|
if (value != null) { |
||||||
|
this.attributes.put(name, value); |
||||||
|
if (value instanceof HttpSessionBindingListener) { |
||||||
|
((HttpSessionBindingListener) value).valueBound(new HttpSessionBindingEvent(this, name, value)); |
||||||
|
} |
||||||
|
} else { |
||||||
|
removeAttribute(name); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void putValue(String name, Object value) { |
||||||
|
setAttribute(name, value); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeAttribute(String name) { |
||||||
|
Assert.notNull(name, "Attribute name must not be null"); |
||||||
|
Object value = this.attributes.remove(name); |
||||||
|
if (value instanceof HttpSessionBindingListener) { |
||||||
|
((HttpSessionBindingListener) value).valueUnbound(new HttpSessionBindingEvent(this, name, value)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeValue(String name) { |
||||||
|
removeAttribute(name); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Clear all of this session's attributes. |
||||||
|
*/ |
||||||
|
public void clearAttributes() { |
||||||
|
for (Iterator<Map.Entry<String, Object>> it = this.attributes.entrySet().iterator(); it.hasNext();) { |
||||||
|
Map.Entry<String, Object> entry = it.next(); |
||||||
|
String name = entry.getKey(); |
||||||
|
Object value = entry.getValue(); |
||||||
|
it.remove(); |
||||||
|
if (value instanceof HttpSessionBindingListener) { |
||||||
|
((HttpSessionBindingListener) value).valueUnbound(new HttpSessionBindingEvent(this, name, value)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void invalidate() { |
||||||
|
this.invalid = true; |
||||||
|
clearAttributes(); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isInvalid() { |
||||||
|
return this.invalid; |
||||||
|
} |
||||||
|
|
||||||
|
public void setNew(boolean value) { |
||||||
|
this.isNew = value; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isNew() { |
||||||
|
return this.isNew; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Serialize the attributes of this session into an object that can be |
||||||
|
* turned into a byte array with standard Java serialization. |
||||||
|
* |
||||||
|
* @return a representation of this session's serialized state |
||||||
|
*/ |
||||||
|
public Serializable serializeState() { |
||||||
|
HashMap<String, Serializable> state = new HashMap<String, Serializable>(); |
||||||
|
for (Iterator<Map.Entry<String, Object>> it = this.attributes.entrySet().iterator(); it.hasNext();) { |
||||||
|
Map.Entry<String, Object> entry = it.next(); |
||||||
|
String name = entry.getKey(); |
||||||
|
Object value = entry.getValue(); |
||||||
|
it.remove(); |
||||||
|
if (value instanceof Serializable) { |
||||||
|
state.put(name, (Serializable) value); |
||||||
|
} else { |
||||||
|
// Not serializable... Servlet containers usually automatically
|
||||||
|
// unbind the attribute in this case.
|
||||||
|
if (value instanceof HttpSessionBindingListener) { |
||||||
|
((HttpSessionBindingListener) value).valueUnbound(new HttpSessionBindingEvent(this, name, value)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return state; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Deserialize the attributes of this session from a state object created by |
||||||
|
* {@link #serializeState()}. |
||||||
|
* |
||||||
|
* @param state |
||||||
|
* a representation of this session's serialized state |
||||||
|
*/ |
||||||
|
@SuppressWarnings("unchecked") |
||||||
|
public void deserializeState(Serializable state) { |
||||||
|
Assert.isTrue(state instanceof Map, "Serialized state needs to be of type [java.util.Map]"); |
||||||
|
this.attributes.putAll((Map<String, Object>) state); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.session; |
||||||
|
|
||||||
|
import org.springframework.data.redis.connection.DefaultStringRedisConnection; |
||||||
|
import org.springframework.data.redis.connection.RedisConnection; |
||||||
|
import org.springframework.data.redis.connection.RedisConnectionFactory; |
||||||
|
import org.springframework.data.redis.core.RedisTemplate; |
||||||
|
import org.springframework.data.redis.serializer.RedisSerializer; |
||||||
|
import org.springframework.data.redis.serializer.StringRedisSerializer; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class ApiRedisTemplate extends RedisTemplate<String, Object> { |
||||||
|
public ApiRedisTemplate() { |
||||||
|
RedisSerializer<String> stringSerializer = new StringRedisSerializer(); |
||||||
|
setKeySerializer(stringSerializer); |
||||||
|
setHashKeySerializer(stringSerializer); |
||||||
|
} |
||||||
|
|
||||||
|
public ApiRedisTemplate(RedisConnectionFactory connectionFactory) { |
||||||
|
this(); |
||||||
|
setConnectionFactory(connectionFactory); |
||||||
|
afterPropertiesSet(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) { |
||||||
|
return new DefaultStringRedisConnection(connection); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,100 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.session; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.bean.ApiContext; |
||||||
|
import com.gitee.sop.gatewaycommon.message.ErrorEnum; |
||||||
|
import com.google.common.cache.CacheBuilder; |
||||||
|
import com.google.common.cache.CacheLoader; |
||||||
|
import com.google.common.cache.LoadingCache; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
import javax.servlet.ServletContext; |
||||||
|
import javax.servlet.http.HttpSession; |
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
|
||||||
|
/** |
||||||
|
* session管理,默认存放的是{@link ApiHttpSession}。采用谷歌guava缓存实现。 |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
* |
||||||
|
*/ |
||||||
|
public class ApiSessionManager implements SessionManager { |
||||||
|
private static Logger logger = LoggerFactory.getLogger(ApiSessionManager.class); |
||||||
|
|
||||||
|
private int sessionTimeout = 20; |
||||||
|
|
||||||
|
private LoadingCache<String, HttpSession> cache; |
||||||
|
|
||||||
|
public ApiSessionManager() { |
||||||
|
cache = this.buildCache(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public HttpSession getSession(String sessionId) { |
||||||
|
if(sessionId == null) { |
||||||
|
return this.createSession(sessionId); |
||||||
|
} |
||||||
|
try { |
||||||
|
HttpSession session = cache.get(sessionId); |
||||||
|
return session; |
||||||
|
} catch (Exception e) { |
||||||
|
logger.error(e.getMessage(), e); |
||||||
|
throw ErrorEnum.AOP_UNKNOW_ERROR.getErrorMeta().getException(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 创建一个session |
||||||
|
* |
||||||
|
* @param sessionId 传null将返回一个新session |
||||||
|
* @return 返回session |
||||||
|
*/ |
||||||
|
protected HttpSession createSession(String sessionId) { |
||||||
|
ServletContext servletContext = getServletContext(); |
||||||
|
HttpSession session = this.newSession(sessionId, servletContext); |
||||||
|
session.setMaxInactiveInterval(getSessionTimeout()); |
||||||
|
this.cache.put(session.getId(), session); |
||||||
|
return session; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 返回新的session实例 |
||||||
|
* |
||||||
|
* @param sessionId |
||||||
|
* @param servletContext |
||||||
|
* @return 返回session |
||||||
|
*/ |
||||||
|
protected HttpSession newSession(String sessionId, ServletContext servletContext) { |
||||||
|
return new ApiHttpSession(servletContext, sessionId); |
||||||
|
} |
||||||
|
|
||||||
|
protected ServletContext getServletContext() { |
||||||
|
return ApiContext.getServletContext(); |
||||||
|
} |
||||||
|
|
||||||
|
protected LoadingCache<String, HttpSession> buildCache() { |
||||||
|
return CacheBuilder.newBuilder().expireAfterAccess(getSessionTimeout(), TimeUnit.MINUTES) |
||||||
|
.build(new CacheLoader<String, HttpSession>() { |
||||||
|
// 找不到sessionId对应的HttpSession时,进入这个方法
|
||||||
|
// 找不到就新建一个
|
||||||
|
@Override |
||||||
|
public HttpSession load(String sessionId) throws Exception { |
||||||
|
return createSession(sessionId); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
public void setSessionTimeout(int sessionTimeout) { |
||||||
|
this.sessionTimeout = sessionTimeout; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 过期时间,分钟,默认20分钟 |
||||||
|
* |
||||||
|
* @return 返回过期时间 |
||||||
|
*/ |
||||||
|
public int getSessionTimeout() { |
||||||
|
return sessionTimeout; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,264 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.session; |
||||||
|
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate; |
||||||
|
import org.springframework.util.Assert; |
||||||
|
|
||||||
|
import javax.servlet.ServletContext; |
||||||
|
import javax.servlet.http.HttpSession; |
||||||
|
import javax.servlet.http.HttpSessionContext; |
||||||
|
import java.io.Serializable; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.Enumeration; |
||||||
|
import java.util.HashSet; |
||||||
|
import java.util.Set; |
||||||
|
import java.util.UUID; |
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
|
||||||
|
/** |
||||||
|
* RedisHttpSession |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
@SuppressWarnings("deprecation") |
||||||
|
public class RedisHttpSession implements HttpSession, Serializable { |
||||||
|
private static final long serialVersionUID = -8081963657251144855L; |
||||||
|
|
||||||
|
private static final int SEC60 = 60; |
||||||
|
|
||||||
|
private static final String SESSION_ATTR = "session_attr:"; |
||||||
|
private static final String CREATION_TIME = "creationTime"; |
||||||
|
private static final String LAST_ACCESSED_TIME = "lastAccessedTime"; |
||||||
|
private static final String MAX_INACTIVE_INTERVAL = "maxInactiveInterval"; |
||||||
|
|
||||||
|
/** |
||||||
|
* 存入redis的key |
||||||
|
*/ |
||||||
|
private String key; |
||||||
|
|
||||||
|
/** |
||||||
|
* sessionId |
||||||
|
*/ |
||||||
|
private String id; |
||||||
|
|
||||||
|
private ServletContext servletContext; |
||||||
|
|
||||||
|
private RedisTemplate redisTemplate; |
||||||
|
|
||||||
|
private RedisHttpSession() { |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
protected static String buildId(String id) { |
||||||
|
return (id != null ? id : UUID.randomUUID().toString().replace("-", "").toUpperCase()); |
||||||
|
} |
||||||
|
|
||||||
|
public static String buildKey(String keyPrefix, String sessionId) { |
||||||
|
Assert.notNull(keyPrefix, "sessionPrefix不能为null"); |
||||||
|
return keyPrefix + sessionId; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 创建新的session |
||||||
|
* |
||||||
|
* @param servletContext |
||||||
|
* @param sessionId |
||||||
|
* @param sessionTimeout 过期时间,单位秒 |
||||||
|
* @param redisTemplate redis客户端 |
||||||
|
* @param keyPrefix 存入的key前缀 |
||||||
|
* @return 返回session |
||||||
|
*/ |
||||||
|
public static RedisHttpSession createNewSession(ServletContext servletContext, String sessionId, int sessionTimeout, RedisTemplate redisTemplate, String keyPrefix) { |
||||||
|
Assert.notNull(redisTemplate, "redisTemplate can not null."); |
||||||
|
Assert.notNull(sessionId, "sessionId can not null."); |
||||||
|
Assert.notNull(keyPrefix, "keyPrefix can not null."); |
||||||
|
RedisHttpSession redisHttpSession = new RedisHttpSession(); |
||||||
|
redisHttpSession.setId(sessionId); |
||||||
|
redisHttpSession.setKey(buildKey(keyPrefix, sessionId)); |
||||||
|
redisHttpSession.setRedisTemplate(redisTemplate); |
||||||
|
redisHttpSession.setServletContext(servletContext); |
||||||
|
|
||||||
|
long creationTime = System.currentTimeMillis(); |
||||||
|
// 过期时间,分转换成秒
|
||||||
|
int maxInactiveInterval = sessionTimeout * SEC60; |
||||||
|
|
||||||
|
redisHttpSession.setCreationTime(creationTime); |
||||||
|
redisHttpSession.setLastAccessedTime(creationTime); |
||||||
|
redisHttpSession.setMaxInactiveInterval(maxInactiveInterval); |
||||||
|
|
||||||
|
redisHttpSession.refresh(); |
||||||
|
|
||||||
|
return redisHttpSession; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 创建已经存在的session,数据在redis里面 |
||||||
|
* |
||||||
|
* @param sessionId |
||||||
|
* @param servletContext |
||||||
|
* @param redisTemplate redis客户端 |
||||||
|
* @param keyPrefix 存入的key前缀 |
||||||
|
* @return 返回session |
||||||
|
*/ |
||||||
|
public static RedisHttpSession createExistSession(String sessionId, ServletContext servletContext, RedisTemplate redisTemplate, String keyPrefix) { |
||||||
|
Assert.notNull(redisTemplate, "redisTemplate can not null."); |
||||||
|
Assert.notNull(sessionId, "sessionId can not null."); |
||||||
|
Assert.notNull(keyPrefix, "keyPrefix can not null."); |
||||||
|
RedisHttpSession redisHttpSession = new RedisHttpSession(); |
||||||
|
redisHttpSession.setId(sessionId); |
||||||
|
redisHttpSession.setKey(buildKey(keyPrefix, sessionId)); |
||||||
|
redisHttpSession.setRedisTemplate(redisTemplate); |
||||||
|
redisHttpSession.setServletContext(servletContext); |
||||||
|
|
||||||
|
redisHttpSession.refresh(); |
||||||
|
|
||||||
|
return redisHttpSession; |
||||||
|
} |
||||||
|
|
||||||
|
public void setId(String id) { |
||||||
|
this.id = id; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public void setServletContext(ServletContext servletContext) { |
||||||
|
this.servletContext = servletContext; |
||||||
|
} |
||||||
|
|
||||||
|
public void setCreationTime(long creationTime) { |
||||||
|
this.redisTemplate.opsForHash().put(key, CREATION_TIME, String.valueOf(creationTime)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public long getCreationTime() { |
||||||
|
Object createTime = this.redisTemplate.opsForHash().get(key, CREATION_TIME); |
||||||
|
return Long.valueOf(String.valueOf(createTime)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getId() { |
||||||
|
return id; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public long getLastAccessedTime() { |
||||||
|
Object lastAccessedTime = this.redisTemplate.opsForHash().get(key, LAST_ACCESSED_TIME); |
||||||
|
return Long.valueOf(String.valueOf(lastAccessedTime)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public ServletContext getServletContext() { |
||||||
|
return servletContext; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setMaxInactiveInterval(int interval) { |
||||||
|
this.redisTemplate.opsForHash().put(key, MAX_INACTIVE_INTERVAL, String.valueOf(interval)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getMaxInactiveInterval() { |
||||||
|
Object maxInactiveInterval = this.redisTemplate.opsForHash().get(key, MAX_INACTIVE_INTERVAL); |
||||||
|
return Integer.valueOf(String.valueOf(maxInactiveInterval)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public HttpSessionContext getSessionContext() { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object getAttribute(String name) { |
||||||
|
return this.redisTemplate.opsForHash().get(key, SESSION_ATTR + name); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object getValue(String name) { |
||||||
|
return getAttribute(name); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Enumeration<String> getAttributeNames() { |
||||||
|
return Collections.enumeration(getAttributeKeys()); |
||||||
|
} |
||||||
|
|
||||||
|
private Set<String> getAttributeKeys() { |
||||||
|
Set keys = this.redisTemplate.opsForHash().keys(key); |
||||||
|
Set<String> attrNames = new HashSet<>(); |
||||||
|
for (Object key : keys) { |
||||||
|
String k = String.valueOf(key); |
||||||
|
if (k.startsWith(SESSION_ATTR)) { |
||||||
|
attrNames.add(k.substring(SESSION_ATTR.length())); |
||||||
|
} |
||||||
|
} |
||||||
|
return attrNames; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public String[] getValueNames() { |
||||||
|
return getAttributeKeys().toArray(new String[0]); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setAttribute(String name, Object value) { |
||||||
|
this.redisTemplate.opsForHash().put(key, SESSION_ATTR + name, value); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void putValue(String name, Object value) { |
||||||
|
setAttribute(name, value); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeAttribute(String name) { |
||||||
|
this.redisTemplate.opsForHash().delete(key, name); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeValue(String name) { |
||||||
|
removeAttribute(name); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void invalidate() { |
||||||
|
this.redisTemplate.delete(key); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isNew() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* update expireTime,accessTime |
||||||
|
*/ |
||||||
|
public void refresh() { |
||||||
|
// token更新过期时间
|
||||||
|
this.redisTemplate.expire(key, getMaxInactiveInterval(), TimeUnit.SECONDS); |
||||||
|
// 设置访问时间
|
||||||
|
this.setLastAccessedTime(System.currentTimeMillis()); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public void setLastAccessedTime(long lastAccessedTime) { |
||||||
|
this.redisTemplate.opsForHash().put(key, LAST_ACCESSED_TIME, String.valueOf(lastAccessedTime)); |
||||||
|
} |
||||||
|
|
||||||
|
public String getKey() { |
||||||
|
return key; |
||||||
|
} |
||||||
|
|
||||||
|
public void setKey(String key) { |
||||||
|
this.key = key; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isInvalidated() { |
||||||
|
return !this.redisTemplate.hasKey(key); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public void setRedisTemplate(RedisTemplate redisTemplate) { |
||||||
|
this.redisTemplate = redisTemplate; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,100 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.session; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.bean.ApiContext; |
||||||
|
import org.springframework.data.redis.core.RedisTemplate; |
||||||
|
import org.springframework.util.Assert; |
||||||
|
|
||||||
|
import javax.servlet.ServletContext; |
||||||
|
import javax.servlet.http.HttpSession; |
||||||
|
import java.util.UUID; |
||||||
|
|
||||||
|
/** |
||||||
|
* SessionManager的redis实现,使用redis管理session |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
* |
||||||
|
*/ |
||||||
|
public class RedisSessionManager implements SessionManager { |
||||||
|
|
||||||
|
private ApiRedisTemplate redisTemplate; |
||||||
|
|
||||||
|
/** 过期时间,30分钟 */ |
||||||
|
private int sessionTimeout = 30; |
||||||
|
/** 存入redis中key的前缀 */ |
||||||
|
private String keyPrefix = "session:"; |
||||||
|
|
||||||
|
public RedisSessionManager(@SuppressWarnings("rawtypes") RedisTemplate redisTemplate) { |
||||||
|
Assert.notNull(redisTemplate, "RedisSessionManager中的redisTemplate不能为null"); |
||||||
|
this.redisTemplate = new ApiRedisTemplate(redisTemplate.getConnectionFactory()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public HttpSession getSession(String sessionId) { |
||||||
|
return this.getSession(sessionId, this.keyPrefix); |
||||||
|
} |
||||||
|
|
||||||
|
public HttpSession getSession(String sessionId, String keyPrefix) { |
||||||
|
if (this.hasKey(sessionId)) { |
||||||
|
return RedisHttpSession.createExistSession(sessionId, getServletContext(), redisTemplate, keyPrefix); |
||||||
|
} else { |
||||||
|
sessionId = this.buildSessionId(sessionId); |
||||||
|
return RedisHttpSession.createNewSession(getServletContext(), sessionId, this.getSessionTimeout(), |
||||||
|
redisTemplate, keyPrefix); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 构建sessionId |
||||||
|
* @param id |
||||||
|
* @return 返回sessionid |
||||||
|
*/ |
||||||
|
public String buildSessionId(String id) { |
||||||
|
return (id != null ? id : UUID.randomUUID().toString().replace("-", "").toUpperCase()); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean hasKey(String sessionId) { |
||||||
|
if (sessionId == null) { |
||||||
|
return false; |
||||||
|
} else { |
||||||
|
String key = RedisHttpSession.buildKey(this.keyPrefix, sessionId); |
||||||
|
return redisTemplate.hasKey(key); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public ServletContext getServletContext() { |
||||||
|
return ApiContext.getServletContext(); |
||||||
|
} |
||||||
|
|
||||||
|
public int getSessionTimeout() { |
||||||
|
return sessionTimeout; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 设置session过期时间,单位分钟 |
||||||
|
* @param sessionTimeout |
||||||
|
*/ |
||||||
|
public void setSessionTimeout(int sessionTimeout) { |
||||||
|
this.sessionTimeout = sessionTimeout; |
||||||
|
} |
||||||
|
|
||||||
|
public ApiRedisTemplate getRedisTemplate() { |
||||||
|
return redisTemplate; |
||||||
|
} |
||||||
|
|
||||||
|
public void setRedisTemplate(ApiRedisTemplate redisTemplate) { |
||||||
|
this.redisTemplate = redisTemplate; |
||||||
|
} |
||||||
|
|
||||||
|
public String getKeyPrefix() { |
||||||
|
return keyPrefix; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 设置存入redis中key的前缀,默认为"session:" |
||||||
|
* @param keyPrefix |
||||||
|
*/ |
||||||
|
public void setKeyPrefix(String keyPrefix) { |
||||||
|
this.keyPrefix = keyPrefix; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.session; |
||||||
|
|
||||||
|
import javax.servlet.http.HttpSession; |
||||||
|
|
||||||
|
/** |
||||||
|
* session管理 |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
* |
||||||
|
*/ |
||||||
|
public interface SessionManager { |
||||||
|
|
||||||
|
/** |
||||||
|
* 根据sessionId获取session |
||||||
|
* |
||||||
|
* @param sessionId 客户端传过来的sessionId,为null时创建一个新session |
||||||
|
* @return 返回session |
||||||
|
*/ |
||||||
|
HttpSession getSession(String sessionId); |
||||||
|
} |
@ -0,0 +1,146 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.util; |
||||||
|
|
||||||
|
import org.apache.commons.codec.binary.Base64; |
||||||
|
import org.apache.commons.codec.binary.Hex; |
||||||
|
|
||||||
|
import javax.crypto.Cipher; |
||||||
|
import javax.crypto.SecretKey; |
||||||
|
import javax.crypto.spec.SecretKeySpec; |
||||||
|
|
||||||
|
/** |
||||||
|
* AES-128 ECB加密.<br> |
||||||
|
* |
||||||
|
* <pre> |
||||||
|
* 字符集:UTF-8 |
||||||
|
* 算法模式:ECB |
||||||
|
* 数据块:128位 |
||||||
|
* 补码方式:PKCS5Padding |
||||||
|
* 加密结果编码方式:Base64 |
||||||
|
* </pre> |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
* |
||||||
|
*/ |
||||||
|
public class AESUtil { |
||||||
|
private static final String UTF8 = "UTF-8"; |
||||||
|
private static final String ALGORITHM = "AES"; |
||||||
|
/** 默认的加密算法 */ |
||||||
|
private static final String ALGORITHM_CIPHER = "AES/ECB/PKCS5Padding"; |
||||||
|
|
||||||
|
private static final int LIMIT_LEN = 16; |
||||||
|
|
||||||
|
/** |
||||||
|
* 生成一个SecretKey |
||||||
|
* @param password 长度必须小于等于16 |
||||||
|
* @return 返回SecretKey |
||||||
|
*/ |
||||||
|
public static SecretKey getSecretKey(String password) { |
||||||
|
byte[] passwordData = password.getBytes(); |
||||||
|
if(passwordData.length > LIMIT_LEN) { |
||||||
|
throw new IllegalArgumentException("password 长度必须小于等于16"); |
||||||
|
} |
||||||
|
// 创建一个空的16位字节数组(默认值为0),16byte(128bit)
|
||||||
|
byte[] keyData = new byte[16]; |
||||||
|
System.arraycopy(passwordData, 0, keyData, 0, passwordData.length); |
||||||
|
|
||||||
|
return new SecretKeySpec(keyData, ALGORITHM); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 加密 |
||||||
|
* @param data 待加密数据 |
||||||
|
* @param password 密码 |
||||||
|
* @return 返回加密成功后数据 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
public static byte[] encrypt(byte[] data, String password) throws Exception { |
||||||
|
SecretKey secretKey = getSecretKey(password); |
||||||
|
// Ciphr完成加密或解密工作类
|
||||||
|
Cipher cipher = Cipher.getInstance(ALGORITHM_CIPHER); |
||||||
|
// 对Cipher初始化,解密模式
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, secretKey); |
||||||
|
// 加密data
|
||||||
|
return cipher.doFinal(data); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 解密 |
||||||
|
* @param data 待解密数据 |
||||||
|
* @param password 密码 |
||||||
|
* @return 返回解密后的数据 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
public static byte[] decrypt(byte[] data, String password) throws Exception { |
||||||
|
SecretKey secretKey = getSecretKey(password); |
||||||
|
// Cipher完成加密或解密工作类
|
||||||
|
Cipher cipher = Cipher.getInstance(ALGORITHM_CIPHER); |
||||||
|
// 对Cipher初始化,解密模式
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, secretKey); |
||||||
|
// 解密data
|
||||||
|
return cipher.doFinal(data); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 文本加密 |
||||||
|
* @param content 明文 |
||||||
|
* @param password 密码 |
||||||
|
* @return 返回base64内容 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
public static String encryptToBase64String(String content, String password) throws Exception { |
||||||
|
byte[] data = content.getBytes(UTF8); |
||||||
|
byte[] result = encrypt(data, password); |
||||||
|
return Base64.encodeBase64String(result); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 文本解密 |
||||||
|
* @param base64String 待解密文本 |
||||||
|
* @param password 密码 |
||||||
|
* @return 返回明文 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
public static String decryptFromBase64String(String base64String, String password) throws Exception { |
||||||
|
byte[] data = Base64.decodeBase64(base64String); |
||||||
|
byte[] contentData = decrypt(data, password); |
||||||
|
return new String(contentData, UTF8); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 文本加密 |
||||||
|
* @param content 明文 |
||||||
|
* @param password 密码 |
||||||
|
* @return 返回16进制内容 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
public static String encryptToHex(String content, String password) throws Exception { |
||||||
|
byte[] data = content.getBytes(UTF8); |
||||||
|
byte[] result = encrypt(data, password); |
||||||
|
return Hex.encodeHexString(result); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 文本解密 |
||||||
|
* @param hex 待解密文本 |
||||||
|
* @param password 密码 |
||||||
|
* @return 返回明文 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
public static String decryptFromHex(String hex, String password) throws Exception { |
||||||
|
byte[] data = Hex.decodeHex(hex); |
||||||
|
byte[] contentData = decrypt(data, password); |
||||||
|
return new String(contentData,UTF8); |
||||||
|
} |
||||||
|
|
||||||
|
/*public static void main(String[] args) throws Exception { |
||||||
|
String content = "我爱你"; |
||||||
|
String password = "1234567890123456"; |
||||||
|
System.out.println("password:" + password); |
||||||
|
|
||||||
|
String ret2 = encryptToBase64String(content, password); |
||||||
|
System.out.println("密文:" + ret2); |
||||||
|
String content3 = decryptFromBase64String(ret2, password); |
||||||
|
System.out.println(content.equals(content3)); |
||||||
|
}*/ |
||||||
|
|
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.util; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class KeyStore { |
||||||
|
private String publicKey; |
||||||
|
private String privateKey; |
||||||
|
|
||||||
|
public String getPublicKey() { |
||||||
|
return publicKey; |
||||||
|
} |
||||||
|
|
||||||
|
public void setPublicKey(String publicKey) { |
||||||
|
this.publicKey = publicKey; |
||||||
|
} |
||||||
|
|
||||||
|
public String getPrivateKey() { |
||||||
|
return privateKey; |
||||||
|
} |
||||||
|
|
||||||
|
public void setPrivateKey(String privateKey) { |
||||||
|
this.privateKey = privateKey; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,208 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.util; |
||||||
|
|
||||||
|
import org.apache.commons.codec.binary.Base64; |
||||||
|
|
||||||
|
import javax.crypto.Cipher; |
||||||
|
import java.security.KeyFactory; |
||||||
|
import java.security.KeyPair; |
||||||
|
import java.security.KeyPairGenerator; |
||||||
|
import java.security.interfaces.RSAPrivateKey; |
||||||
|
import java.security.interfaces.RSAPublicKey; |
||||||
|
import java.security.spec.PKCS8EncodedKeySpec; |
||||||
|
import java.security.spec.X509EncodedKeySpec; |
||||||
|
|
||||||
|
/** |
||||||
|
* RSA加解密工具<br> |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class RSANewUtil { |
||||||
|
public static final String RSA_ALGORITHM = "RSA"; |
||||||
|
public static final String UTF8 = "UTF-8"; |
||||||
|
|
||||||
|
/** |
||||||
|
* 创建公钥私钥 |
||||||
|
* |
||||||
|
* @return 返回公私钥对 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
public static KeyStore createKeys() throws Exception { |
||||||
|
KeyPairGenerator keyPairGeno = KeyPairGenerator.getInstance(RSA_ALGORITHM); |
||||||
|
keyPairGeno.initialize(1024); |
||||||
|
KeyPair keyPair = keyPairGeno.generateKeyPair(); |
||||||
|
|
||||||
|
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); |
||||||
|
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); |
||||||
|
|
||||||
|
KeyStore keyStore = new KeyStore(); |
||||||
|
keyStore.setPublicKey(Base64.encodeBase64String(publicKey.getEncoded())); |
||||||
|
keyStore.setPrivateKey(Base64.encodeBase64String(privateKey.getEncoded())); |
||||||
|
return keyStore; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取公钥对象 |
||||||
|
* |
||||||
|
* @param pubKeyData 公钥数据 |
||||||
|
* @return 公钥对象 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
public static RSAPublicKey getPublicKey(byte[] pubKeyData) throws Exception { |
||||||
|
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(pubKeyData); |
||||||
|
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM); |
||||||
|
return (RSAPublicKey) keyFactory.generatePublic(keySpec); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取公钥对象 |
||||||
|
* |
||||||
|
* @param pubKey |
||||||
|
* 公钥 |
||||||
|
* @return 返回公钥对象 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
public static RSAPublicKey getPublicKey(String pubKey) throws Exception { |
||||||
|
return getPublicKey(Base64.decodeBase64(pubKey)); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取私钥对象 |
||||||
|
* |
||||||
|
* @param priKey |
||||||
|
* 私钥 |
||||||
|
* @return 返回私钥对象 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
public static RSAPrivateKey getPrivateKey(String priKey) throws Exception { |
||||||
|
return getPrivateKey(Base64.decodeBase64(priKey)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 通过私钥byte[]将公钥还原,适用于RSA算法 |
||||||
|
* |
||||||
|
* @param keyBytes 私钥数据 |
||||||
|
* @return 返回公钥对象 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
public static RSAPrivateKey getPrivateKey(byte[] keyBytes) throws Exception { |
||||||
|
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); |
||||||
|
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM); |
||||||
|
return (RSAPrivateKey) keyFactory.generatePrivate(keySpec); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public static String encryptByPublicKey(String data, String publicKey) throws Exception { |
||||||
|
return encryptByPublicKey(data, getPublicKey(publicKey)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 公钥加密 |
||||||
|
* |
||||||
|
* @param data 内容 |
||||||
|
* @param publicKey 公钥 |
||||||
|
* @return 返回密文 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
public static String encryptByPublicKey(String data, RSAPublicKey publicKey) throws Exception { |
||||||
|
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM); |
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, publicKey); |
||||||
|
byte[] bytes = cipher.doFinal(data.getBytes(UTF8)); |
||||||
|
return Base64.encodeBase64String(bytes); |
||||||
|
} |
||||||
|
|
||||||
|
public static String decryptByPublicKey(String data, String rsaPublicKey) throws Exception { |
||||||
|
return decryptByPublicKey(data, getPublicKey(rsaPublicKey)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 公钥解密 |
||||||
|
* |
||||||
|
* @param data 待解密内容 |
||||||
|
* @param rsaPublicKey 公钥 |
||||||
|
* @return 返回明文 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
public static String decryptByPublicKey(String data, RSAPublicKey rsaPublicKey) throws Exception { |
||||||
|
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM); |
||||||
|
cipher.init(Cipher.DECRYPT_MODE, rsaPublicKey); |
||||||
|
byte[] inputData = Base64.decodeBase64(data); |
||||||
|
byte[] bytes = cipher.doFinal(inputData); |
||||||
|
return new String(bytes, UTF8); |
||||||
|
} |
||||||
|
|
||||||
|
public static String encryptByPrivateKey(String data, String privateKey) throws Exception { |
||||||
|
return encryptByPrivateKey(data, getPrivateKey(privateKey)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 私钥加密 |
||||||
|
* |
||||||
|
* @param data 内容 |
||||||
|
* @param privateKey 私钥 |
||||||
|
* @return 返回密文 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
public static String encryptByPrivateKey(String data, RSAPrivateKey privateKey) throws Exception { |
||||||
|
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM); |
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, privateKey); |
||||||
|
byte[] bytes = cipher.doFinal(data.getBytes(UTF8)); |
||||||
|
return Base64.encodeBase64String(bytes); |
||||||
|
} |
||||||
|
|
||||||
|
public static String decryptByPrivateKey(String data, String privateKey) throws Exception { |
||||||
|
return decryptByPrivateKey(data, getPrivateKey(privateKey)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 私钥解密 |
||||||
|
* |
||||||
|
* @param data 待解密内容 |
||||||
|
* @param privateKey 私钥 |
||||||
|
* @return 返回明文 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
public static String decryptByPrivateKey(String data, RSAPrivateKey privateKey) throws Exception { |
||||||
|
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM); |
||||||
|
cipher.init(Cipher.DECRYPT_MODE, privateKey); |
||||||
|
byte[] inputData = Base64.decodeBase64(data); |
||||||
|
byte[] bytes = cipher.doFinal(inputData); |
||||||
|
return new String(bytes, UTF8); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/* |
||||||
|
pubKey: |
||||||
|
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCG/iIZZzb16PxKqslkDMYa4tVFb3IVPBpLj4BgHQmDfe843sG4gkJIPXCm7+t6QxIbfDfynBpqZJLvu0c6E7TqlCtynBIlRFOBZrQVNEFkaanR2Kln3vd3CIidR571UstOC32XDyqAQNlvjD19zeIDVfmLa0Q+Or0zaxY99QwBHwIDAQAB |
||||||
|
priKey: |
||||||
|
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIb+IhlnNvXo/EqqyWQMxhri1UVvchU8GkuPgGAdCYN97zjewbiCQkg9cKbv63pDEht8N/KcGmpkku+7RzoTtOqUK3KcEiVEU4FmtBU0QWRpqdHYqWfe93cIiJ1HnvVSy04LfZcPKoBA2W+MPX3N4gNV+YtrRD46vTNrFj31DAEfAgMBAAECgYBiNPQdwwcq86rHr2QAE4L0AF3ju+YlKKqAmg9s3PMU5ENq/jO0xZ7u6zPPXu/S7IR51m7lY0ecazqyiW6SA9AzYH7ImWWkZ4stZ03beTB2US3cSeJIkugoexoN5fQRAGZiZezTLs91CeJivESOZyDKnnQdgJ49mveBV5OvievD8QJBAMztpqiWWavdR4tqQ+plat+rwYoXqejsK3Hyfg0pVJqEdazve2sr74rla7yI9P47ZAh1sklCv0CO//ctICv366UCQQCoop3T0FeZtbKJG+fHzZvpAe63tXpdhLMaQvTBuXLG8vi78Wyfhg5r7HOWR0Z1V7nzF1gzMywL53Pmkq9tB65zAkAiHu/A4kfL9ewTqn3kaT6CP3baJ1aDEc+qCVYzms4bbDKruLQ0A/y+g7SMj8E7E2h0gCRPTm3JsgWsgjb5Gy6BAkAA8mjQd6sGQe7utilnBdCKTmh4v5wgSk53J0kYjWIHm/WpmIFzo90Q3hMIFP5gSk3Q/6CPKQpmRrZv5QL3KcPhAkEAuMoQbij/7hyLlIxRHZs2SMXxfHPiZgDc6rVi1KNxeq8HXTlERi7Npc2Uz5TeWN4JwBBx9uA50zowk9iS05nclQ==
|
||||||
|
用公钥加密mi : c3B0jtMdvkqrgaPxHZCK2cXMUQC2QzLud2ouLMNx0nBAj9k2/ytOuVJViTGe/DozB/ky5jvl4spD9Ey6aTMrwLHfQVhn0gRJ+wHcmx/51dXQDIgsldt6bf7YpdPdnghBjQz2+P5RhqSkeFDbTZKkl2BNaLE78a/OyWWeCGwN+4s= |
||||||
|
true |
||||||
|
用私钥加密mi2 : QU5vDnQ1ukj8GsauokFlgcB/g61U882tj82wHGrrqHEnvaga+4cXjML9RhjpZtKqwDGZTCujsmpynDk4qek6IGOQ/oxdWLwV4ZNjfa/oqA8OFDothVUT8wpqCu9kOYHrTdGybmXD0dB2Iy1/AMQTAgPNNXXiRXdvsz9xWYTV6z8= |
||||||
|
true |
||||||
|
*/ |
||||||
|
/*public static void main(String[] args) throws Exception { |
||||||
|
KeyStore keys = createKeys(); |
||||||
|
String pubKey = keys.getPublicKey(); |
||||||
|
System.out.println("pubKey:"); |
||||||
|
System.out.println(pubKey); |
||||||
|
String priKey = keys.getPrivateKey(); |
||||||
|
System.out.println("priKey:"); |
||||||
|
System.out.println(priKey); |
||||||
|
|
||||||
|
String ming = "1234567890123456"; |
||||||
|
// 用公钥加密
|
||||||
|
String mi = encryptByPublicKey(ming, pubKey); |
||||||
|
System.out.println("用公钥加密mi : " + mi); |
||||||
|
// 用私钥解密
|
||||||
|
System.out.println(ming.equals(decryptByPrivateKey(mi, priKey))); |
||||||
|
|
||||||
|
// 用私钥加密
|
||||||
|
String mi2 = encryptByPrivateKey(ming, priKey); |
||||||
|
|
||||||
|
System.out.println("用私钥加密mi2 : " + mi2); |
||||||
|
// 用公钥解密
|
||||||
|
String ming2 = decryptByPublicKey(mi2, pubKey); |
||||||
|
System.out.println(ming.equals(ming2)); |
||||||
|
}*/ |
||||||
|
|
||||||
|
} |
@ -0,0 +1,312 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.util; |
||||||
|
|
||||||
|
import org.apache.commons.codec.binary.Base64; |
||||||
|
|
||||||
|
import javax.crypto.Cipher; |
||||||
|
import java.security.KeyFactory; |
||||||
|
import java.security.KeyPair; |
||||||
|
import java.security.KeyPairGenerator; |
||||||
|
import java.security.interfaces.RSAPrivateKey; |
||||||
|
import java.security.interfaces.RSAPublicKey; |
||||||
|
import java.security.spec.PKCS8EncodedKeySpec; |
||||||
|
import java.security.spec.X509EncodedKeySpec; |
||||||
|
|
||||||
|
/** |
||||||
|
* RSA加解密工具<br> |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class RSAUtil { |
||||||
|
public static String RSA_ALGORITHM = "RSA"; |
||||||
|
|
||||||
|
/** |
||||||
|
* 创建公钥私钥 |
||||||
|
* |
||||||
|
* @return 返回公私钥对 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
public static KeyStore createKeys() throws Exception { |
||||||
|
KeyPairGenerator keyPairGeno = KeyPairGenerator.getInstance(RSA_ALGORITHM); |
||||||
|
keyPairGeno.initialize(1024); |
||||||
|
KeyPair keyPair = keyPairGeno.generateKeyPair(); |
||||||
|
|
||||||
|
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); |
||||||
|
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); |
||||||
|
|
||||||
|
KeyStore keyStore = new KeyStore(); |
||||||
|
keyStore.setPublicKey(Base64.encodeBase64String(publicKey.getEncoded())); |
||||||
|
keyStore.setPrivateKey(Base64.encodeBase64String(privateKey.getEncoded())); |
||||||
|
return keyStore; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取公钥对象 |
||||||
|
* |
||||||
|
* @param pubKeyData 公钥 |
||||||
|
* @return 返回公钥对象 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
public static RSAPublicKey getPublicKey(byte[] pubKeyData) throws Exception { |
||||||
|
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(pubKeyData); |
||||||
|
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM); |
||||||
|
return (RSAPublicKey) keyFactory.generatePublic(keySpec); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取公钥对象 |
||||||
|
* |
||||||
|
* @param pubKey |
||||||
|
* 公钥 |
||||||
|
* @return 返回私钥对象 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
public static RSAPublicKey getPublicKey(String pubKey) throws Exception { |
||||||
|
return getPublicKey(Base64.decodeBase64(pubKey)); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取私钥对象 |
||||||
|
* |
||||||
|
* @param priKey |
||||||
|
* 私钥 |
||||||
|
* @return 私钥对象 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
public static RSAPrivateKey getPrivateKey(String priKey) throws Exception { |
||||||
|
return getPrivateKey(Base64.decodeBase64(priKey)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 通过私钥byte[]将公钥还原,适用于RSA算法 |
||||||
|
* |
||||||
|
* @param keyBytes |
||||||
|
* @return 返回私钥 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
public static RSAPrivateKey getPrivateKey(byte[] keyBytes) throws Exception { |
||||||
|
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); |
||||||
|
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM); |
||||||
|
return (RSAPrivateKey) keyFactory.generatePrivate(keySpec); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 公钥加密 |
||||||
|
* |
||||||
|
* @param data 待加密内容 |
||||||
|
* @param publicKey 公钥 |
||||||
|
* @return 返回密文 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
public static String encryptByPublicKey(String data, RSAPublicKey publicKey) throws Exception { |
||||||
|
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM); |
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, publicKey); |
||||||
|
// 模长
|
||||||
|
int key_len = publicKey.getModulus().bitLength() / 8; |
||||||
|
// 加密数据长度 <= 模长-11
|
||||||
|
String[] datas = splitString(data, key_len - 11); |
||||||
|
String mi = ""; |
||||||
|
// 如果明文长度大于模长-11则要分组加密
|
||||||
|
for (String s : datas) { |
||||||
|
mi += bcd2Str(cipher.doFinal(s.getBytes())); |
||||||
|
} |
||||||
|
return mi; |
||||||
|
} |
||||||
|
public static String encryptByPrivateKey(String data, String privateKey) throws Exception { |
||||||
|
return encryptByPrivateKey(data, getPrivateKey(privateKey)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 私钥加密 |
||||||
|
* |
||||||
|
* @param data 待加密数据 |
||||||
|
* @param privateKey 私钥 |
||||||
|
* @return 返回密文 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
public static String encryptByPrivateKey(String data, RSAPrivateKey privateKey) throws Exception { |
||||||
|
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM); |
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, privateKey); |
||||||
|
// 模长
|
||||||
|
int key_len = privateKey.getModulus().bitLength() / 8; |
||||||
|
// 加密数据长度 <= 模长-11
|
||||||
|
String[] datas = splitString(data, key_len - 11); |
||||||
|
String mi = ""; |
||||||
|
// 如果明文长度大于模长-11则要分组加密
|
||||||
|
for (String s : datas) { |
||||||
|
mi += bcd2Str(cipher.doFinal(s.getBytes())); |
||||||
|
} |
||||||
|
return mi; |
||||||
|
} |
||||||
|
|
||||||
|
public static String decryptByPrivateKey(String data, String privateKey) throws Exception { |
||||||
|
return decryptByPrivateKey(data, getPrivateKey(privateKey)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 私钥解密 |
||||||
|
* |
||||||
|
* @param data 待解密内容 |
||||||
|
* @param privateKey 私钥 |
||||||
|
* @return 返回明文 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
public static String decryptByPrivateKey(String data, RSAPrivateKey privateKey) throws Exception { |
||||||
|
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM); |
||||||
|
cipher.init(Cipher.DECRYPT_MODE, privateKey); |
||||||
|
// 模长
|
||||||
|
int key_len = privateKey.getModulus().bitLength() / 8; |
||||||
|
byte[] bytes = data.getBytes(); |
||||||
|
byte[] bcd = ASCII_To_BCD(bytes, bytes.length); |
||||||
|
// 如果密文长度大于模长则要分组解密
|
||||||
|
String ming = ""; |
||||||
|
byte[][] arrays = splitArray(bcd, key_len); |
||||||
|
for (byte[] arr : arrays) { |
||||||
|
ming += new String(cipher.doFinal(arr)); |
||||||
|
} |
||||||
|
return ming; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 公钥解密 |
||||||
|
* |
||||||
|
* @param data 待解密内容 |
||||||
|
* @param rsaPublicKey 公钥 |
||||||
|
* @return 返回明文 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
public static String decryptByPublicKey(String data, RSAPublicKey rsaPublicKey) throws Exception { |
||||||
|
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM); |
||||||
|
cipher.init(Cipher.DECRYPT_MODE, rsaPublicKey); |
||||||
|
// 模长
|
||||||
|
int key_len = rsaPublicKey.getModulus().bitLength() / 8; |
||||||
|
byte[] bytes = data.getBytes(); |
||||||
|
byte[] bcd = ASCII_To_BCD(bytes, bytes.length); |
||||||
|
// 如果密文长度大于模长则要分组解密
|
||||||
|
String ming = ""; |
||||||
|
byte[][] arrays = splitArray(bcd, key_len); |
||||||
|
for (byte[] arr : arrays) { |
||||||
|
ming += new String(cipher.doFinal(arr)); |
||||||
|
} |
||||||
|
return ming; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* ASCII码转BCD码 |
||||||
|
* |
||||||
|
*/ |
||||||
|
public static byte[] ASCII_To_BCD(byte[] ascii, int asc_len) { |
||||||
|
byte[] bcd = new byte[asc_len / 2]; |
||||||
|
int j = 0; |
||||||
|
for (int i = 0; i < (asc_len + 1) / 2; i++) { |
||||||
|
bcd[i] = asc_to_bcd(ascii[j++]); |
||||||
|
bcd[i] = (byte) (((j >= asc_len) ? 0x00 : asc_to_bcd(ascii[j++]) & 0xff) + (bcd[i] << 4)); |
||||||
|
} |
||||||
|
return bcd; |
||||||
|
} |
||||||
|
|
||||||
|
public static byte asc_to_bcd(byte asc) { |
||||||
|
byte bcd; |
||||||
|
|
||||||
|
if ((asc >= '0') && (asc <= '9')) { |
||||||
|
bcd = (byte) (asc - '0'); |
||||||
|
} else if ((asc >= 'A') && (asc <= 'F')) { |
||||||
|
bcd = (byte) (asc - 'A' + 10); |
||||||
|
} else if ((asc >= 'a') && (asc <= 'f')) { |
||||||
|
bcd = (byte) (asc - 'a' + 10); |
||||||
|
} else { |
||||||
|
bcd = (byte) (asc - 48); |
||||||
|
} |
||||||
|
return bcd; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* BCD转字符串 |
||||||
|
*/ |
||||||
|
public static String bcd2Str(byte[] bytes) { |
||||||
|
char[] temp = new char[bytes.length * 2]; |
||||||
|
char val; |
||||||
|
|
||||||
|
for (int i = 0; i < bytes.length; i++) { |
||||||
|
val = (char) (((bytes[i] & 0xf0) >> 4) & 0x0f); |
||||||
|
temp[i * 2] = (char) (val > 9 ? val + 'A' - 10 : val + '0'); |
||||||
|
|
||||||
|
val = (char) (bytes[i] & 0x0f); |
||||||
|
temp[i * 2 + 1] = (char) (val > 9 ? val + 'A' - 10 : val + '0'); |
||||||
|
} |
||||||
|
return new String(temp); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 拆分字符串 |
||||||
|
*/ |
||||||
|
public static String[] splitString(String string, int len) { |
||||||
|
int x = string.length() / len; |
||||||
|
int y = string.length() % len; |
||||||
|
int z = 0; |
||||||
|
if (y != 0) { |
||||||
|
z = 1; |
||||||
|
} |
||||||
|
String[] strings = new String[x + z]; |
||||||
|
String str = ""; |
||||||
|
for (int i = 0; i < x + z; i++) { |
||||||
|
if (i == x + z - 1 && y != 0) { |
||||||
|
str = string.substring(i * len, i * len + y); |
||||||
|
} else { |
||||||
|
str = string.substring(i * len, i * len + len); |
||||||
|
} |
||||||
|
strings[i] = str; |
||||||
|
} |
||||||
|
return strings; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 拆分数组 |
||||||
|
*/ |
||||||
|
public static byte[][] splitArray(byte[] data, int len) { |
||||||
|
int x = data.length / len; |
||||||
|
int y = data.length % len; |
||||||
|
int z = 0; |
||||||
|
if (y != 0) { |
||||||
|
z = 1; |
||||||
|
} |
||||||
|
byte[][] arrays = new byte[x + z][]; |
||||||
|
byte[] arr; |
||||||
|
for (int i = 0; i < x + z; i++) { |
||||||
|
arr = new byte[len]; |
||||||
|
if (i == x + z - 1 && y != 0) { |
||||||
|
System.arraycopy(data, i * len, arr, 0, y); |
||||||
|
} else { |
||||||
|
System.arraycopy(data, i * len, arr, 0, len); |
||||||
|
} |
||||||
|
arrays[i] = arr; |
||||||
|
} |
||||||
|
return arrays; |
||||||
|
} |
||||||
|
|
||||||
|
/*public static void main(String[] args) throws Exception { |
||||||
|
KeyStore keys = createKeys(); |
||||||
|
String pubKey = keys.getPublicKey(); |
||||||
|
System.out.println("pubKey:"); |
||||||
|
System.out.println(pubKey); |
||||||
|
String priKey = keys.getPrivateKey(); |
||||||
|
System.out.println("priKey:"); |
||||||
|
System.out.println(priKey); |
||||||
|
|
||||||
|
String ming = "6460201d23954f8e90cf79b818844ca0"; |
||||||
|
// 用公钥加密
|
||||||
|
String mi = encryptByPublicKey(ming, getPublicKey(pubKey)); |
||||||
|
System.out.println("mi : " + mi); |
||||||
|
// 用私钥解密
|
||||||
|
System.out.println("ming : " + decryptByPrivateKey(mi, getPrivateKey(priKey))); |
||||||
|
|
||||||
|
// 用私钥加密
|
||||||
|
String mi2 = encryptByPrivateKey(ming, getPrivateKey(priKey)); |
||||||
|
|
||||||
|
System.out.println("mi2 : " + mi2); |
||||||
|
// 用公钥解密
|
||||||
|
System.out.println("ming2 : " + decryptByPublicKey(mi2, getPublicKey(pubKey))); |
||||||
|
}*/ |
||||||
|
|
||||||
|
} |
@ -0,0 +1,139 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.util; |
||||||
|
|
||||||
|
import org.apache.commons.codec.digest.DigestUtils; |
||||||
|
import org.springframework.data.redis.core.RedisTemplate; |
||||||
|
import org.springframework.data.redis.core.script.RedisScript; |
||||||
|
|
||||||
|
import java.util.Collections; |
||||||
|
|
||||||
|
/** |
||||||
|
* redis分布式锁,详见:https://blog.csdn.net/thc1987/article/details/80355155<br>
|
||||||
|
* 思路: |
||||||
|
* <pre> |
||||||
|
* 用SETNX命令,SETNX只有在key不存在时才返回成功。这意味着只有一个线程可以成功运行SETNX命令,而其他线程会失败,然后不断重试,直到它们能建立锁。 |
||||||
|
* 然后使用脚本来创建锁,因为一个redis脚本同一时刻只能运行一次。 |
||||||
|
* 创建锁代码: |
||||||
|
* <code> |
||||||
|
-- KEYS[1] key, |
||||||
|
-- ARGV[1] value, |
||||||
|
-- ARGV[2] expireTimeMilliseconds |
||||||
|
|
||||||
|
if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then |
||||||
|
redis.call('pexpire', KEYS[1], ARGV[2]) |
||||||
|
return 1 |
||||||
|
else |
||||||
|
return 0 |
||||||
|
end |
||||||
|
* </code> |
||||||
|
* 最后使用脚本来解锁。 |
||||||
|
* 解锁代码: |
||||||
|
* |
||||||
|
* <code> |
||||||
|
-- KEYS[1] key, |
||||||
|
-- ARGV[1] value |
||||||
|
if redis.call("get", KEYS[1]) == ARGV[1] |
||||||
|
then |
||||||
|
return redis.call("del", KEYS[1]) |
||||||
|
else |
||||||
|
return 0 |
||||||
|
end |
||||||
|
* </code> |
||||||
|
* </pre> |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class RedisLockUtil { |
||||||
|
|
||||||
|
private static final Long SUCCESS = 1L; |
||||||
|
|
||||||
|
/** 加锁脚本 */ |
||||||
|
private static final String SCRIPT_LOCK = "if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then redis.call('pexpire', KEYS[1], ARGV[2]) return 1 else return 0 end"; |
||||||
|
/** 解锁脚本 */ |
||||||
|
private static final String SCRIPT_UNLOCK = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; |
||||||
|
/** 加锁脚本sha1值 */ |
||||||
|
private static final String SCRIPT_LOCK_SHA1 = DigestUtils.sha1Hex(SCRIPT_LOCK); |
||||||
|
/** 解锁脚本sha1值 */ |
||||||
|
private static final String SCRIPT_UNLOCK_SHA1 = DigestUtils.sha1Hex(SCRIPT_UNLOCK); |
||||||
|
|
||||||
|
/** |
||||||
|
* 尝试获取分布式锁 |
||||||
|
* |
||||||
|
* @param redisTemplate |
||||||
|
* Redis客户端 |
||||||
|
* @param lockKey |
||||||
|
* 锁 |
||||||
|
* @param requestId |
||||||
|
* 请求标识 |
||||||
|
* @param expireTimeMilliseconds |
||||||
|
* 超期时间,多少毫秒后这把锁自动释放 |
||||||
|
* @return 返回true表示拿到锁 |
||||||
|
*/ |
||||||
|
@SuppressWarnings("unchecked") |
||||||
|
public static boolean tryGetDistributedLock(@SuppressWarnings("rawtypes") final RedisTemplate redisTemplate, |
||||||
|
final String lockKey, final String requestId, final int expireTimeMilliseconds) { |
||||||
|
|
||||||
|
Object result = redisTemplate.execute(new RedisScript<Long>() { |
||||||
|
@Override |
||||||
|
public String getSha1() { |
||||||
|
return SCRIPT_LOCK_SHA1; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Class<Long> getResultType() { |
||||||
|
return Long.class; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getScriptAsString() { |
||||||
|
return SCRIPT_LOCK; |
||||||
|
} |
||||||
|
|
||||||
|
}, |
||||||
|
// KEYS[1]
|
||||||
|
Collections.singletonList(lockKey), |
||||||
|
// ARGV[1]
|
||||||
|
requestId, |
||||||
|
// ARGV[2]
|
||||||
|
String.valueOf(expireTimeMilliseconds) |
||||||
|
); |
||||||
|
|
||||||
|
return SUCCESS.equals(result); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 释放分布式锁 |
||||||
|
* |
||||||
|
* @param redisTemplate |
||||||
|
* Redis客户端 |
||||||
|
* @param lockKey |
||||||
|
* 锁 |
||||||
|
* @param requestId |
||||||
|
* 请求标识 |
||||||
|
* @return 返回true表示释放锁成功 |
||||||
|
*/ |
||||||
|
@SuppressWarnings("unchecked") |
||||||
|
public static boolean releaseDistributedLock(@SuppressWarnings("rawtypes") RedisTemplate redisTemplate, |
||||||
|
String lockKey, String requestId) { |
||||||
|
|
||||||
|
Object result = redisTemplate.execute(new RedisScript<Long>() { |
||||||
|
@Override |
||||||
|
public String getSha1() { |
||||||
|
return SCRIPT_UNLOCK_SHA1; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Class<Long> getResultType() { |
||||||
|
return Long.class; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getScriptAsString() { |
||||||
|
return SCRIPT_UNLOCK; |
||||||
|
} |
||||||
|
|
||||||
|
}, Collections.singletonList(lockKey), requestId); |
||||||
|
|
||||||
|
return SUCCESS.equals(result); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,137 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.util; |
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON; |
||||||
|
import com.gitee.sop.gatewaycommon.message.ErrorEnum; |
||||||
|
import org.apache.commons.io.IOUtils; |
||||||
|
import org.apache.commons.lang.StringUtils; |
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
import java.io.IOException; |
||||||
|
import java.net.InetAddress; |
||||||
|
import java.net.UnknownHostException; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Map.Entry; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class RequestUtil { |
||||||
|
|
||||||
|
private static final String CONTENT_TYPE_URLENCODED = "application/x-www-form-urlencoded"; |
||||||
|
private static final String CONTENT_TYPE_JSON = "application/json"; |
||||||
|
private static final String CONTENT_TYPE_TEXT = "text/plain"; |
||||||
|
|
||||||
|
private static final String UTF8 = "UTF-8"; |
||||||
|
private static final String GET = "get"; |
||||||
|
|
||||||
|
private static final String UNKOWN = "unknown"; |
||||||
|
private static final String LOCAL_IP = "127.0.0.1"; |
||||||
|
private static final int IP_LEN = 15; |
||||||
|
|
||||||
|
public static String getText(HttpServletRequest request) throws Exception { |
||||||
|
return IOUtils.toString(request.getInputStream(), UTF8); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 从request中获取json。如果提交方式是application/x-www-form-urlencoded,则组装成json格式。 |
||||||
|
* |
||||||
|
* @param request request对象 |
||||||
|
* @return 返回json |
||||||
|
* @throws IOException |
||||||
|
*/ |
||||||
|
public static String getJson(HttpServletRequest request) throws Exception { |
||||||
|
String requestJson = null; |
||||||
|
String contectType = request.getContentType(); |
||||||
|
if (StringUtils.isBlank(contectType)) { |
||||||
|
throw ErrorEnum.ISV_INVALID_CONTENT_TYPE.getErrorMeta().getException(contectType); |
||||||
|
} |
||||||
|
|
||||||
|
contectType = contectType.toLowerCase(); |
||||||
|
|
||||||
|
if (contectType.contains(CONTENT_TYPE_JSON) || contectType.contains(CONTENT_TYPE_TEXT)) { |
||||||
|
requestJson = getText(request); |
||||||
|
} else if (contectType.contains(CONTENT_TYPE_URLENCODED)) { |
||||||
|
Map<String, Object> params = convertRequestParamsToMap(request); |
||||||
|
requestJson = JSON.toJSONString(params); |
||||||
|
} else { |
||||||
|
throw ErrorEnum.ISV_INVALID_CONTENT_TYPE.getErrorMeta().getException(contectType); |
||||||
|
} |
||||||
|
return requestJson; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* request中的参数转换成map |
||||||
|
* |
||||||
|
* @param request request对象 |
||||||
|
* @return 返回参数键值对 |
||||||
|
*/ |
||||||
|
public static Map<String, Object> convertRequestParamsToMap(HttpServletRequest request) { |
||||||
|
Map<String, String[]> paramMap = request.getParameterMap(); |
||||||
|
if(paramMap == null || paramMap.isEmpty()) { |
||||||
|
return Collections.emptyMap(); |
||||||
|
} |
||||||
|
Map<String, Object> retMap = new HashMap<String, Object>(paramMap.size()); |
||||||
|
|
||||||
|
Set<Entry<String, String[]>> entrySet = paramMap.entrySet(); |
||||||
|
|
||||||
|
for (Entry<String, String[]> entry : entrySet) { |
||||||
|
String name = entry.getKey(); |
||||||
|
String[] values = entry.getValue(); |
||||||
|
if (values.length == 1) { |
||||||
|
retMap.put(name, values[0]); |
||||||
|
} |
||||||
|
} |
||||||
|
return retMap; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取客户端真实IP |
||||||
|
* |
||||||
|
* @param request request对象 |
||||||
|
* @return 返回ip |
||||||
|
*/ |
||||||
|
public static String getClientIP(HttpServletRequest request) { |
||||||
|
String ipAddress = request.getHeader("x-forwarded-for"); |
||||||
|
if (ipAddress == null || ipAddress.length() == 0 || UNKOWN.equalsIgnoreCase(ipAddress)) { |
||||||
|
ipAddress = request.getHeader("Proxy-Client-IP"); |
||||||
|
} |
||||||
|
if (ipAddress == null || ipAddress.length() == 0 || UNKOWN.equalsIgnoreCase(ipAddress)) { |
||||||
|
ipAddress = request.getHeader("WL-Proxy-Client-IP"); |
||||||
|
} |
||||||
|
if (ipAddress == null || ipAddress.length() == 0 || UNKOWN.equalsIgnoreCase(ipAddress)) { |
||||||
|
ipAddress = request.getRemoteAddr(); |
||||||
|
if (LOCAL_IP.equals(ipAddress)) { |
||||||
|
// 根据网卡取本机配置的IP
|
||||||
|
try { |
||||||
|
InetAddress inet = InetAddress.getLocalHost(); |
||||||
|
ipAddress = inet.getHostAddress(); |
||||||
|
} catch (UnknownHostException e) { |
||||||
|
// ignore
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
// 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
|
||||||
|
if (ipAddress != null && ipAddress.length() > IP_LEN) { |
||||||
|
if (ipAddress.indexOf(",") > 0) { |
||||||
|
ipAddress = ipAddress.substring(0, ipAddress.indexOf(",")); |
||||||
|
} |
||||||
|
} |
||||||
|
return ipAddress; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 是否是get请求 |
||||||
|
* @param request request对象 |
||||||
|
* @return true,是 |
||||||
|
*/ |
||||||
|
public static boolean isGetRequest(HttpServletRequest request) { |
||||||
|
return GET.equalsIgnoreCase(request.getMethod()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,119 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.util; |
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON; |
||||||
|
import com.alibaba.fastjson.JSONArray; |
||||||
|
import com.alibaba.fastjson.JSONObject; |
||||||
|
import lombok.Data; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
import org.springframework.http.MediaType; |
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletResponse; |
||||||
|
import java.io.IOException; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.Iterator; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class ResponseUtil { |
||||||
|
public static final String UTF_8 = "UTF-8"; |
||||||
|
|
||||||
|
private static Logger log = LoggerFactory.getLogger(ResponseUtil.class); |
||||||
|
|
||||||
|
public static void writeJson(HttpServletResponse response, Object result) { |
||||||
|
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); |
||||||
|
response.setCharacterEncoding(UTF_8); |
||||||
|
try { |
||||||
|
response.getWriter().write(JSON.toJSONString(result)); |
||||||
|
} catch (IOException e) { |
||||||
|
log.error("doWriter", e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* map转成xml |
||||||
|
* |
||||||
|
* @param parameters |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static String mapToXml(JSONObject parameters) { |
||||||
|
String content = doMap2xml(parameters); |
||||||
|
return content; |
||||||
|
} |
||||||
|
|
||||||
|
private static String doMap2xml(JSONObject parameters) { |
||||||
|
StringBuffer sb = new StringBuffer(); |
||||||
|
Set es = parameters.entrySet(); |
||||||
|
Iterator it = es.iterator(); |
||||||
|
while (it.hasNext()) { |
||||||
|
Map.Entry entry = (Map.Entry) it.next(); |
||||||
|
String k = (String) entry.getKey(); |
||||||
|
Object v = entry.getValue(); |
||||||
|
if (v instanceof JSONObject) { |
||||||
|
sb.append("<").append(k).append(">") |
||||||
|
.append(doMap2xml((JSONObject) v)) |
||||||
|
.append("</").append(k).append(">"); |
||||||
|
} else if (v instanceof JSONArray) { |
||||||
|
JSONArray collection = (JSONArray) v; |
||||||
|
String items = buildItems(k + "_item", collection); |
||||||
|
sb.append("<").append(k).append(">") |
||||||
|
.append(items) |
||||||
|
.append("</").append(k).append(">"); |
||||||
|
} else { |
||||||
|
sb.append("<").append(k).append("><![CDATA[") |
||||||
|
.append(v) |
||||||
|
.append("]]></").append(k).append(">"); |
||||||
|
} |
||||||
|
} |
||||||
|
return sb.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
private static String buildItems(String key, JSONArray collection) { |
||||||
|
StringBuffer sb = new StringBuffer(); |
||||||
|
for (int i = 0; i < collection.size(); i++) { |
||||||
|
Object jsonObject = collection.get(i); |
||||||
|
sb.append("<").append(key).append(">"); |
||||||
|
if (jsonObject instanceof JSONObject) { |
||||||
|
sb.append(doMap2xml((JSONObject) jsonObject)); |
||||||
|
} else { |
||||||
|
sb.append(jsonObject); |
||||||
|
} |
||||||
|
sb.append("</").append(key).append(">"); |
||||||
|
} |
||||||
|
return sb.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
public static void main(String[] args) { |
||||||
|
Persion persion = new Persion(); |
||||||
|
persion.setId(1); |
||||||
|
persion.setName("aaa"); |
||||||
|
|
||||||
|
String jsonString = JSON.toJSONString(persion); |
||||||
|
JSONObject jsonObject = JSON.parseObject(jsonString); |
||||||
|
|
||||||
|
String xml = mapToXml(jsonObject); |
||||||
|
System.out.println(xml); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Data |
||||||
|
public static class Persion { |
||||||
|
int id; |
||||||
|
String name; |
||||||
|
List<String> items = Arrays.asList("item1", "item2"); |
||||||
|
List<Man> child = Arrays.asList(new Man("Jim"), new Man("Tom")); |
||||||
|
} |
||||||
|
|
||||||
|
@Data |
||||||
|
public static class Man{ |
||||||
|
String name; |
||||||
|
|
||||||
|
public Man(String name) { |
||||||
|
this.name = name; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.util; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.result.ApiResult; |
||||||
|
import com.thoughtworks.xstream.XStream; |
||||||
|
import com.thoughtworks.xstream.io.naming.NoNameCoder; |
||||||
|
import com.thoughtworks.xstream.io.xml.StaxDriver; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class XmlUtil { |
||||||
|
private static XStream xStream = new XStream(new StaxDriver(new NoNameCoder())); |
||||||
|
static { |
||||||
|
xStream.processAnnotations(ApiResult.class); |
||||||
|
xStream.aliasSystemAttribute(null, "class"); |
||||||
|
} |
||||||
|
|
||||||
|
public static String serialize(Object obj) { |
||||||
|
return getXStream().toXML(obj); |
||||||
|
} |
||||||
|
|
||||||
|
public static <T> T unserialize(String xml, Class<T> clazz) { |
||||||
|
xStream.processAnnotations(clazz); |
||||||
|
Object object = xStream.fromXML(xml); |
||||||
|
T cast = clazz.cast(object); |
||||||
|
return cast; |
||||||
|
} |
||||||
|
|
||||||
|
public static XStream getXStream() { |
||||||
|
return xStream; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.validate; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.bean.ApiContext; |
||||||
|
import com.gitee.sop.gatewaycommon.message.ErrorEnum; |
||||||
|
import com.gitee.sop.gatewaycommon.param.ApiParam; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.apache.commons.lang.StringUtils; |
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
public abstract class AbstractSigner implements Signer { |
||||||
|
|
||||||
|
/** |
||||||
|
* 构建服务端签名串 |
||||||
|
* |
||||||
|
* @param params |
||||||
|
* @param secret |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
protected abstract String buildServerSign(ApiParam params, String secret); |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean checkSign(HttpServletRequest request, String secret) { |
||||||
|
ApiParam apiParam = ApiContext.getApiParam(); |
||||||
|
String clientSign = apiParam.fetchSignAndRemove(); |
||||||
|
if (StringUtils.isBlank(clientSign)) { |
||||||
|
throw ErrorEnum.ISV_MISSING_SIGNATURE.getErrorMeta().getException(); |
||||||
|
} |
||||||
|
String serverSign = buildServerSign(apiParam, secret); |
||||||
|
return clientSign.equals(serverSign); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,60 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.validate; |
||||||
|
|
||||||
|
import org.apache.commons.codec.digest.DigestUtils; |
||||||
|
import com.gitee.sop.gatewaycommon.util.AESUtil; |
||||||
|
import com.gitee.sop.gatewaycommon.util.RSANewUtil; |
||||||
|
import com.gitee.sop.gatewaycommon.util.RSAUtil; |
||||||
|
|
||||||
|
/** |
||||||
|
* 负责各类加解密 |
||||||
|
* @author tanghc |
||||||
|
* |
||||||
|
*/ |
||||||
|
public class ApiEncrypter implements Encrypter { |
||||||
|
|
||||||
|
@Override |
||||||
|
public String aesEncryptToHex(String content, String password) throws Exception { |
||||||
|
return AESUtil.encryptToHex(content, password); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String aesDecryptFromHex(String hex, String password) throws Exception { |
||||||
|
return AESUtil.decryptFromHex(hex, password); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String aesEncryptToBase64String(String content, String password) throws Exception { |
||||||
|
return AESUtil.encryptToBase64String(content, password); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String aesDecryptFromBase64String(String base64String, String password) throws Exception { |
||||||
|
return AESUtil.decryptFromBase64String(base64String, password); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String rsaDecryptByPrivateKey(String data, String privateKey) throws Exception { |
||||||
|
return RSAUtil.decryptByPrivateKey(data, privateKey); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String rsaEncryptByPrivateKey(String data, String privateKey) throws Exception { |
||||||
|
return RSAUtil.encryptByPrivateKey(data, privateKey); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String rsaDecryptByPrivateKeyNew(String data, String privateKey) throws Exception { |
||||||
|
return RSANewUtil.decryptByPrivateKey(data, privateKey); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String rsaEncryptByPrivateKeyNew(String data, String privateKey) throws Exception { |
||||||
|
return RSANewUtil.encryptByPrivateKey(data, privateKey); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String md5(String value) { |
||||||
|
return DigestUtils.md5Hex(value); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,56 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.validate; |
||||||
|
|
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.message.ErrorEnum; |
||||||
|
import com.gitee.sop.gatewaycommon.param.ApiParam; |
||||||
|
import org.apache.tomcat.util.buf.HexUtils; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
/** |
||||||
|
* 签名验证实现 |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class ApiSigner extends AbstractSigner { |
||||||
|
|
||||||
|
private Map<String, SignEncipher> signEncipherMap = new HashMap<>(); |
||||||
|
|
||||||
|
public ApiSigner() { |
||||||
|
signEncipherMap.put("md5", new SignEncipherMD5()); |
||||||
|
signEncipherMap.put("hmac", new SignEncipherHMAC_MD5()); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public String buildServerSign(ApiParam param, String secret) { |
||||||
|
String signMethod = param.fetchSignMethod(); |
||||||
|
SignEncipher signEncipher = signEncipherMap.get(signMethod); |
||||||
|
if (signEncipher == null) { |
||||||
|
throw ErrorEnum.ISV_INVALID_SIGNATURE_TYPE.getErrorMeta().getException(signMethod); |
||||||
|
} |
||||||
|
|
||||||
|
// 第一步:参数排序
|
||||||
|
Set<String> keySet = param.keySet(); |
||||||
|
List<String> paramNames = new ArrayList<String>(keySet); |
||||||
|
Collections.sort(paramNames); |
||||||
|
|
||||||
|
// 第二步:把所有参数名和参数值串在一起
|
||||||
|
StringBuilder paramNameValue = new StringBuilder(); |
||||||
|
for (String paramName : paramNames) { |
||||||
|
paramNameValue.append(paramName).append(param.get(paramName)); |
||||||
|
} |
||||||
|
|
||||||
|
// 第三步:使用MD5/HMAC加密
|
||||||
|
String source = paramNameValue.toString(); |
||||||
|
byte[] bytes = signEncipher.encrypt(source, secret); |
||||||
|
|
||||||
|
// 第四步:把二进制转化为大写的十六进制
|
||||||
|
return HexUtils.toHexString(bytes).toUpperCase(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,150 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.validate; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.bean.ApiConfig; |
||||||
|
import com.gitee.sop.gatewaycommon.bean.ApiContext; |
||||||
|
import com.gitee.sop.gatewaycommon.message.ErrorEnum; |
||||||
|
import com.gitee.sop.gatewaycommon.param.ApiParam; |
||||||
|
import com.gitee.sop.gatewaycommon.param.ParamNames; |
||||||
|
import com.gitee.sop.gatewaycommon.param.UploadContext; |
||||||
|
import com.gitee.sop.gatewaycommon.secret.AppSecretManager; |
||||||
|
import org.apache.commons.codec.digest.DigestUtils; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
import org.springframework.util.Assert; |
||||||
|
import org.springframework.util.StringUtils; |
||||||
|
import org.springframework.web.multipart.MultipartFile; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.text.ParseException; |
||||||
|
import java.text.SimpleDateFormat; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.Date; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* 负责校验,校验工作都在这里 |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class ApiValidator implements Validator { |
||||||
|
private static final Logger logger = LoggerFactory.getLogger(ApiValidator.class); |
||||||
|
|
||||||
|
private static final int MILLISECOND_OF_ONE_SECOND = 1000; |
||||||
|
|
||||||
|
|
||||||
|
private static List<String> FORMAT_LIST = Arrays.asList("json", "xml"); |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void validate(ApiParam param) { |
||||||
|
ApiConfig apiConfig = ApiContext.getApiConfig(); |
||||||
|
if (apiConfig.isIgnoreValidate() || param.fetchIgnoreValidate()) { |
||||||
|
logger.debug("忽略所有验证(ignoreValidate=true), name:{}, version:{}", param.fetchName(), param.fetchVersion()); |
||||||
|
return; |
||||||
|
} |
||||||
|
if (param.fetchIgnoreSign()) { |
||||||
|
logger.debug("忽略签名验证, name:{}, version:{}", param.fetchName(), param.fetchVersion()); |
||||||
|
} else { |
||||||
|
// 需要验证签名
|
||||||
|
checkAppKey(param); |
||||||
|
checkSign(param); |
||||||
|
} |
||||||
|
checkUploadFile(param); |
||||||
|
checkTimeout(param); |
||||||
|
checkFormat(param); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 校验上传文件内容 |
||||||
|
* |
||||||
|
* @param param |
||||||
|
*/ |
||||||
|
protected void checkUploadFile(ApiParam param) { |
||||||
|
UploadContext uploadContext = ApiContext.getUploadContext(); |
||||||
|
if (uploadContext != null) { |
||||||
|
try { |
||||||
|
List<MultipartFile> files = uploadContext.getAllFile(); |
||||||
|
for (MultipartFile file : files) { |
||||||
|
// 客户端传来的文件md5
|
||||||
|
String clientMd5 = param.getString(file.getName()); |
||||||
|
if (clientMd5 != null) { |
||||||
|
String fileMd5 = DigestUtils.md5Hex(file.getBytes()); |
||||||
|
if (!clientMd5.equals(fileMd5)) { |
||||||
|
throw ErrorEnum.ISV_UPLOAD_FAIL.getErrorMeta().getException(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} catch (IOException e) { |
||||||
|
logger.error("验证上传文件MD5错误", e); |
||||||
|
throw ErrorEnum.ISV_UPLOAD_FAIL.getErrorMeta().getException(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
protected void checkTimeout(ApiParam param) { |
||||||
|
int timeoutSeconds = ApiContext.getApiConfig().getTimeoutSeconds(); |
||||||
|
// 如果设置为0,表示不校验
|
||||||
|
if (timeoutSeconds == 0) { |
||||||
|
return; |
||||||
|
} |
||||||
|
if (timeoutSeconds < 0) { |
||||||
|
throw new IllegalArgumentException("服务端timeoutSeconds设置错误"); |
||||||
|
} |
||||||
|
String requestTime = param.fetchTimestamp(); |
||||||
|
try { |
||||||
|
Date requestDate = new SimpleDateFormat(ParamNames.TIMESTAMP_PATTERN).parse(requestTime); |
||||||
|
long requestMilliseconds = requestDate.getTime(); |
||||||
|
if (System.currentTimeMillis() - requestMilliseconds > timeoutSeconds * MILLISECOND_OF_ONE_SECOND) { |
||||||
|
throw ErrorEnum.ISV_INVALID_TIMESTAMP.getErrorMeta().getException(); |
||||||
|
} |
||||||
|
} catch (ParseException e) { |
||||||
|
throw ErrorEnum.ISV_INVALID_TIMESTAMP.getErrorMeta().getException(param.fetchNameVersion()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected void checkAppKey(ApiParam param) { |
||||||
|
if (StringUtils.isEmpty(param.fetchAppKey())) { |
||||||
|
throw ErrorEnum.ISV_MISSING_APP_ID.getErrorMeta().getException(); |
||||||
|
} |
||||||
|
AppSecretManager appSecretManager = ApiContext.getApiConfig().getAppSecretManager(); |
||||||
|
Assert.notNull(appSecretManager, "appSecretManager未初始化"); |
||||||
|
boolean isTrueAppKey = appSecretManager.isValidAppKey(param.fetchAppKey()); |
||||||
|
if (!isTrueAppKey) { |
||||||
|
throw ErrorEnum.ISV_INVALID_APP_ID.getErrorMeta().getException(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected void checkSign(ApiParam param) { |
||||||
|
String clientSign = param.fetchSign(); |
||||||
|
try { |
||||||
|
if (StringUtils.isEmpty(param.fetchSign())) { |
||||||
|
throw ErrorEnum.ISV_MISSING_SIGNATURE.getErrorMeta().getException(param.fetchNameVersion(), ParamNames.SIGN_NAME); |
||||||
|
} |
||||||
|
String secret = ApiContext.getApiConfig().getAppSecretManager().getSecret(param.fetchAppKey()); |
||||||
|
if (StringUtils.isEmpty(secret)) { |
||||||
|
throw ErrorEnum.ISV_MISSING_SIGNATURE_CONFIG.getErrorMeta().getException(); |
||||||
|
} |
||||||
|
Signer signer = ApiContext.getApiConfig().getSigner(); |
||||||
|
boolean isRightSign = signer.checkSign(ApiContext.getRequest(), secret); |
||||||
|
// 错误的sign
|
||||||
|
if (!isRightSign) { |
||||||
|
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(param.fetchNameVersion()); |
||||||
|
} |
||||||
|
} finally { |
||||||
|
// 校验过程中会移除sign,这里需要重新设置进去
|
||||||
|
param.setSign(clientSign); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
protected void checkFormat(ApiParam param) { |
||||||
|
String format = param.fetchFormat(); |
||||||
|
boolean contains = FORMAT_LIST.contains(format.toLowerCase()); |
||||||
|
|
||||||
|
if (!contains) { |
||||||
|
throw ErrorEnum.ISV_INVALID_FORMAT.getErrorMeta().getException(param.fetchNameVersion(), format); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,95 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.validate; |
||||||
|
|
||||||
|
/** |
||||||
|
* 负责加解密 |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public interface Encrypter { |
||||||
|
|
||||||
|
/** |
||||||
|
* AES文本加密 |
||||||
|
* |
||||||
|
* @param content 明文 |
||||||
|
* @param password 密码 |
||||||
|
* @return 返回16进制内容 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
String aesEncryptToHex(String content, String password) throws Exception; |
||||||
|
|
||||||
|
/** |
||||||
|
* AES文本解密 |
||||||
|
* |
||||||
|
* @param hex 待解密文本,16进制内容 |
||||||
|
* @param password 密码 |
||||||
|
* @return 返回明文 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
String aesDecryptFromHex(String hex, String password) throws Exception; |
||||||
|
|
||||||
|
/** |
||||||
|
* AES文本加密 |
||||||
|
* |
||||||
|
* @param content 明文 |
||||||
|
* @param password 密码 |
||||||
|
* @return 返回base64内容 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
String aesEncryptToBase64String(String content, String password) throws Exception; |
||||||
|
|
||||||
|
/** |
||||||
|
* AES文本解密 |
||||||
|
* |
||||||
|
* @param base64String 待解密文本,16进制内容 |
||||||
|
* @param password 密码 |
||||||
|
* @return 返回明文 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
String aesDecryptFromBase64String(String base64String, String password) throws Exception; |
||||||
|
|
||||||
|
/** |
||||||
|
* RSA私钥解密 |
||||||
|
* |
||||||
|
* @param data 解密内容 |
||||||
|
* @param privateKey 私钥 |
||||||
|
* @return 返回明文 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
String rsaDecryptByPrivateKey(String data, String privateKey) throws Exception; |
||||||
|
|
||||||
|
/** |
||||||
|
* 新版rsa私钥解密 |
||||||
|
* @param data 解密内容 |
||||||
|
* @param privateKey 私钥 |
||||||
|
* @return 返回明文 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
String rsaDecryptByPrivateKeyNew(String data, String privateKey) throws Exception; |
||||||
|
|
||||||
|
/** |
||||||
|
* RSA私钥加密 |
||||||
|
* |
||||||
|
* @param data 明文 |
||||||
|
* @param privateKey 私钥 |
||||||
|
* @return 返回密文 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
String rsaEncryptByPrivateKey(String data, String privateKey) throws Exception; |
||||||
|
|
||||||
|
/** |
||||||
|
* 新版rsa私钥加密 |
||||||
|
* @param data 明文 |
||||||
|
* @param privateKey 私钥 |
||||||
|
* @return 返回密文 |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
String rsaEncryptByPrivateKeyNew(String data, String privateKey) throws Exception; |
||||||
|
|
||||||
|
/** |
||||||
|
* md5加密,全部小写 |
||||||
|
* |
||||||
|
* @param value |
||||||
|
* @return 返回md5内容 |
||||||
|
*/ |
||||||
|
String md5(String value); |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.validate; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public interface SignEncipher { |
||||||
|
/** |
||||||
|
* 签名的摘要算法 |
||||||
|
* @param input 待签名数据 |
||||||
|
* @return 返回加密后的数据 |
||||||
|
*/ |
||||||
|
byte[] encrypt(String input, String secret); |
||||||
|
} |
@ -0,0 +1,37 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.validate; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.bean.SopConstants; |
||||||
|
import com.gitee.sop.gatewaycommon.message.ErrorEnum; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
|
||||||
|
import javax.crypto.Mac; |
||||||
|
import javax.crypto.SecretKey; |
||||||
|
import javax.crypto.spec.SecretKeySpec; |
||||||
|
import java.security.InvalidKeyException; |
||||||
|
import java.security.NoSuchAlgorithmException; |
||||||
|
|
||||||
|
/** |
||||||
|
* HMAC_MD5加密 |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
public class SignEncipherHMAC_MD5 implements SignEncipher { |
||||||
|
|
||||||
|
public static final String HMAC_MD5 = "HmacMD5"; |
||||||
|
|
||||||
|
@Override |
||||||
|
public byte[] encrypt(String input, String secret) { |
||||||
|
try { |
||||||
|
SecretKey secretKey = new SecretKeySpec(secret.getBytes(SopConstants.CHARSET_UTF8), HMAC_MD5); |
||||||
|
Mac mac = Mac.getInstance(secretKey.getAlgorithm()); |
||||||
|
mac.init(secretKey); |
||||||
|
return mac.doFinal(input.getBytes(SopConstants.CHARSET_UTF8)); |
||||||
|
} catch (NoSuchAlgorithmException e) { |
||||||
|
log.error("HMAC_MD5加密加密失败NoSuchAlgorithmException", e); |
||||||
|
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(); |
||||||
|
} catch (InvalidKeyException e) { |
||||||
|
log.error("HMAC_MD5加密加密失败InvalidKeyException", e); |
||||||
|
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.validate; |
||||||
|
|
||||||
|
import org.apache.commons.codec.digest.DigestUtils; |
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class SignEncipherMD5 implements SignEncipher { |
||||||
|
@Override |
||||||
|
public byte[] encrypt(String input, String secret) { |
||||||
|
String source = secret + input + secret; |
||||||
|
return DigestUtils.md5(source.getBytes(StandardCharsets.UTF_8)); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.validate; |
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
|
||||||
|
/** |
||||||
|
* 负责签名校验 |
||||||
|
* @author tanghc |
||||||
|
* |
||||||
|
*/ |
||||||
|
public interface Signer { |
||||||
|
|
||||||
|
/** |
||||||
|
* 签名校验 |
||||||
|
* @param request |
||||||
|
* @param secret 秘钥 |
||||||
|
* @return true签名正确 |
||||||
|
*/ |
||||||
|
boolean checkSign(HttpServletRequest request, String secret); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.validate; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.param.ApiParam; |
||||||
|
|
||||||
|
/** |
||||||
|
* 校验接口 |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
* |
||||||
|
*/ |
||||||
|
public interface Validator { |
||||||
|
/** |
||||||
|
* 接口验证 |
||||||
|
* @param param 接口参数 |
||||||
|
*/ |
||||||
|
void validate(ApiParam param); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,97 @@ |
|||||||
|
/** |
||||||
|
* Alipay.com Inc. |
||||||
|
* Copyright (c) 2004-2012 All Rights Reserved. |
||||||
|
*/ |
||||||
|
package com.gitee.sop.gatewaycommon.validate.alipay; |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* @author runzhi |
||||||
|
*/ |
||||||
|
public class AlipayConstants { |
||||||
|
|
||||||
|
public static final String SIGN_TYPE = "sign_type"; |
||||||
|
|
||||||
|
public static final String SIGN_TYPE_RSA = "RSA"; |
||||||
|
|
||||||
|
/** |
||||||
|
* sha256WithRsa 算法请求类型 |
||||||
|
*/ |
||||||
|
public static final String SIGN_TYPE_RSA2 = "RSA2"; |
||||||
|
|
||||||
|
public static final String SIGN_ALGORITHMS = "SHA1WithRSA"; |
||||||
|
|
||||||
|
public static final String SIGN_SHA256RSA_ALGORITHMS = "SHA256WithRSA"; |
||||||
|
|
||||||
|
public static final String ENCRYPT_TYPE_AES = "AES"; |
||||||
|
|
||||||
|
public static final String APP_ID = "app_id"; |
||||||
|
|
||||||
|
public static final String FORMAT = "format"; |
||||||
|
|
||||||
|
public static final String METHOD = "method"; |
||||||
|
|
||||||
|
public static final String TIMESTAMP = "timestamp"; |
||||||
|
|
||||||
|
public static final String VERSION = "version"; |
||||||
|
|
||||||
|
public static final String SIGN = "sign"; |
||||||
|
|
||||||
|
public static final String ALIPAY_SDK = "alipay_sdk"; |
||||||
|
|
||||||
|
public static final String ACCESS_TOKEN = "auth_token"; |
||||||
|
|
||||||
|
public static final String APP_AUTH_TOKEN = "app_auth_token"; |
||||||
|
|
||||||
|
public static final String TERMINAL_TYPE = "terminal_type"; |
||||||
|
|
||||||
|
public static final String TERMINAL_INFO = "terminal_info"; |
||||||
|
|
||||||
|
public static final String CHARSET = "charset"; |
||||||
|
|
||||||
|
public static final String NOTIFY_URL = "notify_url"; |
||||||
|
|
||||||
|
public static final String RETURN_URL = "return_url"; |
||||||
|
|
||||||
|
public static final String ENCRYPT_TYPE = "encrypt_type"; |
||||||
|
|
||||||
|
//-----===-------///
|
||||||
|
|
||||||
|
public static final String BIZ_CONTENT_KEY = "biz_content"; |
||||||
|
|
||||||
|
/** 默认时间格式 **/ |
||||||
|
public static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; |
||||||
|
|
||||||
|
/** Date默认时区 **/ |
||||||
|
public static final String DATE_TIMEZONE = "GMT+8"; |
||||||
|
|
||||||
|
/** UTF-8字符集 **/ |
||||||
|
public static final String CHARSET_UTF8 = "UTF-8"; |
||||||
|
|
||||||
|
/** GBK字符集 **/ |
||||||
|
public static final String CHARSET_GBK = "GBK"; |
||||||
|
|
||||||
|
/** JSON 应格式 */ |
||||||
|
public static final String FORMAT_JSON = "json"; |
||||||
|
|
||||||
|
/** XML 应格式 */ |
||||||
|
public static final String FORMAT_XML = "xml"; |
||||||
|
|
||||||
|
/** SDK版本号 */ |
||||||
|
public static final String SDK_VERSION = "alipay-sdk-java-3.6.0.ALL"; |
||||||
|
|
||||||
|
public static final String PROD_CODE = "prod_code"; |
||||||
|
|
||||||
|
/** 老版本失败节点 */ |
||||||
|
public static final String ERROR_RESPONSE = "error_response"; |
||||||
|
|
||||||
|
/** 新版本节点后缀 */ |
||||||
|
public static final String RESPONSE_SUFFIX = "_response"; |
||||||
|
|
||||||
|
/** 加密后XML返回报文的节点名字 */ |
||||||
|
public static final String RESPONSE_XML_ENCRYPT_NODE_NAME = "response_encrypted"; |
||||||
|
|
||||||
|
/** 批量请求id **/ |
||||||
|
public static final String BATCH_REQUEST_ID = "batch_request_id"; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,616 @@ |
|||||||
|
/** |
||||||
|
* Alipay.com Inc. |
||||||
|
* Copyright (c) 2004-2012 All Rights Reserved. |
||||||
|
*/ |
||||||
|
package com.gitee.sop.gatewaycommon.validate.alipay; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.message.ErrorEnum; |
||||||
|
import org.apache.commons.codec.binary.Base64; |
||||||
|
|
||||||
|
import javax.crypto.Cipher; |
||||||
|
import java.io.ByteArrayInputStream; |
||||||
|
import java.io.ByteArrayOutputStream; |
||||||
|
import java.io.InputStream; |
||||||
|
import java.io.InputStreamReader; |
||||||
|
import java.io.StringWriter; |
||||||
|
import java.security.KeyFactory; |
||||||
|
import java.security.PrivateKey; |
||||||
|
import java.security.PublicKey; |
||||||
|
import java.security.spec.InvalidKeySpecException; |
||||||
|
import java.security.spec.PKCS8EncodedKeySpec; |
||||||
|
import java.security.spec.X509EncodedKeySpec; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author runzhi |
||||||
|
*/ |
||||||
|
public class AlipaySignature { |
||||||
|
|
||||||
|
/** |
||||||
|
* RSA最大加密明文大小 |
||||||
|
*/ |
||||||
|
private static final int MAX_ENCRYPT_BLOCK = 117; |
||||||
|
|
||||||
|
/** |
||||||
|
* RSA最大解密密文大小 |
||||||
|
*/ |
||||||
|
private static final int MAX_DECRYPT_BLOCK = 128; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @param sortedParams |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static String getSignContent(Map<String, Object> sortedParams) { |
||||||
|
StringBuffer content = new StringBuffer(); |
||||||
|
List<String> keys = new ArrayList<String>(sortedParams.keySet()); |
||||||
|
Collections.sort(keys); |
||||||
|
int index = 0; |
||||||
|
for (int i = 0; i < keys.size(); i++) { |
||||||
|
String key = keys.get(i); |
||||||
|
String value = String.valueOf(sortedParams.get(key)); |
||||||
|
if (StringUtils.areNotEmpty(key, value)) { |
||||||
|
content.append((index == 0 ? "" : "&") + key + "=" + value); |
||||||
|
index++; |
||||||
|
} |
||||||
|
} |
||||||
|
return content.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* rsa内容签名 |
||||||
|
* |
||||||
|
* @param content |
||||||
|
* @param publicKey |
||||||
|
* @param charset |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static String rsaSign(String content, String publicKey, String charset, |
||||||
|
String signType) { |
||||||
|
|
||||||
|
if (AlipayConstants.SIGN_TYPE_RSA.equals(signType)) { |
||||||
|
|
||||||
|
return rsaSign(content, publicKey, charset); |
||||||
|
} else if (AlipayConstants.SIGN_TYPE_RSA2.equals(signType)) { |
||||||
|
|
||||||
|
return rsa256Sign(content, publicKey, charset); |
||||||
|
} else { |
||||||
|
throw ErrorEnum.ISV_INVALID_SIGNATURE_TYPE.getErrorMeta().getException(); |
||||||
|
// throw new AlipayApiException("Sign Type is Not Support : signType=" + signType);
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* sha256WithRsa 加签 |
||||||
|
* |
||||||
|
* @param content |
||||||
|
* @param publicKey |
||||||
|
* @param charset |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static String rsa256Sign(String content, String publicKey, |
||||||
|
String charset) { |
||||||
|
|
||||||
|
try { |
||||||
|
PrivateKey priKey = getPrivateKeyFromPKCS8(AlipayConstants.SIGN_TYPE_RSA, |
||||||
|
new ByteArrayInputStream(publicKey.getBytes())); |
||||||
|
|
||||||
|
java.security.Signature signature = java.security.Signature |
||||||
|
.getInstance(AlipayConstants.SIGN_SHA256RSA_ALGORITHMS); |
||||||
|
|
||||||
|
signature.initSign(priKey); |
||||||
|
|
||||||
|
if (StringUtils.isEmpty(charset)) { |
||||||
|
signature.update(content.getBytes()); |
||||||
|
} else { |
||||||
|
signature.update(content.getBytes(charset)); |
||||||
|
} |
||||||
|
|
||||||
|
byte[] signed = signature.sign(); |
||||||
|
|
||||||
|
return new String(Base64.encodeBase64(signed)); |
||||||
|
} catch (Exception e) { |
||||||
|
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(e); |
||||||
|
// throw new AlipayApiException("RSAcontent = " + content + "; charset = " + charset, e);
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* sha1WithRsa 加签 |
||||||
|
* |
||||||
|
* @param content |
||||||
|
* @param publicKey |
||||||
|
* @param charset |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static String rsaSign(String content, String publicKey, |
||||||
|
String charset) { |
||||||
|
try { |
||||||
|
PrivateKey priKey = getPrivateKeyFromPKCS8(AlipayConstants.SIGN_TYPE_RSA, |
||||||
|
new ByteArrayInputStream(publicKey.getBytes())); |
||||||
|
|
||||||
|
java.security.Signature signature = java.security.Signature |
||||||
|
.getInstance(AlipayConstants.SIGN_ALGORITHMS); |
||||||
|
|
||||||
|
signature.initSign(priKey); |
||||||
|
|
||||||
|
if (StringUtils.isEmpty(charset)) { |
||||||
|
signature.update(content.getBytes()); |
||||||
|
} else { |
||||||
|
signature.update(content.getBytes(charset)); |
||||||
|
} |
||||||
|
|
||||||
|
byte[] signed = signature.sign(); |
||||||
|
|
||||||
|
return new String(Base64.encodeBase64(signed)); |
||||||
|
} catch (InvalidKeySpecException ie) { |
||||||
|
throw ErrorEnum.ISV_INVALID_SIGNATURE_TYPE.getErrorMeta().getException(ie); |
||||||
|
// throw new AlipayApiException("RSA私钥格式不正确,请检查是否正确配置了PKCS8格式的私钥", ie);
|
||||||
|
} catch (Exception e) { |
||||||
|
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(e); |
||||||
|
// throw new AlipayApiException("RSAcontent = " + content + "; charset = " + charset, e);
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static String rsaSign(Map<String, Object> params, String publicKey, |
||||||
|
String charset, String signType) { |
||||||
|
String signContent = getSignContent(params); |
||||||
|
|
||||||
|
return rsaSign(signContent, publicKey, charset, signType); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public static PrivateKey getPrivateKeyFromPKCS8(String algorithm, |
||||||
|
InputStream ins) throws Exception { |
||||||
|
if (ins == null || StringUtils.isEmpty(algorithm)) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
KeyFactory keyFactory = KeyFactory.getInstance(algorithm); |
||||||
|
|
||||||
|
byte[] encodedKey = StreamUtil.readText(ins, "UTF-8").getBytes(); |
||||||
|
|
||||||
|
encodedKey = Base64.decodeBase64(encodedKey); |
||||||
|
|
||||||
|
return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodedKey)); |
||||||
|
} |
||||||
|
|
||||||
|
public static String getSignCheckContentV1(Map<String, String> params) { |
||||||
|
if (params == null) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
params.remove("sign"); |
||||||
|
params.remove("sign_type"); |
||||||
|
|
||||||
|
StringBuffer content = new StringBuffer(); |
||||||
|
List<String> keys = new ArrayList<String>(params.keySet()); |
||||||
|
Collections.sort(keys); |
||||||
|
|
||||||
|
for (int i = 0; i < keys.size(); i++) { |
||||||
|
String key = keys.get(i); |
||||||
|
String value = params.get(key); |
||||||
|
content.append((i == 0 ? "" : "&") + key + "=" + value); |
||||||
|
} |
||||||
|
|
||||||
|
return content.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
public static String getSignCheckContentV2(Map<String, ?> params) { |
||||||
|
if (params == null) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
params.remove("sign"); |
||||||
|
|
||||||
|
StringBuffer content = new StringBuffer(); |
||||||
|
List<String> keys = new ArrayList<String>(params.keySet()); |
||||||
|
Collections.sort(keys); |
||||||
|
|
||||||
|
for (int i = 0; i < keys.size(); i++) { |
||||||
|
String key = keys.get(i); |
||||||
|
String value = String.valueOf(params.get(key)); |
||||||
|
content.append((i == 0 ? "" : "&") + key + "=" + value); |
||||||
|
} |
||||||
|
|
||||||
|
return content.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
public static boolean rsaCheckV1(Map<String, String> params, String publicKey, |
||||||
|
String charset) { |
||||||
|
String sign = params.get("sign"); |
||||||
|
String content = getSignCheckContentV1(params); |
||||||
|
|
||||||
|
return rsaCheckContent(content, sign, publicKey, charset); |
||||||
|
} |
||||||
|
|
||||||
|
public static boolean rsaCheckV1(Map<String, String> params, String publicKey, |
||||||
|
String charset, String signType) { |
||||||
|
String sign = params.get("sign"); |
||||||
|
String content = getSignCheckContentV1(params); |
||||||
|
|
||||||
|
return rsaCheck(content, sign, publicKey, charset, signType); |
||||||
|
} |
||||||
|
|
||||||
|
public static boolean rsaCheckV2(Map<String, String> params, String publicKey, |
||||||
|
String charset) { |
||||||
|
String sign = params.get("sign"); |
||||||
|
String content = getSignCheckContentV2(params); |
||||||
|
|
||||||
|
return rsaCheckContent(content, sign, publicKey, charset); |
||||||
|
} |
||||||
|
|
||||||
|
public static boolean rsaCheckV2(Map<String, ?> params, String publicKey, |
||||||
|
String charset, String signType) { |
||||||
|
String sign = String.valueOf(params.get("sign")); |
||||||
|
String content = getSignCheckContentV2(params); |
||||||
|
|
||||||
|
return rsaCheck(content, sign, publicKey, charset, signType); |
||||||
|
} |
||||||
|
|
||||||
|
public static boolean rsaCheck(String content, String sign, String publicKey, String charset, |
||||||
|
String signType) { |
||||||
|
|
||||||
|
if (AlipayConstants.SIGN_TYPE_RSA.equals(signType)) { |
||||||
|
|
||||||
|
return rsaCheckContent(content, sign, publicKey, charset); |
||||||
|
|
||||||
|
} else if (AlipayConstants.SIGN_TYPE_RSA2.equals(signType)) { |
||||||
|
|
||||||
|
return rsa256CheckContent(content, sign, publicKey, charset); |
||||||
|
|
||||||
|
} else { |
||||||
|
throw ErrorEnum.ISV_INVALID_SIGNATURE_TYPE.getErrorMeta().getException(); |
||||||
|
// throw new AlipayApiException("Sign Type is Not Support : signType=" + signType);
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public static boolean rsa256CheckContent(String content, String sign, String publicKey, |
||||||
|
String charset) { |
||||||
|
try { |
||||||
|
PublicKey pubKey = getPublicKeyFromX509("RSA", |
||||||
|
new ByteArrayInputStream(publicKey.getBytes())); |
||||||
|
|
||||||
|
java.security.Signature signature = java.security.Signature |
||||||
|
.getInstance(AlipayConstants.SIGN_SHA256RSA_ALGORITHMS); |
||||||
|
|
||||||
|
signature.initVerify(pubKey); |
||||||
|
|
||||||
|
if (StringUtils.isEmpty(charset)) { |
||||||
|
signature.update(content.getBytes()); |
||||||
|
} else { |
||||||
|
signature.update(content.getBytes(charset)); |
||||||
|
} |
||||||
|
|
||||||
|
return signature.verify(Base64.decodeBase64(sign.getBytes())); |
||||||
|
} catch (Exception e) { |
||||||
|
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(e); |
||||||
|
// throw new AlipayApiException(
|
||||||
|
// "RSAcontent = " + content + ",sign=" + sign + ",charset = " + charset, e);
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static boolean rsaCheckContent(String content, String sign, String publicKey, |
||||||
|
String charset) { |
||||||
|
try { |
||||||
|
PublicKey pubKey = getPublicKeyFromX509("RSA", |
||||||
|
new ByteArrayInputStream(publicKey.getBytes())); |
||||||
|
|
||||||
|
java.security.Signature signature = java.security.Signature |
||||||
|
.getInstance(AlipayConstants.SIGN_ALGORITHMS); |
||||||
|
|
||||||
|
signature.initVerify(pubKey); |
||||||
|
|
||||||
|
if (StringUtils.isEmpty(charset)) { |
||||||
|
signature.update(content.getBytes()); |
||||||
|
} else { |
||||||
|
signature.update(content.getBytes(charset)); |
||||||
|
} |
||||||
|
|
||||||
|
return signature.verify(Base64.decodeBase64(sign.getBytes())); |
||||||
|
} catch (Exception e) { |
||||||
|
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(e); |
||||||
|
// throw new AlipayApiException(
|
||||||
|
// "RSAcontent = " + content + ",sign=" + sign + ",charset = " + charset, e);
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static PublicKey getPublicKeyFromX509(String algorithm, |
||||||
|
InputStream ins) throws Exception { |
||||||
|
KeyFactory keyFactory = KeyFactory.getInstance(algorithm); |
||||||
|
|
||||||
|
StringWriter writer = new StringWriter(); |
||||||
|
StreamUtil.io(new InputStreamReader(ins), writer); |
||||||
|
|
||||||
|
byte[] encodedKey = writer.toString().getBytes(); |
||||||
|
|
||||||
|
encodedKey = Base64.decodeBase64(encodedKey); |
||||||
|
|
||||||
|
return keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 验签并解密 |
||||||
|
* <p> |
||||||
|
* <b>目前适用于公众号</b><br> |
||||||
|
* params参数示例: |
||||||
|
* <br>{ |
||||||
|
* <br>biz_content=M0qGiGz+8kIpxe8aF4geWJdBn0aBTuJRQItLHo9R7o5JGhpic/MIUjvXo2BLB++BbkSq2OsJCEQFDZ0zK5AJYwvBgeRX30gvEj6eXqXRt16/IkB9HzAccEqKmRHrZJ7PjQWE0KfvDAHsJqFIeMvEYk1Zei2QkwSQPlso7K0oheo/iT+HYE8aTATnkqD/ByD9iNDtGg38pCa2xnnns63abKsKoV8h0DfHWgPH62urGY7Pye3r9FCOXA2Ykm8X4/Bl1bWFN/PFCEJHWe/HXj8KJKjWMO6ttsoV0xRGfeyUO8agu6t587Dl5ux5zD/s8Lbg5QXygaOwo3Fz1G8EqmGhi4+soEIQb8DBYanQOS3X+m46tVqBGMw8Oe+hsyIMpsjwF4HaPKMr37zpW3fe7xOMuimbZ0wq53YP/jhQv6XWodjT3mL0H5ACqcsSn727B5ztquzCPiwrqyjUHjJQQefFTzOse8snaWNQTUsQS7aLsHq0FveGpSBYORyA90qPdiTjXIkVP7mAiYiAIWW9pCEC7F3XtViKTZ8FRMM9ySicfuAlf3jtap6v2KPMtQv70X+hlmzO/IXB6W0Ep8DovkF5rB4r/BJYJLw/6AS0LZM9w5JfnAZhfGM2rKzpfNsgpOgEZS1WleG4I2hoQC0nxg9IcP0Hs+nWIPkEUcYNaiXqeBc=, |
||||||
|
* <br>sign=rlqgA8O+RzHBVYLyHmrbODVSANWPXf3pSrr82OCO/bm3upZiXSYrX5fZr6UBmG6BZRAydEyTIguEW6VRuAKjnaO/sOiR9BsSrOdXbD5Rhos/Xt7/mGUWbTOt/F+3W0/XLuDNmuYg1yIC/6hzkg44kgtdSTsQbOC9gWM7ayB4J4c=, |
||||||
|
* sign_type=RSA, |
||||||
|
* <br>charset=UTF-8 |
||||||
|
* <br>} |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @param params |
||||||
|
* @param alipayPublicKey 支付宝公钥 |
||||||
|
* @param cusPrivateKey 商户私钥 |
||||||
|
* @param isCheckSign 是否验签 |
||||||
|
* @param isDecrypt 是否解密 |
||||||
|
* @return 解密后明文,验签失败则异常抛出 |
||||||
|
*/ |
||||||
|
public static String checkSignAndDecrypt(Map<String, String> params, String alipayPublicKey, |
||||||
|
String cusPrivateKey, boolean isCheckSign, |
||||||
|
boolean isDecrypt) { |
||||||
|
String charset = params.get("charset"); |
||||||
|
String bizContent = params.get("biz_content"); |
||||||
|
if (isCheckSign) { |
||||||
|
if (!rsaCheckV2(params, alipayPublicKey, charset)) { |
||||||
|
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(); |
||||||
|
// throw new AlipayApiException("rsaCheck failure:rsaParams=" + params);
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (isDecrypt) { |
||||||
|
return rsaDecrypt(bizContent, cusPrivateKey, charset); |
||||||
|
} |
||||||
|
|
||||||
|
return bizContent; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 验签并解密 |
||||||
|
* <p> |
||||||
|
* <b>目前适用于公众号</b><br> |
||||||
|
* params参数示例: |
||||||
|
* <br>{ |
||||||
|
* <br>biz_content=M0qGiGz+8kIpxe8aF4geWJdBn0aBTuJRQItLHo9R7o5JGhpic/MIUjvXo2BLB++BbkSq2OsJCEQFDZ0zK5AJYwvBgeRX30gvEj6eXqXRt16/IkB9HzAccEqKmRHrZJ7PjQWE0KfvDAHsJqFIeMvEYk1Zei2QkwSQPlso7K0oheo/iT+HYE8aTATnkqD/ByD9iNDtGg38pCa2xnnns63abKsKoV8h0DfHWgPH62urGY7Pye3r9FCOXA2Ykm8X4/Bl1bWFN/PFCEJHWe/HXj8KJKjWMO6ttsoV0xRGfeyUO8agu6t587Dl5ux5zD/s8Lbg5QXygaOwo3Fz1G8EqmGhi4+soEIQb8DBYanQOS3X+m46tVqBGMw8Oe+hsyIMpsjwF4HaPKMr37zpW3fe7xOMuimbZ0wq53YP/jhQv6XWodjT3mL0H5ACqcsSn727B5ztquzCPiwrqyjUHjJQQefFTzOse8snaWNQTUsQS7aLsHq0FveGpSBYORyA90qPdiTjXIkVP7mAiYiAIWW9pCEC7F3XtViKTZ8FRMM9ySicfuAlf3jtap6v2KPMtQv70X+hlmzO/IXB6W0Ep8DovkF5rB4r/BJYJLw/6AS0LZM9w5JfnAZhfGM2rKzpfNsgpOgEZS1WleG4I2hoQC0nxg9IcP0Hs+nWIPkEUcYNaiXqeBc=, |
||||||
|
* <br>sign=rlqgA8O+RzHBVYLyHmrbODVSANWPXf3pSrr82OCO/bm3upZiXSYrX5fZr6UBmG6BZRAydEyTIguEW6VRuAKjnaO/sOiR9BsSrOdXbD5Rhos/Xt7/mGUWbTOt/F+3W0/XLuDNmuYg1yIC/6hzkg44kgtdSTsQbOC9gWM7ayB4J4c=, |
||||||
|
* sign_type=RSA, |
||||||
|
* <br>charset=UTF-8 |
||||||
|
* <br>} |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @param params |
||||||
|
* @param alipayPublicKey 支付宝公钥 |
||||||
|
* @param cusPrivateKey 商户私钥 |
||||||
|
* @param isCheckSign 是否验签 |
||||||
|
* @param isDecrypt 是否解密 |
||||||
|
* @return 解密后明文,验签失败则异常抛出 |
||||||
|
*/ |
||||||
|
public static String checkSignAndDecrypt(Map<String, String> params, String alipayPublicKey, |
||||||
|
String cusPrivateKey, boolean isCheckSign, |
||||||
|
boolean isDecrypt, String signType) { |
||||||
|
String charset = params.get("charset"); |
||||||
|
String bizContent = params.get("biz_content"); |
||||||
|
if (isCheckSign) { |
||||||
|
if (!rsaCheckV2(params, alipayPublicKey, charset, signType)) { |
||||||
|
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(); |
||||||
|
// throw new AlipayApiException("rsaCheck failure:rsaParams=" + params);
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (isDecrypt) { |
||||||
|
return rsaDecrypt(bizContent, cusPrivateKey, charset); |
||||||
|
} |
||||||
|
|
||||||
|
return bizContent; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 加密并签名<br> |
||||||
|
* <b>目前适用于公众号</b> |
||||||
|
* |
||||||
|
* @param bizContent 待加密、签名内容 |
||||||
|
* @param alipayPublicKey 支付宝公钥 |
||||||
|
* @param cusPrivateKey 商户私钥 |
||||||
|
* @param charset 字符集,如UTF-8, GBK, GB2312 |
||||||
|
* @param isEncrypt 是否加密,true-加密 false-不加密 |
||||||
|
* @param isSign 是否签名,true-签名 false-不签名 |
||||||
|
* @return 加密、签名后xml内容字符串 |
||||||
|
* <p> |
||||||
|
* 返回示例: |
||||||
|
* <alipay> |
||||||
|
* <response>密文</response> |
||||||
|
* <encryption_type>RSA</encryption_type> |
||||||
|
* <sign>sign</sign> |
||||||
|
* <sign_type>RSA</sign_type> |
||||||
|
* </alipay> |
||||||
|
* </p> |
||||||
|
*/ |
||||||
|
public static String encryptAndSign(String bizContent, String alipayPublicKey, |
||||||
|
String cusPrivateKey, String charset, boolean isEncrypt, |
||||||
|
boolean isSign) { |
||||||
|
StringBuilder sb = new StringBuilder(); |
||||||
|
if (StringUtils.isEmpty(charset)) { |
||||||
|
charset = AlipayConstants.CHARSET_GBK; |
||||||
|
} |
||||||
|
sb.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>"); |
||||||
|
if (isEncrypt) {// 加密
|
||||||
|
sb.append("<alipay>"); |
||||||
|
String encrypted = rsaEncrypt(bizContent, alipayPublicKey, charset); |
||||||
|
sb.append("<response>" + encrypted + "</response>"); |
||||||
|
sb.append("<encryption_type>RSA</encryption_type>"); |
||||||
|
if (isSign) { |
||||||
|
String sign = rsaSign(encrypted, cusPrivateKey, charset); |
||||||
|
sb.append("<sign>" + sign + "</sign>"); |
||||||
|
sb.append("<sign_type>RSA</sign_type>"); |
||||||
|
} |
||||||
|
sb.append("</alipay>"); |
||||||
|
} else if (isSign) {// 不加密,但需要签名
|
||||||
|
sb.append("<alipay>"); |
||||||
|
sb.append("<response>" + bizContent + "</response>"); |
||||||
|
String sign = rsaSign(bizContent, cusPrivateKey, charset); |
||||||
|
sb.append("<sign>" + sign + "</sign>"); |
||||||
|
sb.append("<sign_type>RSA</sign_type>"); |
||||||
|
sb.append("</alipay>"); |
||||||
|
} else {// 不加密,不加签
|
||||||
|
sb.append(bizContent); |
||||||
|
} |
||||||
|
return sb.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 加密并签名<br> |
||||||
|
* <b>目前适用于公众号</b> |
||||||
|
* |
||||||
|
* @param bizContent 待加密、签名内容 |
||||||
|
* @param alipayPublicKey 支付宝公钥 |
||||||
|
* @param cusPrivateKey 商户私钥 |
||||||
|
* @param charset 字符集,如UTF-8, GBK, GB2312 |
||||||
|
* @param isEncrypt 是否加密,true-加密 false-不加密 |
||||||
|
* @param isSign 是否签名,true-签名 false-不签名 |
||||||
|
* @return 加密、签名后xml内容字符串 |
||||||
|
* <p> |
||||||
|
* 返回示例: |
||||||
|
* <alipay> |
||||||
|
* <response>密文</response> |
||||||
|
* <encryption_type>RSA</encryption_type> |
||||||
|
* <sign>sign</sign> |
||||||
|
* <sign_type>RSA</sign_type> |
||||||
|
* </alipay> |
||||||
|
* </p> |
||||||
|
*/ |
||||||
|
public static String encryptAndSign(String bizContent, String alipayPublicKey, |
||||||
|
String cusPrivateKey, String charset, boolean isEncrypt, |
||||||
|
boolean isSign, String signType) { |
||||||
|
StringBuilder sb = new StringBuilder(); |
||||||
|
if (StringUtils.isEmpty(charset)) { |
||||||
|
charset = AlipayConstants.CHARSET_GBK; |
||||||
|
} |
||||||
|
sb.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>"); |
||||||
|
if (isEncrypt) {// 加密
|
||||||
|
sb.append("<alipay>"); |
||||||
|
String encrypted = rsaEncrypt(bizContent, alipayPublicKey, charset); |
||||||
|
sb.append("<response>" + encrypted + "</response>"); |
||||||
|
sb.append("<encryption_type>RSA</encryption_type>"); |
||||||
|
if (isSign) { |
||||||
|
String sign = rsaSign(encrypted, cusPrivateKey, charset, signType); |
||||||
|
sb.append("<sign>" + sign + "</sign>"); |
||||||
|
sb.append("<sign_type>"); |
||||||
|
sb.append(signType); |
||||||
|
sb.append("</sign_type>"); |
||||||
|
} |
||||||
|
sb.append("</alipay>"); |
||||||
|
} else if (isSign) {// 不加密,但需要签名
|
||||||
|
sb.append("<alipay>"); |
||||||
|
sb.append("<response>" + bizContent + "</response>"); |
||||||
|
String sign = rsaSign(bizContent, cusPrivateKey, charset, signType); |
||||||
|
sb.append("<sign>" + sign + "</sign>"); |
||||||
|
sb.append("<sign_type>"); |
||||||
|
sb.append(signType); |
||||||
|
sb.append("</sign_type>"); |
||||||
|
sb.append("</alipay>"); |
||||||
|
} else {// 不加密,不加签
|
||||||
|
sb.append(bizContent); |
||||||
|
} |
||||||
|
return sb.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 公钥加密 |
||||||
|
* |
||||||
|
* @param content 待加密内容 |
||||||
|
* @param publicKey 公钥 |
||||||
|
* @param charset 字符集,如UTF-8, GBK, GB2312 |
||||||
|
* @return 密文内容 |
||||||
|
*/ |
||||||
|
public static String rsaEncrypt(String content, String publicKey, |
||||||
|
String charset) { |
||||||
|
try { |
||||||
|
PublicKey pubKey = getPublicKeyFromX509(AlipayConstants.SIGN_TYPE_RSA, |
||||||
|
new ByteArrayInputStream(publicKey.getBytes())); |
||||||
|
Cipher cipher = Cipher.getInstance(AlipayConstants.SIGN_TYPE_RSA); |
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, pubKey); |
||||||
|
byte[] data = StringUtils.isEmpty(charset) ? content.getBytes() |
||||||
|
: content.getBytes(charset); |
||||||
|
int inputLen = data.length; |
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream(); |
||||||
|
int offSet = 0; |
||||||
|
byte[] cache; |
||||||
|
int i = 0; |
||||||
|
// 对数据分段加密
|
||||||
|
while (inputLen - offSet > 0) { |
||||||
|
if (inputLen - offSet > MAX_ENCRYPT_BLOCK) { |
||||||
|
cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK); |
||||||
|
} else { |
||||||
|
cache = cipher.doFinal(data, offSet, inputLen - offSet); |
||||||
|
} |
||||||
|
out.write(cache, 0, cache.length); |
||||||
|
i++; |
||||||
|
offSet = i * MAX_ENCRYPT_BLOCK; |
||||||
|
} |
||||||
|
byte[] encryptedData = Base64.encodeBase64(out.toByteArray()); |
||||||
|
out.close(); |
||||||
|
|
||||||
|
return StringUtils.isEmpty(charset) ? new String(encryptedData) |
||||||
|
: new String(encryptedData, charset); |
||||||
|
} catch (Exception e) { |
||||||
|
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(e); |
||||||
|
// throw new AlipayApiException("EncryptContent = " + content + ",charset = " + charset,
|
||||||
|
// e);
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 私钥解密 |
||||||
|
* |
||||||
|
* @param content 待解密内容 |
||||||
|
* @param publicKey 私钥 |
||||||
|
* @param charset 字符集,如UTF-8, GBK, GB2312 |
||||||
|
* @return 明文内容 |
||||||
|
*/ |
||||||
|
public static String rsaDecrypt(String content, String publicKey, |
||||||
|
String charset) { |
||||||
|
try { |
||||||
|
PrivateKey priKey = getPrivateKeyFromPKCS8(AlipayConstants.SIGN_TYPE_RSA, |
||||||
|
new ByteArrayInputStream(publicKey.getBytes())); |
||||||
|
Cipher cipher = Cipher.getInstance(AlipayConstants.SIGN_TYPE_RSA); |
||||||
|
cipher.init(Cipher.DECRYPT_MODE, priKey); |
||||||
|
byte[] encryptedData = StringUtils.isEmpty(charset) |
||||||
|
? Base64.decodeBase64(content.getBytes()) |
||||||
|
: Base64.decodeBase64(content.getBytes(charset)); |
||||||
|
int inputLen = encryptedData.length; |
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream(); |
||||||
|
int offSet = 0; |
||||||
|
byte[] cache; |
||||||
|
int i = 0; |
||||||
|
// 对数据分段解密
|
||||||
|
while (inputLen - offSet > 0) { |
||||||
|
if (inputLen - offSet > MAX_DECRYPT_BLOCK) { |
||||||
|
cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK); |
||||||
|
} else { |
||||||
|
cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet); |
||||||
|
} |
||||||
|
out.write(cache, 0, cache.length); |
||||||
|
i++; |
||||||
|
offSet = i * MAX_DECRYPT_BLOCK; |
||||||
|
} |
||||||
|
byte[] decryptedData = out.toByteArray(); |
||||||
|
out.close(); |
||||||
|
|
||||||
|
return StringUtils.isEmpty(charset) ? new String(decryptedData) |
||||||
|
: new String(decryptedData, charset); |
||||||
|
} catch (Exception e) { |
||||||
|
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(e); |
||||||
|
// throw new AlipayApiException("EncodeContent = " + content + ",charset = " + charset, e);
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue