parent
85d61d2f1e
commit
46a546f1da
@ -1,23 +1,25 @@ |
||||
# Compiled class file |
||||
*.class |
||||
/target/ |
||||
!.mvn/wrapper/maven-wrapper.jar |
||||
|
||||
# Log file |
||||
*.log |
||||
### STS ### |
||||
.apt_generated |
||||
.classpath |
||||
.factorypath |
||||
.project |
||||
.settings |
||||
.springBeans |
||||
.sts4-cache |
||||
|
||||
# BlueJ files |
||||
*.ctxt |
||||
### IntelliJ IDEA ### |
||||
.idea |
||||
*.iws |
||||
*.iml |
||||
*.ipr |
||||
|
||||
# Mobile Tools for Java (J2ME) |
||||
.mtj.tmp/ |
||||
|
||||
# Package Files # |
||||
*.jar |
||||
*.war |
||||
*.nar |
||||
*.ear |
||||
*.zip |
||||
*.tar.gz |
||||
*.rar |
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml |
||||
hs_err_pid* |
||||
### NetBeans ### |
||||
/nbproject/private/ |
||||
/nbbuild/ |
||||
/dist/ |
||||
/nbdist/ |
||||
/.nb-gradle/ |
||||
/build/ |
||||
|
@ -1,37 +1,45 @@ |
||||
# SOP |
||||
# SOP(Simple Open Platform) |
||||
|
||||
#### 介绍 |
||||
一个简单易用的接口开放平台(Simple Open Platform),可实现类似于淘宝开放平台的调用方式。 |
||||
一个开放平台解决方案项目,基于Spring Cloud实现,目标是能够让用户快速得搭建起自己的开放平台。 |
||||
|
||||
#### 软件架构 |
||||
软件架构说明 |
||||
SOP提供了两种接口调用方式,分别是:[支付宝开放平台](https://docs.open.alipay.com/api)的调用方式和[淘宝开放平台](http://open.taobao.com/api.htm?docId=285&docType=2)的调用方式。 |
||||
通过简单的配置后,你的项目就具备了和支付宝开放平台的一样的接口提供能力。 |
||||
|
||||
SOP封装了开放平台大部分功能包括:签名验证、统一异常处理、统一返回内容 、业务参数验证(JSR-303)、秘钥管理等,未来还会实现更多功能。 |
||||
|
||||
#### 安装教程 |
||||
## 项目特点 |
||||
|
||||
1. xxxx |
||||
2. xxxx |
||||
3. xxxx |
||||
- 接入方式简单,与老项目不冲突,老项目注册到注册中心,然后的方法上加上注解即可。 |
||||
- 架构松耦合,业务代码实现在各自微服务上,SOP不参与业务实现,这也是Spring Cloud微服务体系带来的好处。 |
||||
- 扩展简单,开放平台对应的功能各自独立,可以自定义实现自己的需求,如:更改参数,更改签名规则等。 |
||||
|
||||
#### 使用说明 |
||||
## 谁能使用这个项目 |
||||
|
||||
1. xxxx |
||||
2. xxxx |
||||
3. xxxx |
||||
- 有现成的项目,想改造成开放平台供他人调用 |
||||
- 有现成的项目,想暴露其中几个接口并通过开放平台供他人调用 |
||||
- 想搭一个开放平台新项目,并结合微服务的方式去维护 |
||||
- 对开放平台感兴趣的朋友 |
||||
|
||||
#### 参与贡献 |
||||
以上情况都可以考虑使用SOP |
||||
|
||||
1. Fork 本仓库 |
||||
2. 新建 Feat_xxx 分支 |
||||
3. 提交代码 |
||||
4. 新建 Pull Request |
||||
## 架构图 |
||||
|
||||
|
||||
#### 码云特技 |
||||
|
||||
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md |
||||
2. 码云官方博客 [blog.gitee.com](https://blog.gitee.com) |
||||
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解码云上的优秀开源项目 |
||||
4. [GVP](https://gitee.com/gvp) 全称是码云最有价值开源项目,是码云综合评定出的优秀开源项目 |
||||
5. 码云官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) |
||||
6. 码云封面人物是一档用来展示码云会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) |
||||
## 工程说明 |
||||
|
||||
- sop-registry:注册中心,eureka实现 |
||||
- sop-gateway:网关,zuul实现,统一访问入口 |
||||
- sop-gateway-common:网关公共模块,封装常用功能,包含签名校验等功能。 |
||||
- sop-server-common:微服务端公共模块,封装配套功能 |
||||
- sop-story:示例,story服务,同时作为Provider提供服务 |
||||
- sop-book:示例,book服务,也是Consumer,调用story提供的服务 |
||||
- sop-test:测试用例 |
||||
|
||||
## 相关文档 |
||||
|
||||
|
||||
|
||||
## 沟通交流 |
||||
|
||||
Q群:328419269 |
||||
|
@ -0,0 +1,286 @@ |
||||
#!/bin/sh |
||||
# ---------------------------------------------------------------------------- |
||||
# Licensed to the Apache Software Foundation (ASF) under one |
||||
# or more contributor license agreements. See the NOTICE file |
||||
# distributed with this work for additional information |
||||
# regarding copyright ownership. The ASF licenses this file |
||||
# to you under the Apache License, Version 2.0 (the |
||||
# "License"); you may not use this file except in compliance |
||||
# with the License. You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, |
||||
# software distributed under the License is distributed on an |
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
||||
# KIND, either express or implied. See the License for the |
||||
# specific language governing permissions and limitations |
||||
# under the License. |
||||
# ---------------------------------------------------------------------------- |
||||
|
||||
# ---------------------------------------------------------------------------- |
||||
# Maven2 Start Up Batch script |
||||
# |
||||
# Required ENV vars: |
||||
# ------------------ |
||||
# JAVA_HOME - location of a JDK home dir |
||||
# |
||||
# Optional ENV vars |
||||
# ----------------- |
||||
# M2_HOME - location of maven2's installed home dir |
||||
# MAVEN_OPTS - parameters passed to the Java VM when running Maven |
||||
# e.g. to debug Maven itself, use |
||||
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 |
||||
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files |
||||
# ---------------------------------------------------------------------------- |
||||
|
||||
if [ -z "$MAVEN_SKIP_RC" ] ; then |
||||
|
||||
if [ -f /etc/mavenrc ] ; then |
||||
. /etc/mavenrc |
||||
fi |
||||
|
||||
if [ -f "$HOME/.mavenrc" ] ; then |
||||
. "$HOME/.mavenrc" |
||||
fi |
||||
|
||||
fi |
||||
|
||||
# OS specific support. $var _must_ be set to either true or false. |
||||
cygwin=false; |
||||
darwin=false; |
||||
mingw=false |
||||
case "`uname`" in |
||||
CYGWIN*) cygwin=true ;; |
||||
MINGW*) mingw=true;; |
||||
Darwin*) darwin=true |
||||
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home |
||||
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html |
||||
if [ -z "$JAVA_HOME" ]; then |
||||
if [ -x "/usr/libexec/java_home" ]; then |
||||
export JAVA_HOME="`/usr/libexec/java_home`" |
||||
else |
||||
export JAVA_HOME="/Library/Java/Home" |
||||
fi |
||||
fi |
||||
;; |
||||
esac |
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then |
||||
if [ -r /etc/gentoo-release ] ; then |
||||
JAVA_HOME=`java-config --jre-home` |
||||
fi |
||||
fi |
||||
|
||||
if [ -z "$M2_HOME" ] ; then |
||||
## resolve links - $0 may be a link to maven's home |
||||
PRG="$0" |
||||
|
||||
# need this for relative symlinks |
||||
while [ -h "$PRG" ] ; do |
||||
ls=`ls -ld "$PRG"` |
||||
link=`expr "$ls" : '.*-> \(.*\)$'` |
||||
if expr "$link" : '/.*' > /dev/null; then |
||||
PRG="$link" |
||||
else |
||||
PRG="`dirname "$PRG"`/$link" |
||||
fi |
||||
done |
||||
|
||||
saveddir=`pwd` |
||||
|
||||
M2_HOME=`dirname "$PRG"`/.. |
||||
|
||||
# make it fully qualified |
||||
M2_HOME=`cd "$M2_HOME" && pwd` |
||||
|
||||
cd "$saveddir" |
||||
# echo Using m2 at $M2_HOME |
||||
fi |
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched |
||||
if $cygwin ; then |
||||
[ -n "$M2_HOME" ] && |
||||
M2_HOME=`cygpath --unix "$M2_HOME"` |
||||
[ -n "$JAVA_HOME" ] && |
||||
JAVA_HOME=`cygpath --unix "$JAVA_HOME"` |
||||
[ -n "$CLASSPATH" ] && |
||||
CLASSPATH=`cygpath --path --unix "$CLASSPATH"` |
||||
fi |
||||
|
||||
# For Mingw, ensure paths are in UNIX format before anything is touched |
||||
if $mingw ; then |
||||
[ -n "$M2_HOME" ] && |
||||
M2_HOME="`(cd "$M2_HOME"; pwd)`" |
||||
[ -n "$JAVA_HOME" ] && |
||||
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" |
||||
# TODO classpath? |
||||
fi |
||||
|
||||
if [ -z "$JAVA_HOME" ]; then |
||||
javaExecutable="`which javac`" |
||||
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then |
||||
# readlink(1) is not available as standard on Solaris 10. |
||||
readLink=`which readlink` |
||||
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then |
||||
if $darwin ; then |
||||
javaHome="`dirname \"$javaExecutable\"`" |
||||
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" |
||||
else |
||||
javaExecutable="`readlink -f \"$javaExecutable\"`" |
||||
fi |
||||
javaHome="`dirname \"$javaExecutable\"`" |
||||
javaHome=`expr "$javaHome" : '\(.*\)/bin'` |
||||
JAVA_HOME="$javaHome" |
||||
export JAVA_HOME |
||||
fi |
||||
fi |
||||
fi |
||||
|
||||
if [ -z "$JAVACMD" ] ; then |
||||
if [ -n "$JAVA_HOME" ] ; then |
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then |
||||
# IBM's JDK on AIX uses strange locations for the executables |
||||
JAVACMD="$JAVA_HOME/jre/sh/java" |
||||
else |
||||
JAVACMD="$JAVA_HOME/bin/java" |
||||
fi |
||||
else |
||||
JAVACMD="`which java`" |
||||
fi |
||||
fi |
||||
|
||||
if [ ! -x "$JAVACMD" ] ; then |
||||
echo "Error: JAVA_HOME is not defined correctly." >&2 |
||||
echo " We cannot execute $JAVACMD" >&2 |
||||
exit 1 |
||||
fi |
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then |
||||
echo "Warning: JAVA_HOME environment variable is not set." |
||||
fi |
||||
|
||||
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher |
||||
|
||||
# traverses directory structure from process work directory to filesystem root |
||||
# first directory with .mvn subdirectory is considered project base directory |
||||
find_maven_basedir() { |
||||
|
||||
if [ -z "$1" ] |
||||
then |
||||
echo "Path not specified to find_maven_basedir" |
||||
return 1 |
||||
fi |
||||
|
||||
basedir="$1" |
||||
wdir="$1" |
||||
while [ "$wdir" != '/' ] ; do |
||||
if [ -d "$wdir"/.mvn ] ; then |
||||
basedir=$wdir |
||||
break |
||||
fi |
||||
# workaround for JBEAP-8937 (on Solaris 10/Sparc) |
||||
if [ -d "${wdir}" ]; then |
||||
wdir=`cd "$wdir/.."; pwd` |
||||
fi |
||||
# end of workaround |
||||
done |
||||
echo "${basedir}" |
||||
} |
||||
|
||||
# concatenates all lines of a file |
||||
concat_lines() { |
||||
if [ -f "$1" ]; then |
||||
echo "$(tr -s '\n' ' ' < "$1")" |
||||
fi |
||||
} |
||||
|
||||
BASE_DIR=`find_maven_basedir "$(pwd)"` |
||||
if [ -z "$BASE_DIR" ]; then |
||||
exit 1; |
||||
fi |
||||
|
||||
########################################################################################## |
||||
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central |
||||
# This allows using the maven wrapper in projects that prohibit checking in binary data. |
||||
########################################################################################## |
||||
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then |
||||
if [ "$MVNW_VERBOSE" = true ]; then |
||||
echo "Found .mvn/wrapper/maven-wrapper.jar" |
||||
fi |
||||
else |
||||
if [ "$MVNW_VERBOSE" = true ]; then |
||||
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." |
||||
fi |
||||
jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" |
||||
while IFS="=" read key value; do |
||||
case "$key" in (wrapperUrl) jarUrl="$value"; break ;; |
||||
esac |
||||
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" |
||||
if [ "$MVNW_VERBOSE" = true ]; then |
||||
echo "Downloading from: $jarUrl" |
||||
fi |
||||
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" |
||||
|
||||
if command -v wget > /dev/null; then |
||||
if [ "$MVNW_VERBOSE" = true ]; then |
||||
echo "Found wget ... using wget" |
||||
fi |
||||
wget "$jarUrl" -O "$wrapperJarPath" |
||||
elif command -v curl > /dev/null; then |
||||
if [ "$MVNW_VERBOSE" = true ]; then |
||||
echo "Found curl ... using curl" |
||||
fi |
||||
curl -o "$wrapperJarPath" "$jarUrl" |
||||
else |
||||
if [ "$MVNW_VERBOSE" = true ]; then |
||||
echo "Falling back to using Java to download" |
||||
fi |
||||
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" |
||||
if [ -e "$javaClass" ]; then |
||||
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then |
||||
if [ "$MVNW_VERBOSE" = true ]; then |
||||
echo " - Compiling MavenWrapperDownloader.java ..." |
||||
fi |
||||
# Compiling the Java class |
||||
("$JAVA_HOME/bin/javac" "$javaClass") |
||||
fi |
||||
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then |
||||
# Running the downloader |
||||
if [ "$MVNW_VERBOSE" = true ]; then |
||||
echo " - Running MavenWrapperDownloader.java ..." |
||||
fi |
||||
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") |
||||
fi |
||||
fi |
||||
fi |
||||
fi |
||||
########################################################################################## |
||||
# End of extension |
||||
########################################################################################## |
||||
|
||||
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} |
||||
if [ "$MVNW_VERBOSE" = true ]; then |
||||
echo $MAVEN_PROJECTBASEDIR |
||||
fi |
||||
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" |
||||
|
||||
# For Cygwin, switch paths to Windows format before running java |
||||
if $cygwin; then |
||||
[ -n "$M2_HOME" ] && |
||||
M2_HOME=`cygpath --path --windows "$M2_HOME"` |
||||
[ -n "$JAVA_HOME" ] && |
||||
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` |
||||
[ -n "$CLASSPATH" ] && |
||||
CLASSPATH=`cygpath --path --windows "$CLASSPATH"` |
||||
[ -n "$MAVEN_PROJECTBASEDIR" ] && |
||||
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` |
||||
fi |
||||
|
||||
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain |
||||
|
||||
exec "$JAVACMD" \ |
||||
$MAVEN_OPTS \ |
||||
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ |
||||
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ |
||||
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" |
@ -0,0 +1,161 @@ |
||||
@REM ---------------------------------------------------------------------------- |
||||
@REM Licensed to the Apache Software Foundation (ASF) under one |
||||
@REM or more contributor license agreements. See the NOTICE file |
||||
@REM distributed with this work for additional information |
||||
@REM regarding copyright ownership. The ASF licenses this file |
||||
@REM to you under the Apache License, Version 2.0 (the |
||||
@REM "License"); you may not use this file except in compliance |
||||
@REM with the License. You may obtain a copy of the License at |
||||
@REM |
||||
@REM http://www.apache.org/licenses/LICENSE-2.0 |
||||
@REM |
||||
@REM Unless required by applicable law or agreed to in writing, |
||||
@REM software distributed under the License is distributed on an |
||||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
||||
@REM KIND, either express or implied. See the License for the |
||||
@REM specific language governing permissions and limitations |
||||
@REM under the License. |
||||
@REM ---------------------------------------------------------------------------- |
||||
|
||||
@REM ---------------------------------------------------------------------------- |
||||
@REM Maven2 Start Up Batch script |
||||
@REM |
||||
@REM Required ENV vars: |
||||
@REM JAVA_HOME - location of a JDK home dir |
||||
@REM |
||||
@REM Optional ENV vars |
||||
@REM M2_HOME - location of maven2's installed home dir |
||||
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands |
||||
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending |
||||
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven |
||||
@REM e.g. to debug Maven itself, use |
||||
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 |
||||
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files |
||||
@REM ---------------------------------------------------------------------------- |
||||
|
||||
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' |
||||
@echo off |
||||
@REM set title of command window |
||||
title %0 |
||||
@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' |
||||
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% |
||||
|
||||
@REM set %HOME% to equivalent of $HOME |
||||
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") |
||||
|
||||
@REM Execute a user defined script before this one |
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre |
||||
@REM check for pre script, once with legacy .bat ending and once with .cmd ending |
||||
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" |
||||
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" |
||||
:skipRcPre |
||||
|
||||
@setlocal |
||||
|
||||
set ERROR_CODE=0 |
||||
|
||||
@REM To isolate internal variables from possible post scripts, we use another setlocal |
||||
@setlocal |
||||
|
||||
@REM ==== START VALIDATION ==== |
||||
if not "%JAVA_HOME%" == "" goto OkJHome |
||||
|
||||
echo. |
||||
echo Error: JAVA_HOME not found in your environment. >&2 |
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2 |
||||
echo location of your Java installation. >&2 |
||||
echo. |
||||
goto error |
||||
|
||||
:OkJHome |
||||
if exist "%JAVA_HOME%\bin\java.exe" goto init |
||||
|
||||
echo. |
||||
echo Error: JAVA_HOME is set to an invalid directory. >&2 |
||||
echo JAVA_HOME = "%JAVA_HOME%" >&2 |
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2 |
||||
echo location of your Java installation. >&2 |
||||
echo. |
||||
goto error |
||||
|
||||
@REM ==== END VALIDATION ==== |
||||
|
||||
:init |
||||
|
||||
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". |
||||
@REM Fallback to current working directory if not found. |
||||
|
||||
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% |
||||
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir |
||||
|
||||
set EXEC_DIR=%CD% |
||||
set WDIR=%EXEC_DIR% |
||||
:findBaseDir |
||||
IF EXIST "%WDIR%"\.mvn goto baseDirFound |
||||
cd .. |
||||
IF "%WDIR%"=="%CD%" goto baseDirNotFound |
||||
set WDIR=%CD% |
||||
goto findBaseDir |
||||
|
||||
:baseDirFound |
||||
set MAVEN_PROJECTBASEDIR=%WDIR% |
||||
cd "%EXEC_DIR%" |
||||
goto endDetectBaseDir |
||||
|
||||
:baseDirNotFound |
||||
set MAVEN_PROJECTBASEDIR=%EXEC_DIR% |
||||
cd "%EXEC_DIR%" |
||||
|
||||
:endDetectBaseDir |
||||
|
||||
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig |
||||
|
||||
@setlocal EnableExtensions EnableDelayedExpansion |
||||
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a |
||||
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% |
||||
|
||||
:endReadAdditionalConfig |
||||
|
||||
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" |
||||
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" |
||||
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain |
||||
|
||||
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" |
||||
FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( |
||||
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B |
||||
) |
||||
|
||||
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central |
||||
@REM This allows using the maven wrapper in projects that prohibit checking in binary data. |
||||
if exist %WRAPPER_JAR% ( |
||||
echo Found %WRAPPER_JAR% |
||||
) else ( |
||||
echo Couldn't find %WRAPPER_JAR%, downloading it ... |
||||
echo Downloading from: %DOWNLOAD_URL% |
||||
powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" |
||||
echo Finished downloading %WRAPPER_JAR% |
||||
) |
||||
@REM End of extension |
||||
|
||||
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* |
||||
if ERRORLEVEL 1 goto error |
||||
goto end |
||||
|
||||
:error |
||||
set ERROR_CODE=1 |
||||
|
||||
:end |
||||
@endlocal & set ERROR_CODE=%ERROR_CODE% |
||||
|
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost |
||||
@REM check for post script, once with legacy .bat ending and once with .cmd ending |
||||
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" |
||||
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" |
||||
:skipRcPost |
||||
|
||||
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' |
||||
if "%MAVEN_BATCH_PAUSE%" == "on" pause |
||||
|
||||
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% |
||||
|
||||
exit /B %ERROR_CODE% |
@ -0,0 +1,20 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
<modelVersion>4.0.0</modelVersion> |
||||
<groupId>com.gitee.sop</groupId> |
||||
<artifactId>sop-parent</artifactId> |
||||
<version>1.0.0-SNAPSHOT</version> |
||||
<packaging>pom</packaging> |
||||
|
||||
<modules> |
||||
<module>sop-registry</module> |
||||
<module>sop-gateway</module> |
||||
<module>sop-gateway-common</module> |
||||
<module>sop-server-common</module> |
||||
<module>sop-story</module> |
||||
<module>sop-book</module> |
||||
<module>sop-test</module> |
||||
</modules> |
||||
</project> |
@ -0,0 +1,15 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
<modelVersion>4.0.0</modelVersion> |
||||
<groupId>com.gitee.sop</groupId> |
||||
<artifactId>sop-book-parent</artifactId> |
||||
<version>1.0.0-SNAPSHOT</version> |
||||
<packaging>pom</packaging> |
||||
|
||||
<modules> |
||||
<module>sop-book-api</module> |
||||
<module>sop-book-web</module> |
||||
</modules> |
||||
</project> |
@ -0,0 +1,25 @@ |
||||
/target/ |
||||
!.mvn/wrapper/maven-wrapper.jar |
||||
|
||||
### STS ### |
||||
.apt_generated |
||||
.classpath |
||||
.factorypath |
||||
.project |
||||
.settings |
||||
.springBeans |
||||
.sts4-cache |
||||
|
||||
### IntelliJ IDEA ### |
||||
.idea |
||||
*.iws |
||||
*.iml |
||||
*.ipr |
||||
|
||||
### NetBeans ### |
||||
/nbproject/private/ |
||||
/nbbuild/ |
||||
/dist/ |
||||
/nbdist/ |
||||
/.nb-gradle/ |
||||
/build/ |
@ -0,0 +1,46 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
<modelVersion>4.0.0</modelVersion> |
||||
<parent> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter-parent</artifactId> |
||||
<version>2.1.2.RELEASE</version> |
||||
<relativePath/> <!-- lookup parent from repository --> |
||||
</parent> |
||||
<groupId>com.gitee.sop</groupId> |
||||
<artifactId>sop-book-api</artifactId> |
||||
<version>1.0-SNAPSHOT</version> |
||||
|
||||
<properties> |
||||
<java.version>1.8</java.version> |
||||
<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version> |
||||
</properties> |
||||
|
||||
<dependencies> |
||||
<dependency> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter-web</artifactId> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.projectlombok</groupId> |
||||
<artifactId>lombok</artifactId> |
||||
<version>1.18.4</version> |
||||
<scope>provided</scope> |
||||
</dependency> |
||||
</dependencies> |
||||
|
||||
<dependencyManagement> |
||||
<dependencies> |
||||
<dependency> |
||||
<groupId>org.springframework.cloud</groupId> |
||||
<artifactId>spring-cloud-dependencies</artifactId> |
||||
<version>${spring-cloud.version}</version> |
||||
<type>pom</type> |
||||
<scope>import</scope> |
||||
</dependency> |
||||
</dependencies> |
||||
</dependencyManagement> |
||||
</project> |
@ -0,0 +1,12 @@ |
||||
package com.gitee.book.api.domain; |
||||
|
||||
import lombok.Data; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
@Data |
||||
public class Book { |
||||
private int id; |
||||
private String name; |
||||
} |
@ -0,0 +1,14 @@ |
||||
package com.hhdd.book.api.service; |
||||
|
||||
import com.gitee.book.api.domain.Book; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.RequestParam; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
@RequestMapping("/book") |
||||
public interface BookService { |
||||
@RequestMapping("/getBook") |
||||
Book getBook(@RequestParam("id") int id); |
||||
} |
@ -0,0 +1,25 @@ |
||||
/target/ |
||||
!.mvn/wrapper/maven-wrapper.jar |
||||
|
||||
### STS ### |
||||
.apt_generated |
||||
.classpath |
||||
.factorypath |
||||
.project |
||||
.settings |
||||
.springBeans |
||||
.sts4-cache |
||||
|
||||
### IntelliJ IDEA ### |
||||
.idea |
||||
*.iws |
||||
*.iml |
||||
*.ipr |
||||
|
||||
### NetBeans ### |
||||
/nbproject/private/ |
||||
/nbbuild/ |
||||
/dist/ |
||||
/nbdist/ |
||||
/.nb-gradle/ |
||||
/build/ |
Binary file not shown.
@ -0,0 +1 @@ |
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.0/apache-maven-3.6.0-bin.zip |
@ -0,0 +1,286 @@ |
||||
#!/bin/sh |
||||
# ---------------------------------------------------------------------------- |
||||
# Licensed to the Apache Software Foundation (ASF) under one |
||||
# or more contributor license agreements. See the NOTICE file |
||||
# distributed with this work for additional information |
||||
# regarding copyright ownership. The ASF licenses this file |
||||
# to you under the Apache License, Version 2.0 (the |
||||
# "License"); you may not use this file except in compliance |
||||
# with the License. You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, |
||||
# software distributed under the License is distributed on an |
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
||||
# KIND, either express or implied. See the License for the |
||||
# specific language governing permissions and limitations |
||||
# under the License. |
||||
# ---------------------------------------------------------------------------- |
||||
|
||||
# ---------------------------------------------------------------------------- |
||||
# Maven2 Start Up Batch script |
||||
# |
||||
# Required ENV vars: |
||||
# ------------------ |
||||
# JAVA_HOME - location of a JDK home dir |
||||
# |
||||
# Optional ENV vars |
||||
# ----------------- |
||||
# M2_HOME - location of maven2's installed home dir |
||||
# MAVEN_OPTS - parameters passed to the Java VM when running Maven |
||||
# e.g. to debug Maven itself, use |
||||
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 |
||||
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files |
||||
# ---------------------------------------------------------------------------- |
||||
|
||||
if [ -z "$MAVEN_SKIP_RC" ] ; then |
||||
|
||||
if [ -f /etc/mavenrc ] ; then |
||||
. /etc/mavenrc |
||||
fi |
||||
|
||||
if [ -f "$HOME/.mavenrc" ] ; then |
||||
. "$HOME/.mavenrc" |
||||
fi |
||||
|
||||
fi |
||||
|
||||
# OS specific support. $var _must_ be set to either true or false. |
||||
cygwin=false; |
||||
darwin=false; |
||||
mingw=false |
||||
case "`uname`" in |
||||
CYGWIN*) cygwin=true ;; |
||||
MINGW*) mingw=true;; |
||||
Darwin*) darwin=true |
||||
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home |
||||
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html |
||||
if [ -z "$JAVA_HOME" ]; then |
||||
if [ -x "/usr/libexec/java_home" ]; then |
||||
export JAVA_HOME="`/usr/libexec/java_home`" |
||||
else |
||||
export JAVA_HOME="/Library/Java/Home" |
||||
fi |
||||
fi |
||||
;; |
||||
esac |
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then |
||||
if [ -r /etc/gentoo-release ] ; then |
||||
JAVA_HOME=`java-config --jre-home` |
||||
fi |
||||
fi |
||||
|
||||
if [ -z "$M2_HOME" ] ; then |
||||
## resolve links - $0 may be a link to maven's home |
||||
PRG="$0" |
||||
|
||||
# need this for relative symlinks |
||||
while [ -h "$PRG" ] ; do |
||||
ls=`ls -ld "$PRG"` |
||||
link=`expr "$ls" : '.*-> \(.*\)$'` |
||||
if expr "$link" : '/.*' > /dev/null; then |
||||
PRG="$link" |
||||
else |
||||
PRG="`dirname "$PRG"`/$link" |
||||
fi |
||||
done |
||||
|
||||
saveddir=`pwd` |
||||
|
||||
M2_HOME=`dirname "$PRG"`/.. |
||||
|
||||
# make it fully qualified |
||||
M2_HOME=`cd "$M2_HOME" && pwd` |
||||
|
||||
cd "$saveddir" |
||||
# echo Using m2 at $M2_HOME |
||||
fi |
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched |
||||
if $cygwin ; then |
||||
[ -n "$M2_HOME" ] && |
||||
M2_HOME=`cygpath --unix "$M2_HOME"` |
||||
[ -n "$JAVA_HOME" ] && |
||||
JAVA_HOME=`cygpath --unix "$JAVA_HOME"` |
||||
[ -n "$CLASSPATH" ] && |
||||
CLASSPATH=`cygpath --path --unix "$CLASSPATH"` |
||||
fi |
||||
|
||||
# For Mingw, ensure paths are in UNIX format before anything is touched |
||||
if $mingw ; then |
||||
[ -n "$M2_HOME" ] && |
||||
M2_HOME="`(cd "$M2_HOME"; pwd)`" |
||||
[ -n "$JAVA_HOME" ] && |
||||
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" |
||||
# TODO classpath? |
||||
fi |
||||
|
||||
if [ -z "$JAVA_HOME" ]; then |
||||
javaExecutable="`which javac`" |
||||
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then |
||||
# readlink(1) is not available as standard on Solaris 10. |
||||
readLink=`which readlink` |
||||
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then |
||||
if $darwin ; then |
||||
javaHome="`dirname \"$javaExecutable\"`" |
||||
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" |
||||
else |
||||
javaExecutable="`readlink -f \"$javaExecutable\"`" |
||||
fi |
||||
javaHome="`dirname \"$javaExecutable\"`" |
||||
javaHome=`expr "$javaHome" : '\(.*\)/bin'` |
||||
JAVA_HOME="$javaHome" |
||||
export JAVA_HOME |
||||
fi |
||||
fi |
||||
fi |
||||
|
||||
if [ -z "$JAVACMD" ] ; then |
||||
if [ -n "$JAVA_HOME" ] ; then |
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then |
||||
# IBM's JDK on AIX uses strange locations for the executables |
||||
JAVACMD="$JAVA_HOME/jre/sh/java" |
||||
else |
||||
JAVACMD="$JAVA_HOME/bin/java" |
||||
fi |
||||
else |
||||
JAVACMD="`which java`" |
||||
fi |
||||
fi |
||||
|
||||
if [ ! -x "$JAVACMD" ] ; then |
||||
echo "Error: JAVA_HOME is not defined correctly." >&2 |
||||
echo " We cannot execute $JAVACMD" >&2 |
||||
exit 1 |
||||
fi |
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then |
||||
echo "Warning: JAVA_HOME environment variable is not set." |
||||
fi |
||||
|
||||
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher |
||||
|
||||
# traverses directory structure from process work directory to filesystem root |
||||
# first directory with .mvn subdirectory is considered project base directory |
||||
find_maven_basedir() { |
||||
|
||||
if [ -z "$1" ] |
||||
then |
||||
echo "Path not specified to find_maven_basedir" |
||||
return 1 |
||||
fi |
||||
|
||||
basedir="$1" |
||||
wdir="$1" |
||||
while [ "$wdir" != '/' ] ; do |
||||
if [ -d "$wdir"/.mvn ] ; then |
||||
basedir=$wdir |
||||
break |
||||
fi |
||||
# workaround for JBEAP-8937 (on Solaris 10/Sparc) |
||||
if [ -d "${wdir}" ]; then |
||||
wdir=`cd "$wdir/.."; pwd` |
||||
fi |
||||
# end of workaround |
||||
done |
||||
echo "${basedir}" |
||||
} |
||||
|
||||
# concatenates all lines of a file |
||||
concat_lines() { |
||||
if [ -f "$1" ]; then |
||||
echo "$(tr -s '\n' ' ' < "$1")" |
||||
fi |
||||
} |
||||
|
||||
BASE_DIR=`find_maven_basedir "$(pwd)"` |
||||
if [ -z "$BASE_DIR" ]; then |
||||
exit 1; |
||||
fi |
||||
|
||||
########################################################################################## |
||||
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central |
||||
# This allows using the maven wrapper in projects that prohibit checking in binary data. |
||||
########################################################################################## |
||||
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then |
||||
if [ "$MVNW_VERBOSE" = true ]; then |
||||
echo "Found .mvn/wrapper/maven-wrapper.jar" |
||||
fi |
||||
else |
||||
if [ "$MVNW_VERBOSE" = true ]; then |
||||
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." |
||||
fi |
||||
jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" |
||||
while IFS="=" read key value; do |
||||
case "$key" in (wrapperUrl) jarUrl="$value"; break ;; |
||||
esac |
||||
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" |
||||
if [ "$MVNW_VERBOSE" = true ]; then |
||||
echo "Downloading from: $jarUrl" |
||||
fi |
||||
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" |
||||
|
||||
if command -v wget > /dev/null; then |
||||
if [ "$MVNW_VERBOSE" = true ]; then |
||||
echo "Found wget ... using wget" |
||||
fi |
||||
wget "$jarUrl" -O "$wrapperJarPath" |
||||
elif command -v curl > /dev/null; then |
||||
if [ "$MVNW_VERBOSE" = true ]; then |
||||
echo "Found curl ... using curl" |
||||
fi |
||||
curl -o "$wrapperJarPath" "$jarUrl" |
||||
else |
||||
if [ "$MVNW_VERBOSE" = true ]; then |
||||
echo "Falling back to using Java to download" |
||||
fi |
||||
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" |
||||
if [ -e "$javaClass" ]; then |
||||
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then |
||||
if [ "$MVNW_VERBOSE" = true ]; then |
||||
echo " - Compiling MavenWrapperDownloader.java ..." |
||||
fi |
||||
# Compiling the Java class |
||||
("$JAVA_HOME/bin/javac" "$javaClass") |
||||
fi |
||||
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then |
||||
# Running the downloader |
||||
if [ "$MVNW_VERBOSE" = true ]; then |
||||
echo " - Running MavenWrapperDownloader.java ..." |
||||
fi |
||||
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") |
||||
fi |
||||
fi |
||||
fi |
||||
fi |
||||
########################################################################################## |
||||
# End of extension |
||||
########################################################################################## |
||||
|
||||
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} |
||||
if [ "$MVNW_VERBOSE" = true ]; then |
||||
echo $MAVEN_PROJECTBASEDIR |
||||
fi |
||||
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" |
||||
|
||||
# For Cygwin, switch paths to Windows format before running java |
||||
if $cygwin; then |
||||
[ -n "$M2_HOME" ] && |
||||
M2_HOME=`cygpath --path --windows "$M2_HOME"` |
||||
[ -n "$JAVA_HOME" ] && |
||||
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` |
||||
[ -n "$CLASSPATH" ] && |
||||
CLASSPATH=`cygpath --path --windows "$CLASSPATH"` |
||||
[ -n "$MAVEN_PROJECTBASEDIR" ] && |
||||
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` |
||||
fi |
||||
|
||||
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain |
||||
|
||||
exec "$JAVACMD" \ |
||||
$MAVEN_OPTS \ |
||||
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ |
||||
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ |
||||
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" |
@ -0,0 +1,161 @@ |
||||
@REM ---------------------------------------------------------------------------- |
||||
@REM Licensed to the Apache Software Foundation (ASF) under one |
||||
@REM or more contributor license agreements. See the NOTICE file |
||||
@REM distributed with this work for additional information |
||||
@REM regarding copyright ownership. The ASF licenses this file |
||||
@REM to you under the Apache License, Version 2.0 (the |
||||
@REM "License"); you may not use this file except in compliance |
||||
@REM with the License. You may obtain a copy of the License at |
||||
@REM |
||||
@REM http://www.apache.org/licenses/LICENSE-2.0 |
||||
@REM |
||||
@REM Unless required by applicable law or agreed to in writing, |
||||
@REM software distributed under the License is distributed on an |
||||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
||||
@REM KIND, either express or implied. See the License for the |
||||
@REM specific language governing permissions and limitations |
||||
@REM under the License. |
||||
@REM ---------------------------------------------------------------------------- |
||||
|
||||
@REM ---------------------------------------------------------------------------- |
||||
@REM Maven2 Start Up Batch script |
||||
@REM |
||||
@REM Required ENV vars: |
||||
@REM JAVA_HOME - location of a JDK home dir |
||||
@REM |
||||
@REM Optional ENV vars |
||||
@REM M2_HOME - location of maven2's installed home dir |
||||
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands |
||||
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending |
||||
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven |
||||
@REM e.g. to debug Maven itself, use |
||||
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 |
||||
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files |
||||
@REM ---------------------------------------------------------------------------- |
||||
|
||||
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' |
||||
@echo off |
||||
@REM set title of command window |
||||
title %0 |
||||
@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' |
||||
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% |
||||
|
||||
@REM set %HOME% to equivalent of $HOME |
||||
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") |
||||
|
||||
@REM Execute a user defined script before this one |
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre |
||||
@REM check for pre script, once with legacy .bat ending and once with .cmd ending |
||||
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" |
||||
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" |
||||
:skipRcPre |
||||
|
||||
@setlocal |
||||
|
||||
set ERROR_CODE=0 |
||||
|
||||
@REM To isolate internal variables from possible post scripts, we use another setlocal |
||||
@setlocal |
||||
|
||||
@REM ==== START VALIDATION ==== |
||||
if not "%JAVA_HOME%" == "" goto OkJHome |
||||
|
||||
echo. |
||||
echo Error: JAVA_HOME not found in your environment. >&2 |
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2 |
||||
echo location of your Java installation. >&2 |
||||
echo. |
||||
goto error |
||||
|
||||
:OkJHome |
||||
if exist "%JAVA_HOME%\bin\java.exe" goto init |
||||
|
||||
echo. |
||||
echo Error: JAVA_HOME is set to an invalid directory. >&2 |
||||
echo JAVA_HOME = "%JAVA_HOME%" >&2 |
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2 |
||||
echo location of your Java installation. >&2 |
||||
echo. |
||||
goto error |
||||
|
||||
@REM ==== END VALIDATION ==== |
||||
|
||||
:init |
||||
|
||||
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". |
||||
@REM Fallback to current working directory if not found. |
||||
|
||||
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% |
||||
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir |
||||
|
||||
set EXEC_DIR=%CD% |
||||
set WDIR=%EXEC_DIR% |
||||
:findBaseDir |
||||
IF EXIST "%WDIR%"\.mvn goto baseDirFound |
||||
cd .. |
||||
IF "%WDIR%"=="%CD%" goto baseDirNotFound |
||||
set WDIR=%CD% |
||||
goto findBaseDir |
||||
|
||||
:baseDirFound |
||||
set MAVEN_PROJECTBASEDIR=%WDIR% |
||||
cd "%EXEC_DIR%" |
||||
goto endDetectBaseDir |
||||
|
||||
:baseDirNotFound |
||||
set MAVEN_PROJECTBASEDIR=%EXEC_DIR% |
||||
cd "%EXEC_DIR%" |
||||
|
||||
:endDetectBaseDir |
||||
|
||||
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig |
||||
|
||||
@setlocal EnableExtensions EnableDelayedExpansion |
||||
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a |
||||
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% |
||||
|
||||
:endReadAdditionalConfig |
||||
|
||||
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" |
||||
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" |
||||
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain |
||||
|
||||
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" |
||||
FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( |
||||
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B |
||||
) |
||||
|
||||
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central |
||||
@REM This allows using the maven wrapper in projects that prohibit checking in binary data. |
||||
if exist %WRAPPER_JAR% ( |
||||
echo Found %WRAPPER_JAR% |
||||
) else ( |
||||
echo Couldn't find %WRAPPER_JAR%, downloading it ... |
||||
echo Downloading from: %DOWNLOAD_URL% |
||||
powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" |
||||
echo Finished downloading %WRAPPER_JAR% |
||||
) |
||||
@REM End of extension |
||||
|
||||
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* |
||||
if ERRORLEVEL 1 goto error |
||||
goto end |
||||
|
||||
:error |
||||
set ERROR_CODE=1 |
||||
|
||||
:end |
||||
@endlocal & set ERROR_CODE=%ERROR_CODE% |
||||
|
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost |
||||
@REM check for post script, once with legacy .bat ending and once with .cmd ending |
||||
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" |
||||
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" |
||||
:skipRcPost |
||||
|
||||
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' |
||||
if "%MAVEN_BATCH_PAUSE%" == "on" pause |
||||
|
||||
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% |
||||
|
||||
exit /B %ERROR_CODE% |
@ -0,0 +1,84 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
<modelVersion>4.0.0</modelVersion> |
||||
<parent> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter-parent</artifactId> |
||||
<version>2.1.2.RELEASE</version> |
||||
<relativePath/> <!-- lookup parent from repository --> |
||||
</parent> |
||||
<groupId>com.gitee.sop</groupId> |
||||
<artifactId>sop-book-web</artifactId> |
||||
<version>0.0.1-SNAPSHOT</version> |
||||
<name>sop-story</name> |
||||
<description>Demo project for Spring Boot</description> |
||||
|
||||
<properties> |
||||
<java.version>1.8</java.version> |
||||
<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version> |
||||
</properties> |
||||
|
||||
<dependencies> |
||||
<dependency> |
||||
<groupId>com.gitee.sop</groupId> |
||||
<artifactId>sop-book-api</artifactId> |
||||
<version>1.0-SNAPSHOT</version> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>com.gitee.sop</groupId> |
||||
<artifactId>sop-story-api</artifactId> |
||||
<version>1.0-SNAPSHOT</version> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.springframework.cloud</groupId> |
||||
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.springframework.cloud</groupId> |
||||
<artifactId>spring-cloud-starter-openfeign</artifactId> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter-test</artifactId> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.projectlombok</groupId> |
||||
<artifactId>lombok</artifactId> |
||||
<version>1.18.4</version> |
||||
<scope>compile</scope> |
||||
</dependency> |
||||
</dependencies> |
||||
|
||||
<dependencyManagement> |
||||
<dependencies> |
||||
<dependency> |
||||
<groupId>org.springframework.cloud</groupId> |
||||
<artifactId>spring-cloud-dependencies</artifactId> |
||||
<version>${spring-cloud.version}</version> |
||||
<type>pom</type> |
||||
<scope>import</scope> |
||||
</dependency> |
||||
</dependencies> |
||||
</dependencyManagement> |
||||
|
||||
<build> |
||||
<plugins> |
||||
<plugin> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-maven-plugin</artifactId> |
||||
</plugin> |
||||
</plugins> |
||||
</build> |
||||
|
||||
<repositories> |
||||
<repository> |
||||
<id>spring-milestones</id> |
||||
<name>Spring Milestones</name> |
||||
<url>https://repo.spring.io/milestone</url> |
||||
</repository> |
||||
</repositories> |
||||
|
||||
</project> |
@ -0,0 +1,20 @@ |
||||
package com.gitee.sop.bookweb; |
||||
|
||||
import org.springframework.boot.SpringApplication; |
||||
import org.springframework.boot.autoconfigure.SpringBootApplication; |
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient; |
||||
import org.springframework.cloud.openfeign.EnableFeignClients; |
||||
|
||||
// 允许调用其他服务
|
||||
@EnableFeignClients |
||||
// 服务注册
|
||||
@EnableDiscoveryClient |
||||
@SpringBootApplication |
||||
public class SopBookApplication { |
||||
|
||||
public static void main(String[] args) { |
||||
SpringApplication.run(SopBookApplication.class, args); |
||||
} |
||||
|
||||
} |
||||
|
@ -0,0 +1,14 @@ |
||||
package com.gitee.sop.bookweb.consumer; |
||||
|
||||
import com.gitee.sop.story.api.service.StoryService; |
||||
import org.springframework.cloud.openfeign.FeignClient; |
||||
|
||||
/** |
||||
* 调用story服务 |
||||
* |
||||
* @author tanghc |
||||
*/ |
||||
// value对应的spring.application.name
|
||||
@FeignClient("story-service") |
||||
public interface StoryServiceConsumer extends StoryService { |
||||
} |
@ -0,0 +1,74 @@ |
||||
package com.gitee.sop.bookweb.controller; |
||||
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias; |
||||
import lombok.Data; |
||||
|
||||
/** |
||||
* 默认的结果封装类. |
||||
* <pre> |
||||
* |
||||
* xml返回结果: |
||||
* <response> |
||||
* <code>50</code> |
||||
* <msg>Remote service error</msg> |
||||
* <sub_code>isv.invalid-parameter</sub_code> |
||||
* <sub_msg>非法参数</sub_msg> |
||||
* </response> |
||||
* 成功情况: |
||||
* <response> |
||||
* <code>0</code> |
||||
* <msg>成功消息</msg> |
||||
* <data> |
||||
* ...返回内容 |
||||
* </data> |
||||
* </response> |
||||
* |
||||
* json返回格式: |
||||
* { |
||||
* "code":"50", |
||||
* "msg":"Remote service error", |
||||
* "sub_code":"isv.invalid-parameter", |
||||
* "sub_msg":"非法参数" |
||||
* } |
||||
* 成功情况: |
||||
* { |
||||
* "code":"0", |
||||
* "msg":"成功消息内容。。。", |
||||
* "data":{ |
||||
* ...返回内容 |
||||
* } |
||||
* } |
||||
* </pre> |
||||
* <p> |
||||
* 字段说明: |
||||
* code:网关异常码 <br> |
||||
* msg:网关异常信息 <br> |
||||
* sub_code:业务异常码 <br> |
||||
* sub_msg:业务异常信息 <br> |
||||
* |
||||
* @author tanghc |
||||
*/ |
||||
@XStreamAlias("response") |
||||
@Data |
||||
public class ApiResult { |
||||
|
||||
/** |
||||
* 网关异常信息 |
||||
*/ |
||||
private String msg; |
||||
|
||||
/** |
||||
* 业务异常码 |
||||
*/ |
||||
private String sub_msg; |
||||
|
||||
/** |
||||
* 业务异常信息 |
||||
*/ |
||||
private String sub_code; |
||||
|
||||
/** |
||||
* 返回结果 |
||||
*/ |
||||
private Object data; |
||||
} |
@ -0,0 +1,73 @@ |
||||
package com.gitee.sop.bookweb.controller; |
||||
|
||||
import com.gitee.book.api.domain.Book; |
||||
import com.gitee.sop.bookweb.consumer.StoryServiceConsumer; |
||||
import com.gitee.sop.bookweb.param.BookParam; |
||||
import com.gitee.sop.story.api.domain.Story; |
||||
import com.hhdd.book.api.service.BookService; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.web.bind.annotation.RequestBody; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.RestController; |
||||
|
||||
import java.util.Arrays; |
||||
|
||||
/** |
||||
* book服务 |
||||
* |
||||
* @author tanghc |
||||
*/ |
||||
@RestController |
||||
public class BookController implements BookService { |
||||
|
||||
@Autowired |
||||
StoryServiceConsumer storyServiceConsumer; |
||||
|
||||
@Override |
||||
public Book getBook(int id) { |
||||
Book book = new Book(); |
||||
book.setId(id); |
||||
book.setName("汪汪队"); |
||||
return book; |
||||
} |
||||
|
||||
@RequestMapping("listBookAndStory") |
||||
public Object listBookAndStory(int id) { |
||||
Book book = new Book(); |
||||
book.setId(id); |
||||
book.setName("汪汪队"); |
||||
|
||||
// 调用story服务
|
||||
Story story = storyServiceConsumer.getStory(id); |
||||
|
||||
return Arrays.asList(book, story); |
||||
} |
||||
|
||||
@RequestMapping("getBook2") |
||||
public Object getBookError(int id) { |
||||
if (id == 0) { |
||||
throw new RuntimeException("id不能为空"); |
||||
} |
||||
Book book = new Book(); |
||||
book.setId(id); |
||||
book.setName("汪汪队"); |
||||
return Arrays.asList(book); |
||||
} |
||||
|
||||
|
||||
|
||||
@RequestMapping("getBook3") |
||||
public Object getBook3(@RequestBody BookParam param) { |
||||
if (param.getId() == 0) { |
||||
throw new RuntimeException("id不能为空"); |
||||
} |
||||
Book book = new Book(); |
||||
book.setId(param.getId()); |
||||
book.setName("小马宝莉"); |
||||
|
||||
ApiResult apiResult = new ApiResult(); |
||||
apiResult.setData(book); |
||||
return apiResult; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,11 @@ |
||||
package com.gitee.sop.bookweb.param; |
||||
|
||||
import lombok.Data; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
@Data |
||||
public class BookParam { |
||||
private int id; |
||||
} |
@ -0,0 +1,6 @@ |
||||
server.port=3333 |
||||
spring.application.name=book-service |
||||
eureka.host=localhost |
||||
eureka.port=1111 |
||||
eureka.client.serviceUrl.defaultZone=http://${eureka.host}:${eureka.port}/eureka/ |
||||
|
@ -0,0 +1,17 @@ |
||||
package com.gitee.sop.bookweb; |
||||
|
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
import org.springframework.boot.test.context.SpringBootTest; |
||||
import org.springframework.test.context.junit4.SpringRunner; |
||||
|
||||
@RunWith(SpringRunner.class) |
||||
@SpringBootTest |
||||
public class SopBookApplicationTests { |
||||
|
||||
@Test |
||||
public void contextLoads() { |
||||
} |
||||
|
||||
} |
||||
|
@ -0,0 +1,25 @@ |
||||
/target/ |
||||
!.mvn/wrapper/maven-wrapper.jar |
||||
|
||||
### STS ### |
||||
.apt_generated |
||||
.classpath |
||||
.factorypath |
||||
.project |
||||
.settings |
||||
.springBeans |
||||
.sts4-cache |
||||
|
||||
### IntelliJ IDEA ### |
||||
.idea |
||||
*.iws |
||||
*.iml |
||||
*.ipr |
||||
|
||||
### NetBeans ### |
||||
/nbproject/private/ |
||||
/nbbuild/ |
||||
/dist/ |
||||
/nbdist/ |
||||
/.nb-gradle/ |
||||
/build/ |
@ -0,0 +1,131 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
<modelVersion>4.0.0</modelVersion> |
||||
<parent> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter-parent</artifactId> |
||||
<version>2.1.2.RELEASE</version> |
||||
<relativePath/> <!-- lookup parent from repository --> |
||||
</parent> |
||||
<groupId>com.gitee.sop</groupId> |
||||
<artifactId>sop-gateway-common</artifactId> |
||||
<version>1.0.0-SNAPSHOT</version> |
||||
<name>sop-gateway-common</name> |
||||
<description>sop-gateway-common</description> |
||||
|
||||
<properties> |
||||
<java.version>1.8</java.version> |
||||
<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version> |
||||
<!-- Logging --> |
||||
<logback.version>1.0.13</logback.version> |
||||
<slf4j.version>1.7.5</slf4j.version> |
||||
|
||||
<!-- Test --> |
||||
<junit.version>4.11</junit.version> |
||||
|
||||
<fastjson.version>1.2.15</fastjson.version> |
||||
<commons-io.version>2.5</commons-io.version> |
||||
<commons-fileupload.version>1.3.3</commons-fileupload.version> |
||||
<commons-collection.version>3.2.2</commons-collection.version> |
||||
<xstream.version>1.4.7</xstream.version> |
||||
<velocity.version>1.7</velocity.version> |
||||
<hibernate-validator.version>6.0.10.Final</hibernate-validator.version> |
||||
<oltu.version>0.31</oltu.version> |
||||
<jwt.version>3.2.0</jwt.version> |
||||
<guava.version>18.0</guava.version> |
||||
<spring-data-redis.version>1.8.8.RELEASE</spring-data-redis.version> |
||||
<netty.version>4.1.25.Final</netty.version> |
||||
<jboss-marshalling-serial.version>2.0.5.Final</jboss-marshalling-serial.version> |
||||
<itextpdf.version>5.5.12</itextpdf.version> |
||||
<xmlworker.version>5.5.8</xmlworker.version> |
||||
<itext-asian.version>5.2.0</itext-asian.version> |
||||
<asm.version>6.2</asm.version> |
||||
<commons-codec.version>1.11</commons-codec.version> |
||||
</properties> |
||||
|
||||
<dependencies> |
||||
<dependency> |
||||
<groupId>org.springframework.cloud</groupId> |
||||
<artifactId>spring-cloud-starter-netflix-zuul</artifactId> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter-test</artifactId> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>com.thoughtworks.xstream</groupId> |
||||
<artifactId>xstream</artifactId> |
||||
<version>${xstream.version}</version> |
||||
<scope>compile</scope> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>com.alibaba</groupId> |
||||
<artifactId>fastjson</artifactId> |
||||
<version>${fastjson.version}</version> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter-data-redis</artifactId> |
||||
</dependency> |
||||
<!-- commons --> |
||||
<dependency> |
||||
<groupId>commons-collections</groupId> |
||||
<artifactId>commons-collections</artifactId> |
||||
<version>${commons-collection.version}</version> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>commons-io</groupId> |
||||
<artifactId>commons-io</artifactId> |
||||
<version>${commons-io.version}</version> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>commons-fileupload</groupId> |
||||
<artifactId>commons-fileupload</artifactId> |
||||
<version>${commons-fileupload.version}</version> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>commons-codec</groupId> |
||||
<artifactId>commons-codec</artifactId> |
||||
<version>${commons-codec.version}</version> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.projectlombok</groupId> |
||||
<artifactId>lombok</artifactId> |
||||
<version>1.18.4</version> |
||||
<scope>provided</scope> |
||||
</dependency> |
||||
|
||||
</dependencies> |
||||
|
||||
<dependencyManagement> |
||||
<dependencies> |
||||
<dependency> |
||||
<groupId>org.springframework.cloud</groupId> |
||||
<artifactId>spring-cloud-dependencies</artifactId> |
||||
<version>${spring-cloud.version}</version> |
||||
<type>pom</type> |
||||
<scope>import</scope> |
||||
</dependency> |
||||
</dependencies> |
||||
</dependencyManagement> |
||||
|
||||
<build> |
||||
<plugins> |
||||
<plugin> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-maven-plugin</artifactId> |
||||
</plugin> |
||||
</plugins> |
||||
</build> |
||||
|
||||
<repositories> |
||||
<repository> |
||||
<id>spring-milestones</id> |
||||
<name>Spring Milestones</name> |
||||
<url>https://repo.spring.io/milestone</url> |
||||
</repository> |
||||
</repositories> |
||||
|
||||
</project> |
@ -0,0 +1,3 @@ |
||||
# sop-gateway-common |
||||
|
||||
zuul通用组件,主要封装网关的一些功能。 |
@ -0,0 +1,116 @@ |
||||
package com.gitee.sop.gatewaycommon.bean; |
||||
|
||||
import com.gitee.sop.gatewaycommon.configuration.BaseZuulController; |
||||
import com.gitee.sop.gatewaycommon.param.ApiParamParser; |
||||
import com.gitee.sop.gatewaycommon.param.ParamParser; |
||||
import com.gitee.sop.gatewaycommon.result.ApiResultExecutor; |
||||
import com.gitee.sop.gatewaycommon.result.JsonResultSerializer; |
||||
import com.gitee.sop.gatewaycommon.result.ResultExecutor; |
||||
import com.gitee.sop.gatewaycommon.result.ResultSerializer; |
||||
import com.gitee.sop.gatewaycommon.result.XmlResultSerializer; |
||||
import com.gitee.sop.gatewaycommon.secret.AppSecretManager; |
||||
import com.gitee.sop.gatewaycommon.secret.CacheAppSecretManager; |
||||
import com.gitee.sop.gatewaycommon.session.ApiSessionManager; |
||||
import com.gitee.sop.gatewaycommon.session.SessionManager; |
||||
import com.gitee.sop.gatewaycommon.validate.ApiEncrypter; |
||||
import com.gitee.sop.gatewaycommon.validate.ApiSigner; |
||||
import com.gitee.sop.gatewaycommon.validate.ApiValidator; |
||||
import com.gitee.sop.gatewaycommon.validate.Encrypter; |
||||
import com.gitee.sop.gatewaycommon.validate.Signer; |
||||
import com.gitee.sop.gatewaycommon.validate.Validator; |
||||
import lombok.Data; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
@Data |
||||
public class ApiConfig { |
||||
|
||||
private static ApiConfig instance = new ApiConfig(); |
||||
|
||||
private ApiConfig() { |
||||
} |
||||
|
||||
/** |
||||
* 合并结果处理 |
||||
*/ |
||||
private ResultExecutor resultExecutor = new ApiResultExecutor(); |
||||
|
||||
/** |
||||
* json序列化 |
||||
*/ |
||||
private ResultSerializer jsonResultSerializer = new JsonResultSerializer(); |
||||
/** |
||||
* xml序列化 |
||||
*/ |
||||
private ResultSerializer xmlResultSerializer = new XmlResultSerializer(); |
||||
|
||||
/** |
||||
* app秘钥管理 |
||||
*/ |
||||
private AppSecretManager appSecretManager = new CacheAppSecretManager(); |
||||
|
||||
/** |
||||
* 加密工具 |
||||
*/ |
||||
private Encrypter encrypter = new ApiEncrypter(); |
||||
|
||||
/** |
||||
* 签名工具 |
||||
*/ |
||||
private Signer signer = new ApiSigner(); |
||||
|
||||
/** |
||||
* 参数解析 |
||||
*/ |
||||
private ParamParser paramParser = new ApiParamParser(); |
||||
|
||||
/** |
||||
* 验证 |
||||
*/ |
||||
private Validator validator = new ApiValidator(); |
||||
|
||||
/** |
||||
* session管理 |
||||
*/ |
||||
private SessionManager sessionManager = new ApiSessionManager(); |
||||
|
||||
/** |
||||
* 错误模块 |
||||
*/ |
||||
private List<String> i18nModules = new ArrayList<String>(); |
||||
|
||||
/** |
||||
* 基础Controller |
||||
*/ |
||||
private BaseZuulController baseZuulController = new BaseZuulController(); |
||||
|
||||
|
||||
// -------- fields ---------
|
||||
/** |
||||
* 忽略验证 |
||||
*/ |
||||
private boolean ignoreValidate; |
||||
|
||||
/** |
||||
* 超时时间 |
||||
*/ |
||||
private int timeoutSeconds = 60 * 5; |
||||
|
||||
public void addAppSecret(Map<String, String> appSecretPair) { |
||||
this.appSecretManager.addAppSecret(appSecretPair); |
||||
} |
||||
|
||||
public static ApiConfig getInstance() { |
||||
return instance; |
||||
} |
||||
|
||||
public static void setInstance(ApiConfig apiConfig) { |
||||
instance = apiConfig; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,186 @@ |
||||
package com.gitee.sop.gatewaycommon.bean; |
||||
|
||||
import com.gitee.sop.gatewaycommon.param.UploadContext; |
||||
import com.netflix.zuul.context.RequestContext; |
||||
import com.gitee.sop.gatewaycommon.param.ApiParam; |
||||
import com.gitee.sop.gatewaycommon.session.SessionManager; |
||||
|
||||
import javax.servlet.ServletContext; |
||||
import javax.servlet.http.HttpServletRequest; |
||||
import javax.servlet.http.HttpServletResponse; |
||||
import javax.servlet.http.HttpSession; |
||||
import java.util.Locale; |
||||
|
||||
/** |
||||
* 应用上下文,方便获取信息 |
||||
* |
||||
* @author tanghc |
||||
* |
||||
*/ |
||||
public class ApiContext { |
||||
private static final String ATTR_PARAM = "zuul.common.api.param"; |
||||
private static final String ATTR_UPLOAD_CONTEXT = "zuul.common.api.upload_context"; |
||||
|
||||
private ApiContext(){} |
||||
|
||||
private static void setAttr(String name, Object val) { |
||||
HttpServletRequest request = getRequest(); |
||||
if (request != null) { |
||||
request.setAttribute(name, val); |
||||
} |
||||
} |
||||
|
||||
private static Object getAttr(String name) { |
||||
HttpServletRequest request = getRequest(); |
||||
if (request == null) { |
||||
return null; |
||||
} |
||||
return request.getAttribute(name); |
||||
} |
||||
|
||||
/** |
||||
* 获取随机码 |
||||
* @return 返回随机码 |
||||
*/ |
||||
public static String getRandomKey() { |
||||
HttpSession session = getSession(); |
||||
if (session == null) { |
||||
return null; |
||||
} |
||||
return (String) session.getAttribute(SopConstants.RANDOM_KEY_NAME); |
||||
} |
||||
|
||||
|
||||
|
||||
/** |
||||
* 获取HttpServletRequest |
||||
* |
||||
* @return HttpServletRequest |
||||
*/ |
||||
public static HttpServletRequest getRequest() { |
||||
return RequestContext.getCurrentContext().getRequest(); |
||||
} |
||||
|
||||
/** |
||||
* 返回默认的HttpServletRequest.getSession(); |
||||
* |
||||
* @return 没有返回null |
||||
*/ |
||||
public static HttpSession getSession() { |
||||
HttpServletRequest req = getRequest(); |
||||
if (req == null) { |
||||
return null; |
||||
} else { |
||||
return req.getSession(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 获取session管理器 |
||||
* @return 返回SessionManager |
||||
*/ |
||||
public static SessionManager getSessionManager() { |
||||
return getApiConfig().getSessionManager(); |
||||
} |
||||
|
||||
/** |
||||
* 返回自定义的session,被SessionManager管理 |
||||
* |
||||
* @return 如果sessionId为null,则返回null |
||||
*/ |
||||
public static HttpSession getManagedSession() { |
||||
String sessionId = getSessionId(); |
||||
if (sessionId != null) { |
||||
return getSessionManager().getSession(sessionId); |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 同getSessionId() |
||||
* @return 返回accessToken,没有返回null |
||||
*/ |
||||
public static String getAccessToken() { |
||||
return getSessionId(); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 获取登陆的token |
||||
* |
||||
* @return 没有返回null |
||||
*/ |
||||
public static String getSessionId() { |
||||
ApiParam apiParam = getApiParam(); |
||||
if (apiParam == null) { |
||||
return null; |
||||
} |
||||
return apiParam.fetchAccessToken(); |
||||
} |
||||
|
||||
/** |
||||
* 获取本地化,从HttpServletRequest中获取,没有则返回Locale.SIMPLIFIED_CHINESE |
||||
* |
||||
* @return Locale |
||||
*/ |
||||
public static Locale getLocale() { |
||||
HttpServletRequest req = getRequest(); |
||||
if (req == null) { |
||||
return Locale.SIMPLIFIED_CHINESE; |
||||
} |
||||
return req.getLocale(); |
||||
} |
||||
|
||||
public static void setApiParam(ApiParam apiParam) { |
||||
setAttr(ATTR_PARAM, apiParam); |
||||
} |
||||
|
||||
/** |
||||
* 获取系统参数 |
||||
* |
||||
* @return 返回ApiParam |
||||
*/ |
||||
public static ApiParam getApiParam() { |
||||
return (ApiParam) getAttr(ATTR_PARAM); |
||||
} |
||||
|
||||
public static ApiConfig getApiConfig() { |
||||
return ApiConfig.getInstance(); |
||||
} |
||||
|
||||
public static void setApiConfig(ApiConfig apiConfig) { |
||||
ApiConfig.setInstance(apiConfig); |
||||
} |
||||
|
||||
|
||||
public static ServletContext getServletContext() { |
||||
ServletContext ctx = null; |
||||
HttpSession session = getSession(); |
||||
if (session != null) { |
||||
ctx = session.getServletContext(); |
||||
} |
||||
return ctx; |
||||
} |
||||
|
||||
/** |
||||
* 获取上传文件,如果客户端有文件上传,从这里取。 |
||||
* @return 如果没有文件上传,返回null |
||||
*/ |
||||
public static UploadContext getUploadContext() { |
||||
return (UploadContext) getAttr(ATTR_UPLOAD_CONTEXT); |
||||
} |
||||
|
||||
public static void setUploadContext(UploadContext uploadCtx) { |
||||
setAttr(ATTR_UPLOAD_CONTEXT, uploadCtx); |
||||
} |
||||
|
||||
/** |
||||
* 获取response |
||||
* @return 返回response |
||||
*/ |
||||
public static HttpServletResponse getResponse() { |
||||
return RequestContext.getCurrentContext().getResponse(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,17 @@ |
||||
package com.gitee.sop.gatewaycommon.bean; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
public enum RequestMode { |
||||
|
||||
/** |
||||
* 数字签名请求模式 |
||||
*/ |
||||
SIGNATURE, |
||||
/** |
||||
* 公私钥加密模式,这样请求和返回的数据都经过加密处理 |
||||
*/ |
||||
ENCRYPT |
||||
|
||||
} |
@ -0,0 +1,29 @@ |
||||
package com.gitee.sop.gatewaycommon.bean; |
||||
|
||||
import lombok.Data; |
||||
|
||||
import java.util.List; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
@Data |
||||
public class ServiceApiInfo { |
||||
private String md5; |
||||
private String appName; |
||||
private List<ApiMeta> apis; |
||||
|
||||
@Data |
||||
public static class ApiMeta { |
||||
/** 接口名 */ |
||||
private String name; |
||||
/** 请求path */ |
||||
private String path; |
||||
/** 版本号 */ |
||||
private String version; |
||||
|
||||
public String fetchNameVersion() { |
||||
return name + version; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,33 @@ |
||||
package com.gitee.sop.gatewaycommon.bean; |
||||
|
||||
import java.nio.charset.Charset; |
||||
import java.nio.charset.StandardCharsets; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
public class SopConstants { |
||||
|
||||
public static final String NULL = "null"; |
||||
public static final String RANDOM_KEY_NAME = "ssl_randomKey"; |
||||
public static final Charset CHARSET_UTF8 = StandardCharsets.UTF_8; |
||||
public static final String FORMAT_JSON = "json"; |
||||
public static final String FORMAT_XML = "xml"; |
||||
public static final String AUTHORIZATION = "Authorization"; |
||||
public static final String PREFIX_BEARER = "Bearer "; |
||||
public static final String YMDHMS = "yyyy-MM-dd HH:mm:ss"; |
||||
|
||||
public static final String DEFAULT_SIGN_METHOD = "md5"; |
||||
|
||||
public static final String CONTENT_TYPE_NAME = "Content-Type"; |
||||
public static final String CONTENT_TYPE_JSON = "application/json;charset=UTF-8"; |
||||
|
||||
public static final String LINE = "\n"; |
||||
|
||||
public static final String EMPTY_JSON = "{}"; |
||||
|
||||
public static final String SORT_DESC = "DESC"; |
||||
|
||||
public static final String REST_PARAM_NAME = "_REST_PARAM_NAME_"; |
||||
public static final String REST_PARAM_VERSION = "_REST_PARAM_VERSION_"; |
||||
} |
@ -0,0 +1,16 @@ |
||||
package com.gitee.sop.gatewaycommon.configuration; |
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext; |
||||
import com.gitee.sop.gatewaycommon.validate.alipay.AlipaySigner; |
||||
|
||||
/** |
||||
* 具备支付宝开放平台能力配置 https://docs.open.alipay.com/api
|
||||
* |
||||
* @author tanghc |
||||
*/ |
||||
public class AlipayZuulConfiguration extends BaseZuulConfiguration { |
||||
|
||||
static { |
||||
ApiContext.getApiConfig().setSigner(new AlipaySigner()); |
||||
} |
||||
} |
@ -0,0 +1,112 @@ |
||||
package com.gitee.sop.gatewaycommon.configuration; |
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext; |
||||
import com.gitee.sop.gatewaycommon.filter.ErrorFilter; |
||||
import com.gitee.sop.gatewaycommon.filter.PostResultFilter; |
||||
import com.gitee.sop.gatewaycommon.filter.PreValidateFilter; |
||||
import com.gitee.sop.gatewaycommon.manager.ApiMetaChangeListener; |
||||
import com.gitee.sop.gatewaycommon.manager.ApiMetaContext; |
||||
import com.gitee.sop.gatewaycommon.manager.ApiMetaManager; |
||||
import com.gitee.sop.gatewaycommon.manager.DefaultApiMetaContext; |
||||
import com.gitee.sop.gatewaycommon.manager.DefaultApiMetaManager; |
||||
import com.gitee.sop.gatewaycommon.manager.SopRouteLocator; |
||||
import com.gitee.sop.gatewaycommon.message.ErrorFactory; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.boot.autoconfigure.web.ServerProperties; |
||||
import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper; |
||||
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties; |
||||
import org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.data.redis.core.StringRedisTemplate; |
||||
import org.springframework.data.redis.listener.PatternTopic; |
||||
import org.springframework.data.redis.listener.RedisMessageListenerContainer; |
||||
|
||||
import javax.annotation.PostConstruct; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
public class BaseZuulConfiguration { |
||||
|
||||
public static final String API_CHANGE_CHANNEL = "channel.sop.api.change"; |
||||
|
||||
|
||||
@Autowired |
||||
protected ZuulProperties zuulProperties; |
||||
|
||||
@Autowired |
||||
protected ServerProperties server; |
||||
|
||||
@Autowired |
||||
protected ApiMetaManager apiMetaManager; |
||||
|
||||
@Bean |
||||
public ApiMetaContext apiMetaContext() { |
||||
return new DefaultApiMetaContext(); |
||||
} |
||||
|
||||
@Bean |
||||
public ApiMetaManager apiMetaManager(StringRedisTemplate stringRedisTemplate, ApiMetaContext apiMetaContext) { |
||||
return new DefaultApiMetaManager(stringRedisTemplate, apiMetaContext); |
||||
} |
||||
|
||||
@Bean |
||||
PreValidateFilter preValidateFilter() { |
||||
return new PreValidateFilter(); |
||||
} |
||||
|
||||
@Bean |
||||
public PreDecorationFilter preDecorationFilter(ApiMetaContext apiMetaContext, ProxyRequestHelper proxyRequestHelper) { |
||||
SopRouteLocator routeLocator = new SopRouteLocator(apiMetaContext); |
||||
return new PreDecorationFilter(routeLocator, |
||||
this.server.getServlet().getContextPath(), |
||||
this.zuulProperties, |
||||
proxyRequestHelper); |
||||
} |
||||
|
||||
@Bean |
||||
ErrorFilter errorFilter() { |
||||
return new ErrorFilter(); |
||||
} |
||||
|
||||
@Bean |
||||
PostResultFilter postResultFilter() { |
||||
return new PostResultFilter(); |
||||
} |
||||
|
||||
/** |
||||
* 配置redis事件订阅 |
||||
* |
||||
* @param apiMetaManager |
||||
* @param redisTemplate |
||||
* @return |
||||
*/ |
||||
@Bean |
||||
RedisMessageListenerContainer container(ApiMetaManager apiMetaManager, StringRedisTemplate redisTemplate) { |
||||
RedisMessageListenerContainer container = new RedisMessageListenerContainer(); |
||||
container.setConnectionFactory(redisTemplate.getConnectionFactory()); |
||||
ApiMetaChangeListener apiMetaChangeListener = new ApiMetaChangeListener(apiMetaManager, redisTemplate); |
||||
container.addMessageListener(apiMetaChangeListener, new PatternTopic(API_CHANGE_CHANNEL)); |
||||
return container; |
||||
} |
||||
|
||||
@Bean |
||||
BaseZuulController baseZuulController() { |
||||
return ApiContext.getApiConfig().getBaseZuulController(); |
||||
} |
||||
|
||||
@PostConstruct |
||||
public void after() { |
||||
doAfter(); |
||||
} |
||||
|
||||
protected void doAfter() { |
||||
initMessage(); |
||||
apiMetaManager.refresh(); |
||||
} |
||||
|
||||
protected void initMessage() { |
||||
ErrorFactory.initMessageSource(ApiContext.getApiConfig().getI18nModules()); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,44 @@ |
||||
package com.gitee.sop.gatewaycommon.configuration; |
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext; |
||||
import com.gitee.sop.gatewaycommon.result.ResultExecutor; |
||||
import com.netflix.zuul.context.RequestContext; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springframework.boot.web.servlet.error.ErrorController; |
||||
import org.springframework.stereotype.Controller; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.ResponseBody; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
import javax.servlet.http.HttpServletResponse; |
||||
|
||||
/** |
||||
* 处理网关自身异常 |
||||
* |
||||
* @author tanghc |
||||
*/ |
||||
@Controller |
||||
@Slf4j |
||||
public class BaseZuulController implements ErrorController { |
||||
|
||||
public static final String ERROR_PATH = "/error"; |
||||
|
||||
// 错误最终会到这里来
|
||||
@RequestMapping(ERROR_PATH) |
||||
@ResponseBody |
||||
public Object error(HttpServletRequest request, HttpServletResponse response) { |
||||
RequestContext ctx = RequestContext.getCurrentContext(); |
||||
Throwable throwable = ctx.getThrowable(); |
||||
return this.buildResult(throwable); |
||||
} |
||||
|
||||
protected Object buildResult(Throwable throwable) { |
||||
ResultExecutor resultExecutor = ApiContext.getApiConfig().getResultExecutor(); |
||||
return resultExecutor.mergeError(throwable); |
||||
} |
||||
|
||||
@Override |
||||
public String getErrorPath() { |
||||
return ERROR_PATH; |
||||
} |
||||
} |
@ -0,0 +1,23 @@ |
||||
package com.gitee.sop.gatewaycommon.configuration; |
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext; |
||||
import com.gitee.sop.gatewaycommon.param.ParamNames; |
||||
import com.gitee.sop.gatewaycommon.validate.taobao.TaobaoSigner; |
||||
|
||||
/** |
||||
* 具备淘宝开放平台能力配置 |
||||
* 淘宝开放平台:http://open.taobao.com/doc.htm
|
||||
* @author tanghc |
||||
*/ |
||||
public class TaobaoZuulConfiguration extends BaseZuulConfiguration { |
||||
|
||||
static { |
||||
ParamNames.APP_KEY_NAME = "app_key"; |
||||
ParamNames.SIGN_TYPE_NAME = "sign_method"; |
||||
ParamNames.VERSION_NAME = "v"; |
||||
ParamNames.APP_AUTH_TOKEN_NAME = "session"; |
||||
|
||||
ApiContext.getApiConfig().setSigner(new TaobaoSigner()); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,20 @@ |
||||
package com.gitee.sop.gatewaycommon.easyopen; |
||||
|
||||
import org.springframework.cloud.netflix.zuul.filters.Route; |
||||
|
||||
import java.util.Collections; |
||||
import java.util.Set; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
public class EasyopenRoute extends Route { |
||||
public EasyopenRoute(String id, String path, String location, String prefix, Boolean retryable, Set<String> ignoredHeaders) { |
||||
super(id, path, location, prefix, retryable, ignoredHeaders); |
||||
} |
||||
|
||||
public EasyopenRoute(String id, String location) { |
||||
this(id, "/" + location + "/", location, "", false, Collections.emptySet()); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,74 @@ |
||||
package com.gitee.sop.gatewaycommon.easyopen; |
||||
|
||||
import com.gitee.sop.gatewaycommon.filter.ErrorFilter; |
||||
import com.gitee.sop.gatewaycommon.filter.PostResultFilter; |
||||
import com.gitee.sop.gatewaycommon.easyopen.filter.PostEasyopenResultFilter; |
||||
import com.gitee.sop.gatewaycommon.filter.PreValidateFilter; |
||||
import com.gitee.sop.gatewaycommon.manager.ApiMetaContext; |
||||
import com.gitee.sop.gatewaycommon.manager.ApiMetaManager; |
||||
import com.gitee.sop.gatewaycommon.manager.DefaultApiMetaContext; |
||||
import com.gitee.sop.gatewaycommon.manager.DefaultApiMetaManager; |
||||
import com.gitee.sop.gatewaycommon.manager.SopRouteLocator; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.boot.autoconfigure.web.ServerProperties; |
||||
import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper; |
||||
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties; |
||||
import org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.data.redis.core.StringRedisTemplate; |
||||
|
||||
import javax.annotation.PostConstruct; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
public class EasyopenZuulConfig { |
||||
|
||||
@Autowired |
||||
protected ZuulProperties zuulProperties; |
||||
|
||||
@Autowired |
||||
protected ServerProperties server; |
||||
|
||||
@Autowired |
||||
protected ApiMetaManager apiMetaManager; |
||||
|
||||
@Bean |
||||
public ApiMetaContext apiMetaContext() { |
||||
return new DefaultApiMetaContext(); |
||||
} |
||||
|
||||
@Bean |
||||
public ApiMetaManager apiMetaManager(StringRedisTemplate stringRedisTemplate, ApiMetaContext apiMetaContext) { |
||||
return new DefaultApiMetaManager(stringRedisTemplate, apiMetaContext); |
||||
} |
||||
|
||||
@Bean |
||||
public PreDecorationFilter preDecorationFilter(ApiMetaContext apiMetaContext, ProxyRequestHelper proxyRequestHelper) { |
||||
SopRouteLocator routeLocator = new SopRouteLocator(apiMetaContext); |
||||
return new PreDecorationFilter(routeLocator, |
||||
this.server.getServlet().getContextPath(), |
||||
this.zuulProperties, |
||||
proxyRequestHelper); |
||||
} |
||||
|
||||
@Bean |
||||
PreValidateFilter preValidateFilter() { |
||||
return new PreValidateFilter(); |
||||
} |
||||
|
||||
@Bean |
||||
ErrorFilter errorFilter() { |
||||
return new ErrorFilter(); |
||||
} |
||||
|
||||
@Bean |
||||
PostResultFilter postResultFilter() { |
||||
return new PostEasyopenResultFilter(); |
||||
} |
||||
|
||||
@PostConstruct |
||||
public void after() { |
||||
apiMetaManager.refresh(); |
||||
} |
||||
} |
@ -0,0 +1,46 @@ |
||||
package com.gitee.sop.gatewaycommon.easyopen.filter; |
||||
|
||||
import com.gitee.sop.gatewaycommon.filter.PostResultFilter; |
||||
|
||||
/** |
||||
* 合并微服务结果,统一返回格式 |
||||
* |
||||
* @author tanghc |
||||
*/ |
||||
public class PostEasyopenResultFilter extends PostResultFilter { |
||||
private static final String EASYOPEN_SUCCESS_CODE = "0"; |
||||
//
|
||||
// protected Result processServiceResult(InputStream responseDataStream, ResultBuilder resultBuilder) throws IOException {
|
||||
// String responseBody = IOUtils.toString(responseDataStream, SopConstants.CHARSET_UTF8);
|
||||
// ApiResult result = this.parseApiResult(responseBody);
|
||||
// Result finalResult;
|
||||
// if (EASYOPEN_SUCCESS_CODE.equals(result.getCode())) {
|
||||
// finalResult = resultBuilder.buildSuccessResult(GATEWAY_SUCCESS_CODE, null, result.getData());
|
||||
// } else {
|
||||
// // 业务出错
|
||||
// finalResult = resultBuilder.buildServiceError(result.getCode(), result.getMsg());
|
||||
// }
|
||||
// return finalResult;
|
||||
// }
|
||||
//
|
||||
// protected ApiResult parseApiResult(String responseBody) {
|
||||
// ApiParam apiParam = ApiContext.getApiParam();
|
||||
// String format = apiParam.fetchFormat();
|
||||
// return SopConstants.FORMAT_XML.equalsIgnoreCase(format)
|
||||
// ? XmlUtil.unserialize(responseBody, ApiResult.class)
|
||||
// : JSON.parseObject(responseBody, ApiResult.class);
|
||||
// }
|
||||
//
|
||||
// protected ResultSerializer buildResultSerializer(HttpServletRequest request, ApiConfig apiConfig) {
|
||||
// ApiParam apiParam = ApiContext.getApiParam();
|
||||
// String format = apiParam.fetchFormat();
|
||||
// if (SopConstants.FORMAT_JSON.equalsIgnoreCase(format)) {
|
||||
// return apiConfig.getJsonResultSerializer();
|
||||
// } else if (SopConstants.FORMAT_XML.equalsIgnoreCase(format)) {
|
||||
// // xml格式输出
|
||||
// return apiConfig.getXmlResultSerializer();
|
||||
// } else {
|
||||
// throw ErrorEnum.isv_invalid_format.getErrorMeta().getException();
|
||||
// }
|
||||
// }
|
||||
} |
@ -0,0 +1,26 @@ |
||||
package com.gitee.sop.gatewaycommon.exception; |
||||
|
||||
|
||||
import com.gitee.sop.gatewaycommon.message.Error; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
public class ApiException extends RuntimeException { |
||||
|
||||
private Error error; |
||||
|
||||
public ApiException(Throwable cause, Error error) { |
||||
super(cause); |
||||
this.error = error; |
||||
} |
||||
|
||||
public ApiException(Error error) { |
||||
super(error.toString()); |
||||
this.error = error; |
||||
} |
||||
|
||||
public Error getError() { |
||||
return error; |
||||
} |
||||
} |
@ -0,0 +1,96 @@ |
||||
package com.gitee.sop.gatewaycommon.filter; |
||||
|
||||
import com.alibaba.fastjson.JSON; |
||||
import com.gitee.sop.gatewaycommon.result.ApiResult; |
||||
import com.netflix.zuul.ZuulFilter; |
||||
import com.netflix.zuul.context.RequestContext; |
||||
import com.netflix.zuul.exception.ZuulException; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
public abstract class BaseZuulFilter extends ZuulFilter { |
||||
|
||||
protected Logger log = LoggerFactory.getLogger(getClass()); |
||||
|
||||
private Integer filterOrder; |
||||
|
||||
protected abstract FilterType getFilterType(); |
||||
|
||||
protected abstract int getFilterOrder(); |
||||
|
||||
protected abstract Object doRun(RequestContext requestContext) throws ZuulException; |
||||
|
||||
/** |
||||
* 设置过滤器顺序 |
||||
* |
||||
* @param filterOrder 顺序,值越小优先执行 |
||||
* @return 返回自身对象 |
||||
*/ |
||||
public BaseZuulFilter order(int filterOrder) { |
||||
this.filterOrder = filterOrder; |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public int filterOrder() { |
||||
return filterOrder != null ? filterOrder : this.getFilterOrder(); |
||||
} |
||||
|
||||
@Override |
||||
public String filterType() { |
||||
return this.getFilterType().getType(); |
||||
} |
||||
|
||||
@Override |
||||
public boolean shouldFilter() { |
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public Object run() throws ZuulException { |
||||
return this.doRun(RequestContext.getCurrentContext()); |
||||
} |
||||
|
||||
/** |
||||
* 过滤该请求,不往下级服务去转发请求,到此结束。并填充responseBody |
||||
* |
||||
* @param requestContext |
||||
* @param result |
||||
*/ |
||||
public static void stopRouteAndReturn(RequestContext requestContext, Object result) { |
||||
requestContext.setSendZuulResponse(false); |
||||
requestContext.setResponseBody(JSON.toJSONString(result)); |
||||
} |
||||
|
||||
public static void main(String[] args) { |
||||
System.out.println(JSON.toJSONString(new ApiResult())); |
||||
} |
||||
|
||||
/** |
||||
* to classify a filter by type. Standard types in Zuul are "pre" for pre-routing filtering, |
||||
* "route" for routing to an origin, "post" for post-routing filters, "error" for error handling. |
||||
* We also support a "static" type for static responses see StaticResponseFilter. |
||||
* Any filterType made be created or added and doRun by calling FilterProcessor.runFilters(type) |
||||
*/ |
||||
public enum FilterType { |
||||
PRE("pre"), |
||||
ROUTE("route"), |
||||
POST("post"), |
||||
ERROR("error"), |
||||
STATIC("static"), |
||||
; |
||||
|
||||
FilterType(String type) { |
||||
this.type = type; |
||||
} |
||||
|
||||
private String type; |
||||
|
||||
public String getType() { |
||||
return type; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,59 @@ |
||||
package com.gitee.sop.gatewaycommon.filter; |
||||
|
||||
import com.netflix.zuul.FilterProcessor; |
||||
import com.netflix.zuul.ZuulFilter; |
||||
import com.netflix.zuul.context.RequestContext; |
||||
import com.netflix.zuul.exception.ZuulException; |
||||
import org.springframework.cloud.netflix.zuul.filters.post.SendErrorFilter; |
||||
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
public class ErrorFilter extends SendErrorFilter { |
||||
|
||||
public static final String FAILED_FILTER = "failed.filter"; |
||||
|
||||
private int filterOrder = 10; |
||||
|
||||
public ErrorFilter() { |
||||
initFilterProcessor(); |
||||
} |
||||
|
||||
public void initFilterProcessor() { |
||||
FilterProcessor instance = FilterProcessor.getInstance(); |
||||
if (!(instance instanceof MyFilterProcessor)) { |
||||
FilterProcessor.setProcessor(new MyFilterProcessor()); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public int filterOrder() { |
||||
return filterOrder; |
||||
} |
||||
|
||||
@Override |
||||
public boolean shouldFilter() { |
||||
// 判断:仅处理来自post过滤器引起的异常
|
||||
RequestContext ctx = RequestContext.getCurrentContext(); |
||||
ZuulFilter failedFilter = (ZuulFilter) ctx.get(FAILED_FILTER); |
||||
return failedFilter != null && failedFilter.filterType().equals(FilterConstants.POST_TYPE); |
||||
} |
||||
|
||||
public static class MyFilterProcessor extends FilterProcessor { |
||||
@Override |
||||
public Object processZuulFilter(ZuulFilter filter) throws ZuulException { |
||||
try { |
||||
return super.processZuulFilter(filter); |
||||
} catch (ZuulException e) { |
||||
RequestContext ctx = RequestContext.getCurrentContext(); |
||||
ctx.set(FAILED_FILTER, filter); |
||||
throw e; |
||||
} |
||||
} |
||||
} |
||||
|
||||
public void setFilterOrder(int filterOrder) { |
||||
this.filterOrder = filterOrder; |
||||
} |
||||
} |
@ -0,0 +1,49 @@ |
||||
package com.gitee.sop.gatewaycommon.filter; |
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiConfig; |
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext; |
||||
import com.gitee.sop.gatewaycommon.bean.SopConstants; |
||||
import com.gitee.sop.gatewaycommon.result.ResultExecutor; |
||||
import com.netflix.zuul.context.RequestContext; |
||||
import com.netflix.zuul.exception.ZuulException; |
||||
import org.apache.commons.io.IOUtils; |
||||
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants; |
||||
|
||||
import java.io.InputStream; |
||||
|
||||
/** |
||||
* 合并微服务结果,统一返回格式 |
||||
* |
||||
* @author tanghc |
||||
*/ |
||||
public class PostResultFilter extends BaseZuulFilter { |
||||
|
||||
@Override |
||||
protected FilterType getFilterType() { |
||||
return FilterType.POST; |
||||
} |
||||
|
||||
@Override |
||||
protected int getFilterOrder() { |
||||
return FilterConstants.SEND_RESPONSE_FILTER_ORDER - 1; |
||||
} |
||||
|
||||
@Override |
||||
protected Object doRun(RequestContext requestContext) throws ZuulException { |
||||
int responseStatusCode = requestContext.getResponseStatusCode(); |
||||
InputStream responseDataStream = requestContext.getResponseDataStream(); |
||||
ApiConfig apiConfig = ApiContext.getApiConfig(); |
||||
ResultExecutor resultExecutor = apiConfig.getResultExecutor(); |
||||
String serviceResult; |
||||
try { |
||||
serviceResult = IOUtils.toString(responseDataStream, SopConstants.CHARSET_UTF8); |
||||
} catch (Exception e) { |
||||
log.error("业务方无数据返回", e); |
||||
serviceResult = SopConstants.EMPTY_JSON; |
||||
} |
||||
String finalResult = resultExecutor.mergeResult(responseStatusCode, serviceResult); |
||||
requestContext.setResponseBody(finalResult); |
||||
return null; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,41 @@ |
||||
package com.gitee.sop.gatewaycommon.filter; |
||||
|
||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum; |
||||
import com.netflix.zuul.context.RequestContext; |
||||
import com.netflix.zuul.exception.ZuulException; |
||||
import org.apache.commons.lang.StringUtils; |
||||
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
public class PreTokenFilter extends BaseZuulFilter { |
||||
|
||||
@Override |
||||
protected FilterType getFilterType() { |
||||
return FilterType.PRE; |
||||
} |
||||
|
||||
@Override |
||||
protected int getFilterOrder() { |
||||
return FilterConstants.PRE_DECORATION_FILTER_ORDER + 1; |
||||
} |
||||
|
||||
@Override |
||||
protected Object doRun(RequestContext ctx) throws ZuulException { |
||||
Object serviceId = ctx.get(FilterConstants.SERVICE_ID_KEY); |
||||
log.info("serviceId:{}", serviceId); |
||||
HttpServletRequest request = ctx.getRequest(); |
||||
|
||||
log.info("send {} request to {}", request.getMethod(), request.getRequestURL().toString()); |
||||
|
||||
String accessToken = request.getParameter("access_token"); |
||||
if (StringUtils.isBlank(accessToken)) { |
||||
throw ErrorEnum.AOP_INVALID_APP_AUTH_TOKEN.getErrorMeta().getException(); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,48 @@ |
||||
package com.gitee.sop.gatewaycommon.filter; |
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext; |
||||
import com.gitee.sop.gatewaycommon.bean.ApiConfig; |
||||
import com.gitee.sop.gatewaycommon.exception.ApiException; |
||||
import com.gitee.sop.gatewaycommon.param.ApiParam; |
||||
import com.gitee.sop.gatewaycommon.validate.Validator; |
||||
import com.netflix.zuul.context.RequestContext; |
||||
import com.netflix.zuul.exception.ZuulException; |
||||
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
|
||||
/** |
||||
* 前置校验 |
||||
* @author tanghc |
||||
*/ |
||||
public class PreValidateFilter extends BaseZuulFilter { |
||||
@Override |
||||
protected FilterType getFilterType() { |
||||
return FilterType.PRE; |
||||
} |
||||
|
||||
@Override |
||||
protected int getFilterOrder() { |
||||
// 在org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter前面
|
||||
return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1; |
||||
} |
||||
|
||||
@Override |
||||
protected Object doRun(RequestContext requestContext) throws ZuulException { |
||||
HttpServletRequest request = requestContext.getRequest(); |
||||
ApiConfig apiConfig = ApiContext.getApiConfig(); |
||||
// 解析参数
|
||||
ApiParam param = apiConfig.getParamParser().parse(request); |
||||
ApiContext.setApiParam(param); |
||||
// 验证操作,这里有负责验证签名参数
|
||||
Validator validator = apiConfig.getValidator(); |
||||
try { |
||||
validator.validate(param); |
||||
} catch (ApiException e) { |
||||
log.error("签名验证失败,params:{}", param.toJSONString(), e); |
||||
throw e; |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,25 @@ |
||||
package com.gitee.sop.gatewaycommon.manager; |
||||
|
||||
import org.springframework.data.redis.connection.Message; |
||||
import org.springframework.data.redis.connection.MessageListener; |
||||
import org.springframework.data.redis.core.StringRedisTemplate; |
||||
import org.springframework.data.redis.serializer.RedisSerializer; |
||||
|
||||
public class ApiMetaChangeListener implements MessageListener { |
||||
|
||||
private ApiMetaManager apiMetaManager; |
||||
private StringRedisTemplate redisTemplate; |
||||
|
||||
public ApiMetaChangeListener(ApiMetaManager apiMetaManager, StringRedisTemplate redisTemplate) { |
||||
this.apiMetaManager = apiMetaManager; |
||||
this.redisTemplate = redisTemplate; |
||||
} |
||||
|
||||
@Override |
||||
public void onMessage(Message message, byte[] bytes) { |
||||
RedisSerializer<String> stringSerializer = redisTemplate.getStringSerializer(); |
||||
String msg = stringSerializer.deserialize(message.getBody()); |
||||
this.apiMetaManager.onChange(msg); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,20 @@ |
||||
package com.gitee.sop.gatewaycommon.manager; |
||||
|
||||
import org.springframework.data.redis.core.StringRedisTemplate; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
public class ApiMetaConfig { |
||||
|
||||
private StringRedisTemplate redisTemplate; |
||||
|
||||
public ApiMetaConfig(StringRedisTemplate redisTemplate) { |
||||
this.redisTemplate = redisTemplate; |
||||
} |
||||
|
||||
public void loadApiMetas() { |
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,13 @@ |
||||
package com.gitee.sop.gatewaycommon.manager; |
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ServiceApiInfo; |
||||
import org.springframework.cloud.netflix.zuul.filters.Route; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
public interface ApiMetaContext { |
||||
void reload(String serviceId, ServiceApiInfo serviceApiInfo); |
||||
|
||||
Route getRoute(String nameVersion); |
||||
} |
@ -0,0 +1,22 @@ |
||||
package com.gitee.sop.gatewaycommon.manager; |
||||
|
||||
/** |
||||
* 管理各服务接口信息 |
||||
* @author tanghc |
||||
*/ |
||||
public interface ApiMetaManager { |
||||
|
||||
String API_STORE_KEY = "com.gitee.sop.api"; |
||||
|
||||
/** |
||||
* 刷新素有的微服务接口信息 |
||||
*/ |
||||
void refresh(); |
||||
|
||||
/** |
||||
* 某个服务接口更改时触发 |
||||
* @param serviceApiInfoJson 接口信息 |
||||
*/ |
||||
void onChange(String serviceApiInfoJson); |
||||
|
||||
} |
@ -0,0 +1,62 @@ |
||||
package com.gitee.sop.gatewaycommon.manager; |
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ServiceApiInfo; |
||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springframework.cloud.netflix.zuul.filters.Route; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.Iterator; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
@Slf4j |
||||
public class DefaultApiMetaContext implements ApiMetaContext { |
||||
|
||||
// key:nameVersion
|
||||
private Map<String, Route> nameVersionServiceIdMap = new HashMap<>(128); |
||||
|
||||
// key: serviceId , value: md5
|
||||
private Map<String, String> serviceIdMd5Map = new HashMap<>(16); |
||||
|
||||
@Override |
||||
public void reload(String serviceId, ServiceApiInfo serviceApiInfo) { |
||||
String md5 = serviceIdMd5Map.get(serviceId); |
||||
if (md5 != null && md5.equals(serviceApiInfo.getMd5())) { |
||||
log.info("MD5相同,无需更改本地接口信息,appName:{}, md5:{}", serviceApiInfo.getAppName(), serviceApiInfo.getMd5()); |
||||
return; |
||||
} |
||||
log.info("更新本地接口信息,appName:{}, md5:{}", serviceApiInfo.getAppName(), serviceApiInfo.getMd5()); |
||||
// 移除原来的
|
||||
Iterator<Map.Entry<String, Route>> iterator = nameVersionServiceIdMap.entrySet().iterator(); |
||||
while (iterator.hasNext()) { |
||||
Map.Entry<String, Route> entry = iterator.next(); |
||||
if (entry.getValue().getLocation().equals(serviceId)) { |
||||
iterator.remove(); |
||||
} |
||||
} |
||||
List<ServiceApiInfo.ApiMeta> apis = serviceApiInfo.getApis(); |
||||
for (ServiceApiInfo.ApiMeta apiMeta : apis) { |
||||
Route route = this.buildRoute(serviceId, apiMeta); |
||||
nameVersionServiceIdMap.put(apiMeta.fetchNameVersion(), route); |
||||
} |
||||
serviceIdMd5Map.put(serviceId, serviceApiInfo.getMd5()); |
||||
} |
||||
|
||||
@Override |
||||
public Route getRoute(String nameVersion) { |
||||
Route route = nameVersionServiceIdMap.get(nameVersion); |
||||
if (route == null) { |
||||
throw ErrorEnum.ISV_INVALID_METHOD.getErrorMeta().getException(); |
||||
} |
||||
return route; |
||||
} |
||||
|
||||
protected Route buildRoute(String serviceId, ServiceApiInfo.ApiMeta apiMeta) { |
||||
return new Route(apiMeta.getName(), apiMeta.getPath(), serviceId, null, false, null); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,51 @@ |
||||
package com.gitee.sop.gatewaycommon.manager; |
||||
|
||||
import com.alibaba.fastjson.JSON; |
||||
import com.gitee.sop.gatewaycommon.bean.ServiceApiInfo; |
||||
import lombok.Getter; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springframework.data.redis.core.StringRedisTemplate; |
||||
|
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* 保存在redis中,结构为HSET。格式如下: |
||||
* <pre> |
||||
* com.gitee.sop.api |
||||
* <serviceId>:{ md5:"xxx", apis:[{name:"", version:""}] } |
||||
* </pre> |
||||
* @author tanghc |
||||
*/ |
||||
@Getter |
||||
@Slf4j |
||||
public class DefaultApiMetaManager implements ApiMetaManager { |
||||
|
||||
private StringRedisTemplate redisTemplate; |
||||
private ApiMetaContext apiMetaContext; |
||||
|
||||
public DefaultApiMetaManager(StringRedisTemplate redisTemplate, ApiMetaContext apiMetaContext) { |
||||
this.redisTemplate = redisTemplate; |
||||
this.apiMetaContext = apiMetaContext; |
||||
} |
||||
|
||||
@Override |
||||
public void refresh() { |
||||
log.info("刷新本地接口信息"); |
||||
Map<Object, Object> entries = redisTemplate.opsForHash().entries(API_STORE_KEY); |
||||
for (Map.Entry<Object, Object> entry : entries.entrySet()) { |
||||
log.info("更新微服务接口,appName:{}", entry.getKey()); |
||||
String serviceId = entry.getKey().toString(); |
||||
String serviceApiInfoJson = entry.getValue().toString(); |
||||
ServiceApiInfo serviceApiInfo = JSON.parseObject(serviceApiInfoJson, ServiceApiInfo.class); |
||||
apiMetaContext.reload(serviceId, serviceApiInfo); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void onChange(String serviceApiInfoJson) { |
||||
ServiceApiInfo serviceApiInfo = JSON.parseObject(serviceApiInfoJson, ServiceApiInfo.class); |
||||
log.info("Redis订阅推送接口信息,appName:{}, md5:{}", serviceApiInfo.getAppName(), serviceApiInfo.getMd5()); |
||||
this.apiMetaContext.reload(serviceApiInfo.getAppName(), serviceApiInfo); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,45 @@ |
||||
package com.gitee.sop.gatewaycommon.manager; |
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext; |
||||
import com.gitee.sop.gatewaycommon.param.ApiParam; |
||||
import org.springframework.cloud.netflix.zuul.filters.Route; |
||||
import org.springframework.cloud.netflix.zuul.filters.RouteLocator; |
||||
import org.springframework.core.Ordered; |
||||
|
||||
import java.util.Collection; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
public class SopRouteLocator implements RouteLocator, Ordered { |
||||
|
||||
private ApiMetaContext apiMetaContext; |
||||
|
||||
public SopRouteLocator(ApiMetaContext apiMetaContext) { |
||||
this.apiMetaContext = apiMetaContext; |
||||
} |
||||
|
||||
@Override |
||||
public Collection<String> getIgnoredPaths() { |
||||
return Collections.emptyList(); |
||||
} |
||||
|
||||
@Override |
||||
public List<Route> getRoutes() { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public Route getMatchingRoute(String path) { |
||||
ApiParam param = ApiContext.getApiParam(); |
||||
String nameVersion = param.fetchNameVersion(); |
||||
return apiMetaContext.getRoute(nameVersion); |
||||
} |
||||
|
||||
@Override |
||||
public int getOrder() { |
||||
return 0; |
||||
} |
||||
} |
@ -0,0 +1,48 @@ |
||||
package com.gitee.sop.gatewaycommon.message; |
||||
|
||||
/** |
||||
* 定义错误返回 |
||||
* code(返回码) |
||||
* msg(返回码描述) |
||||
* sub_code(明细返回码) |
||||
* sub_msg(明细返回码描述) |
||||
* 解决方案 |
||||
* @author tanghc |
||||
*/ |
||||
public interface Error { |
||||
/** |
||||
* 获取网关状态码 |
||||
* |
||||
* @return 返回状态码 |
||||
*/ |
||||
String getCode(); |
||||
|
||||
/** |
||||
* 获取网关错误信息 |
||||
* |
||||
* @return 返回错误信息 |
||||
*/ |
||||
String getMsg(); |
||||
|
||||
/** |
||||
* sub_code(明细返回码) |
||||
* @return sub_code(明细返回码) |
||||
*/ |
||||
String getSub_code(); |
||||
|
||||
/** |
||||
* sub_msg(明细返回码描述) |
||||
* @return sub_msg(明细返回码描述) |
||||
*/ |
||||
String getSub_msg(); |
||||
|
||||
/** |
||||
* 解决方案 |
||||
* @return 解决方案 |
||||
*/ |
||||
String getSolution(); |
||||
|
||||
|
||||
|
||||
|
||||
} |
@ -0,0 +1,66 @@ |
||||
package com.gitee.sop.gatewaycommon.message; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
public enum ErrorEnum { |
||||
SUCCESS("10000", ""), |
||||
|
||||
ISP_UNKNOW_ERROR("20000", "isp.unknow-error"), |
||||
AOP_UNKNOW_ERROR("20000", "aop.unknow-error"), |
||||
|
||||
AOP_INVALID_AUTH_TOKEN("20001", "aop.invalid-auth-token"), |
||||
AOP_INVALID_AUTH_OKEN("20001", "aop.invalid-auth-token"), |
||||
AOP_AUTH_TOKEN_TIME_OUT("20001", "aop.auth-token-time-out"), |
||||
AOP_INVALID_APP_AUTH_TOKEN("20001", "aop.invalid-app-auth-token"), |
||||
AOP_INVALID_APP_AUTH_TOKEN_NO_API("20001", "aop.invalid-app-auth-token-no-api"), |
||||
AOP_APP_AUTH_TOKEN_TIME_OUT("20001", "aop.app-auth-token-time-out"), |
||||
AOP_NO_PRODUCT_REG_BY_PARTNER("20001", "aop.no-product-reg-by-partner"), |
||||
|
||||
ISV_MISSING_METHOD("40001", "isv.missing-method"), |
||||
ISV_MISSING_SIGNATURE("40001", "isv.missing-signature"), |
||||
ISV_MISSING_SIGNATURE_TYPE("40001", "isv.missing-signature-type"), |
||||
ISV_MISSING_SIGNATURE_KEY("40001", "isv.missing-signature-key"), |
||||
ISV_MISSING_APP_ID("40001", "isv.missing-app-id"), |
||||
ISV_MISSING_TIMESTAMP("40001", "isv.missing-timestamp"), |
||||
ISV_MISSING_VERSION("40001", "isv.missing-version"), |
||||
ISV_DECRYPTION_ERROR_MISSING_ENCRYPT_TYPE("40001", "isv.decryption-error-missing-encrypt-type"), |
||||
|
||||
ISV_INVALID_PARAMETER("40002", "isv.invalid-parameter"), |
||||
ISV_UPLOAD_FAIL("40002", "isv.upload-fail"), |
||||
ISV_INVALID_FILE_EXTENSION("40002", "isv.invalid-file-extension"), |
||||
ISV_INVALID_FILE_SIZE("40002", "isv.invalid-file-size"), |
||||
ISV_INVALID_METHOD("40002", "isv.invalid-method"), |
||||
ISV_INVALID_FORMAT("40002", "isv.invalid-format"), |
||||
ISV_INVALID_SIGNATURE_TYPE("40002", "isv.invalid-signature-type"), |
||||
ISV_INVALID_SIGNATURE("40002", "isv.invalid-signature"), |
||||
ISV_INVALID_ENCRYPT_TYPE("40002", "isv.invalid-encrypt-type"), |
||||
ISV_INVALID_ENCRYPT("40002", "isv.invalid-encrypt"), |
||||
ISV_INVALID_APP_ID("40002", "isv.invalid-app-id"), |
||||
ISV_INVALID_TIMESTAMP("40002", "isv.invalid-timestamp"), |
||||
ISV_INVALID_CHARSET("40002", "isv.invalid-charset"), |
||||
ISV_INVALID_DIGEST("40002", "isv.invalid-digest"), |
||||
ISV_DECRYPTION_ERROR_NOT_VALID_ENCRYPT_TYPE("40002", "isv.decryption-error-not-valid-encrypt-type"), |
||||
ISV_DECRYPTION_ERROR_NOT_VALID_ENCRYPT_KEY("40002", "isv.decryption-error-not-valid-encrypt-key"), |
||||
ISV_DECRYPTION_ERROR_UNKNOWN("40002", "isv.decryption-error-unknown"), |
||||
ISV_MISSING_SIGNATURE_CONFIG("40002", "isv.missing-signature-config"), |
||||
ISV_NOT_SUPPORT_APP_AUTH("40002", "isv.not-support-app-auth"), |
||||
ISV_SUSPECTED_ATTACK("40002", "isv.suspected-attack"), |
||||
ISV_INVALID_CONTENT_TYPE("40002", "isv.invalid-content-type"), |
||||
|
||||
BIZ_ERROR("40004", ""), |
||||
|
||||
ISV_INSUFFICIENT_ISV_PERMISSIONS("40006", "isv.insufficient-isv-permissions"), |
||||
ISV_INSUFFICIENT_USER_PERMISSIONS("40006", "isv.insufficient-user-permissions"), |
||||
|
||||
; |
||||
private ErrorMeta errorMeta; |
||||
|
||||
ErrorEnum(String code, String sub_code) { |
||||
this.errorMeta = new ErrorMeta("open.error_", code, sub_code); |
||||
} |
||||
|
||||
public ErrorMeta getErrorMeta() { |
||||
return errorMeta; |
||||
} |
||||
} |
@ -0,0 +1,120 @@ |
||||
package com.gitee.sop.gatewaycommon.message; |
||||
|
||||
|
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springframework.context.support.MessageSourceAccessor; |
||||
import org.springframework.context.support.ResourceBundleMessageSource; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.HashSet; |
||||
import java.util.List; |
||||
import java.util.Locale; |
||||
import java.util.Map; |
||||
import java.util.Set; |
||||
|
||||
/** |
||||
* 负责构建错误消息 |
||||
* |
||||
* @author tanghc |
||||
*/ |
||||
@Slf4j |
||||
public class ErrorFactory { |
||||
|
||||
public static final String SYS_ERR = "系统错误"; |
||||
|
||||
private static final String I18N_OPEN_ERROR = "i18n/open/error"; |
||||
public static final String UNDERLINE = "_"; |
||||
|
||||
private static Set<String> noModuleCache = new HashSet<String>(); |
||||
|
||||
private static Map<String, Error> errorCache = new HashMap<>(64); |
||||
|
||||
/** |
||||
* 错误信息的国际化信息 |
||||
*/ |
||||
private static MessageSourceAccessor errorMessageSourceAccessor; |
||||
|
||||
/** |
||||
* 设置国际化资源信息 |
||||
*/ |
||||
public static void initMessageSource(List<String> isvModules) { |
||||
HashSet<String> baseNamesSet = new HashSet<String>(); |
||||
baseNamesSet.add(I18N_OPEN_ERROR); |
||||
|
||||
if (!isvModules.isEmpty()) { |
||||
baseNamesSet.addAll(isvModules); |
||||
} |
||||
|
||||
String[] totalBaseNames = baseNamesSet.toArray(new String[0]); |
||||
|
||||
log.info("加载错误码国际化资源:{}", StringUtils.arrayToCommaDelimitedString(totalBaseNames)); |
||||
ResourceBundleMessageSource bundleMessageSource = new ResourceBundleMessageSource(); |
||||
bundleMessageSource.setBasenames(totalBaseNames); |
||||
MessageSourceAccessor messageSourceAccessor = new MessageSourceAccessor(bundleMessageSource); |
||||
setErrorMessageSourceAccessor(messageSourceAccessor); |
||||
} |
||||
|
||||
/** |
||||
* 通过ErrorMeta,Locale,params构建国际化错误消息 |
||||
* |
||||
* @param errorMeta 错误信息 |
||||
* @param locale 本地化 |
||||
* @param params 参数 |
||||
* @return 如果没有配置国际化消息,则直接返回errorMeta中的信息 |
||||
*/ |
||||
public static Error getError(ErrorMeta errorMeta, Locale locale, Object... params) { |
||||
String key = errorMeta.getModulePrefix() + errorMeta.getCode() + errorMeta.getSubCode() + locale.toString(); |
||||
Error error = errorCache.get(key); |
||||
if (error == null) { |
||||
Assert.notNull(locale, "未设置Locale"); |
||||
String modulePrefix = errorMeta.getModulePrefix(); |
||||
String code = errorMeta.getCode(); |
||||
// open.error_20000=Service is temporarily unavailable
|
||||
String msg = getErrorMessage(modulePrefix + code, locale); |
||||
String subCode = errorMeta.getSubCode(); |
||||
// open.error_20000_isp.unknow-error=Service is temporarily unavailable
|
||||
String subMsg = getErrorMessage(modulePrefix + code + UNDERLINE + subCode, locale, params); |
||||
if (StringUtils.isEmpty(msg)) { |
||||
msg = SYS_ERR; |
||||
} |
||||
if (StringUtils.isEmpty(subMsg)) { |
||||
subMsg = SYS_ERR; |
||||
} |
||||
// solution暂未实现,如果要实现,可以这样配置:
|
||||
// open.error_20000_isp.unknow-error_solution=Service is temporarily unavailable
|
||||
// String solution = getErrorMessage(modulePrefix + code + UNDERLINE + subCode + "_solution", locale, params);
|
||||
error = new ErrorImpl(code, msg, subCode, subMsg, null); |
||||
errorCache.put(key, error); |
||||
} |
||||
return error; |
||||
} |
||||
|
||||
|
||||
public static void setErrorMessageSourceAccessor(MessageSourceAccessor errorMessageSourceAccessor) { |
||||
ErrorFactory.errorMessageSourceAccessor = errorMessageSourceAccessor; |
||||
} |
||||
|
||||
/** |
||||
* 返回本地化信息 |
||||
* |
||||
* @param module 错误模块 |
||||
* @param locale 本地化 |
||||
* @param params 参数 |
||||
* @return 返回信息 |
||||
*/ |
||||
public static String getErrorMessage(String module, Locale locale, Object... params) { |
||||
if (noModuleCache.contains(module)) { |
||||
return null; |
||||
} |
||||
try { |
||||
return errorMessageSourceAccessor.getMessage(module, params, locale); |
||||
} catch (Exception e) { |
||||
noModuleCache.add(module); |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,23 @@ |
||||
package com.gitee.sop.gatewaycommon.message; |
||||
|
||||
import lombok.Data; |
||||
|
||||
@Data |
||||
public class ErrorImpl implements Error { |
||||
private String code; |
||||
private String msg; |
||||
private String sub_code; |
||||
private String sub_msg; |
||||
private String solution; |
||||
|
||||
public ErrorImpl() { |
||||
} |
||||
|
||||
public ErrorImpl(String code, String msg, String sub_code, String sub_msg, String solution) { |
||||
this.code = code; |
||||
this.msg = msg; |
||||
this.sub_code = sub_code; |
||||
this.sub_msg = sub_msg; |
||||
this.solution = solution; |
||||
} |
||||
} |
@ -0,0 +1,47 @@ |
||||
package com.gitee.sop.gatewaycommon.message; |
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext; |
||||
import com.gitee.sop.gatewaycommon.exception.ApiException; |
||||
import lombok.Getter; |
||||
|
||||
/** |
||||
* 错误对象 |
||||
* |
||||
* @author tanghc |
||||
*/ |
||||
@Getter |
||||
public class ErrorMeta { |
||||
|
||||
private String modulePrefix; |
||||
private String code; |
||||
private String subCode; |
||||
|
||||
public ErrorMeta(String modulePrefix, String code, String subCode) { |
||||
this.modulePrefix = modulePrefix; |
||||
this.code = code; |
||||
this.subCode = subCode; |
||||
} |
||||
|
||||
public Error getError() { |
||||
return ErrorFactory.getError(this, ApiContext.getLocale()); |
||||
} |
||||
|
||||
/** |
||||
* 返回网关exception |
||||
* |
||||
* @param params 参数 |
||||
* @return 返回exception |
||||
*/ |
||||
public ApiException getException(Object... params) { |
||||
if (params != null && params.length == 1) { |
||||
Object param = params[0]; |
||||
if (param instanceof Throwable) { |
||||
Error error = ErrorFactory.getError(this, ApiContext.getLocale()); |
||||
return new ApiException((Throwable) param, error); |
||||
} |
||||
} |
||||
Error error = ErrorFactory.getError(this, ApiContext.getLocale(), params); |
||||
return new ApiException(error); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,197 @@ |
||||
package com.gitee.sop.gatewaycommon.param; |
||||
|
||||
import com.alibaba.fastjson.JSONObject; |
||||
import com.gitee.sop.gatewaycommon.bean.SopConstants; |
||||
import com.netflix.zuul.context.RequestContext; |
||||
import org.apache.commons.lang.StringUtils; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* 客户端传来的参数放在这里. |
||||
* |
||||
* @author tanghc |
||||
*/ |
||||
public class ApiParam extends JSONObject implements Param { |
||||
public ApiParam(Map<String, Object> map) { |
||||
super(map); |
||||
} |
||||
|
||||
private boolean ignoreSign; |
||||
private boolean ignoreValidate; |
||||
|
||||
private String restName; |
||||
private String restVersion; |
||||
|
||||
/** |
||||
* 获取sign,并从param中删除 |
||||
* |
||||
* @return 返回sign内容 |
||||
*/ |
||||
public String fetchSignAndRemove() { |
||||
String sign = this.fetchSign(); |
||||
this.remove(ParamNames.SIGN_NAME); |
||||
return sign; |
||||
} |
||||
|
||||
public HttpServletRequest fetchRequest() { |
||||
return RequestContext.getCurrentContext().getRequest(); |
||||
} |
||||
|
||||
/** |
||||
* 是否忽略验证签名 |
||||
* |
||||
* @return 返回true,忽略签名 |
||||
*/ |
||||
public boolean fetchIgnoreSign() { |
||||
return ignoreSign; |
||||
} |
||||
|
||||
public void setIgnoreSign(boolean ignoreSign) { |
||||
this.ignoreSign = ignoreSign; |
||||
} |
||||
|
||||
public boolean fetchIgnoreValidate() { |
||||
return ignoreValidate; |
||||
} |
||||
|
||||
public void setIgnoreValidate(boolean ignoreValidate) { |
||||
this.ignoreValidate = ignoreValidate; |
||||
} |
||||
|
||||
/** |
||||
* 接口名,如:goods.list |
||||
*/ |
||||
@Override |
||||
public String fetchName() { |
||||
String name = getString(ParamNames.API_NAME); |
||||
if (name == null) { |
||||
name = this.restName; |
||||
} |
||||
return name; |
||||
} |
||||
|
||||
public void setName(String name) { |
||||
this.restName = name; |
||||
} |
||||
|
||||
public String fetchNameVersion() { |
||||
return buildNameVersion(this.fetchName(), this.fetchVersion()); |
||||
} |
||||
|
||||
public static String buildNameVersion(String name, String version) { |
||||
if (StringUtils.isEmpty(version)) { |
||||
return name; |
||||
} else { |
||||
return name + version; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 版本号 |
||||
*/ |
||||
@Override |
||||
public String fetchVersion() { |
||||
String version = getString(ParamNames.VERSION_NAME); |
||||
if (version == null) { |
||||
version = this.restVersion; |
||||
} |
||||
return version; |
||||
} |
||||
|
||||
public void setVersion(String version) { |
||||
this.restVersion = version; |
||||
} |
||||
|
||||
/** |
||||
* 接入应用ID |
||||
*/ |
||||
@Override |
||||
public String fetchAppKey() { |
||||
return getString(ParamNames.APP_KEY_NAME); |
||||
} |
||||
|
||||
public void setAppKey(String appKey) { |
||||
put(ParamNames.APP_KEY_NAME, appKey); |
||||
} |
||||
|
||||
/** |
||||
* 参数,urlencode后的 |
||||
*/ |
||||
@Override |
||||
public String fetchData() { |
||||
return getString(ParamNames.BIZ_CONTENT_NAME); |
||||
} |
||||
|
||||
public void setData(String json) { |
||||
put(ParamNames.BIZ_CONTENT_NAME, json); |
||||
} |
||||
|
||||
/** |
||||
* 时间戳,格式为yyyy-MM-dd HH:mm:ss,例如:2015-01-01 12:00:00 |
||||
*/ |
||||
@Override |
||||
public String fetchTimestamp() { |
||||
return getString(ParamNames.TIMESTAMP_NAME); |
||||
} |
||||
|
||||
public void setTimestamp(String timestamp) { |
||||
put(ParamNames.TIMESTAMP_NAME, timestamp); |
||||
} |
||||
|
||||
/** |
||||
* 签名串 |
||||
*/ |
||||
@Override |
||||
public String fetchSign() { |
||||
return getString(ParamNames.SIGN_NAME); |
||||
} |
||||
|
||||
public void setSign(String sign) { |
||||
put(ParamNames.SIGN_NAME, sign); |
||||
} |
||||
|
||||
@Override |
||||
public String fetchFormat() { |
||||
String format = getString(ParamNames.FORMAT_NAME); |
||||
if (format == null || "".equals(format)) { |
||||
return SopConstants.FORMAT_JSON; |
||||
} |
||||
return format; |
||||
} |
||||
|
||||
public void setFormat(String format) { |
||||
put(ParamNames.FORMAT_NAME, format); |
||||
} |
||||
|
||||
@Override |
||||
public String fetchAccessToken() { |
||||
return getString(ParamNames.APP_AUTH_TOKEN_NAME); |
||||
} |
||||
|
||||
@Override |
||||
public String fetchSignMethod() { |
||||
String signMethod = getString(ParamNames.SIGN_TYPE_NAME); |
||||
if (signMethod == null) { |
||||
return SopConstants.DEFAULT_SIGN_METHOD; |
||||
} else { |
||||
return signMethod; |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public String fetchCharset() { |
||||
return getString(ParamNames.CHARSET_NAME); |
||||
} |
||||
|
||||
@Override |
||||
public ApiParam clone() { |
||||
ApiParam param = new ApiParam(this); |
||||
param.ignoreSign = this.ignoreSign; |
||||
param.ignoreValidate = this.ignoreValidate; |
||||
return param; |
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,104 @@ |
||||
package com.gitee.sop.gatewaycommon.param; |
||||
|
||||
import com.alibaba.fastjson.JSON; |
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext; |
||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum; |
||||
import com.gitee.sop.gatewaycommon.util.RequestUtil; |
||||
import com.netflix.zuul.http.HttpServletRequestWrapper; |
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.web.multipart.MultipartFile; |
||||
import org.springframework.web.multipart.MultipartHttpServletRequest; |
||||
import org.springframework.web.multipart.commons.CommonsMultipartResolver; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
import java.util.Set; |
||||
|
||||
/** |
||||
* 参数解析默认实现 |
||||
* |
||||
* @author tanghc |
||||
*/ |
||||
public class ApiParamParser implements ParamParser { |
||||
|
||||
private static final String CONTENT_TYPE_MULTIPART = MediaType.MULTIPART_FORM_DATA_VALUE; |
||||
private static final String CONTENT_TYPE_JSON = MediaType.APPLICATION_JSON_VALUE; |
||||
private static final String CONTENT_TYPE_TEXT = MediaType.TEXT_PLAIN_VALUE; |
||||
|
||||
@Override |
||||
public ApiParam parse(HttpServletRequest request) { |
||||
try { |
||||
Map<String, Object> params = this.getJson(request); |
||||
return new ApiParam(params); |
||||
} catch (Exception e) { |
||||
throw ErrorEnum.ISV_INVALID_PARAMETER.getErrorMeta().getException(); |
||||
} |
||||
} |
||||
|
||||
public Map<String, Object> getJson(HttpServletRequest request) throws Exception { |
||||
// zuul会做一层包装
|
||||
if (request instanceof HttpServletRequestWrapper) { |
||||
HttpServletRequestWrapper req = (HttpServletRequestWrapper) request; |
||||
request = req.getRequest(); |
||||
} |
||||
Map<String, Object> params = null; |
||||
|
||||
if (RequestUtil.isGetRequest(request)) { |
||||
params = RequestUtil.convertRequestParamsToMap(request); |
||||
} else { |
||||
String contectType = request.getContentType(); |
||||
|
||||
if (contectType == null) { |
||||
contectType = ""; |
||||
} |
||||
|
||||
contectType = contectType.toLowerCase(); |
||||
|
||||
// json或者纯文本形式
|
||||
if (contectType.contains(CONTENT_TYPE_JSON) || contectType.contains(CONTENT_TYPE_TEXT)) { |
||||
String txt = RequestUtil.getText(request); |
||||
params = JSON.parseObject(txt); |
||||
} else if (contectType.contains(CONTENT_TYPE_MULTIPART)) { |
||||
// 上传文件形式
|
||||
params = this.parseUploadRequest(request); |
||||
} else { |
||||
params = RequestUtil.convertRequestParamsToMap(request); |
||||
} |
||||
} |
||||
|
||||
return params; |
||||
} |
||||
|
||||
/** |
||||
* 解析文件上传请求 |
||||
* |
||||
* @param request |
||||
* @return 返回json字符串 |
||||
*/ |
||||
protected Map<String, Object> parseUploadRequest(HttpServletRequest request) { |
||||
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver( |
||||
request.getSession().getServletContext()); |
||||
// 检查form中是否有enctype="multipart/form-data"
|
||||
if (multipartResolver.isMultipart(request)) { |
||||
MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request; |
||||
Map<String, MultipartFile> fileMap = multiRequest.getFileMap(); |
||||
Map<String, MultipartFile> finalMap = new HashMap<>(fileMap.size()); |
||||
|
||||
Set<String> keys = fileMap.keySet(); |
||||
for (String name : keys) { |
||||
MultipartFile file = fileMap.get(name); |
||||
if (file.getSize() > 0) { |
||||
finalMap.put(name, file); |
||||
} |
||||
} |
||||
if (finalMap.size() > 0) { |
||||
// 保存上传文件
|
||||
ApiContext.setUploadContext(new ApiUploadContext(finalMap)); |
||||
} |
||||
} |
||||
|
||||
return RequestUtil.convertRequestParamsToMap(request); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,42 @@ |
||||
package com.gitee.sop.gatewaycommon.param; |
||||
|
||||
import org.springframework.web.multipart.MultipartFile; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* 存放上传文件 |
||||
* |
||||
* @author tanghc |
||||
*/ |
||||
public class ApiUploadContext implements UploadContext { |
||||
|
||||
private Map<String, MultipartFile> fileMap; |
||||
private List<MultipartFile> allFile; |
||||
|
||||
public ApiUploadContext(Map<String, MultipartFile> map) { |
||||
if (map == null) { |
||||
map = Collections.emptyMap(); |
||||
} |
||||
this.fileMap = map; |
||||
this.allFile = new ArrayList<>(map.values()); |
||||
} |
||||
|
||||
@Override |
||||
public MultipartFile getFile(int index) { |
||||
return this.allFile.get(index); |
||||
} |
||||
|
||||
@Override |
||||
public MultipartFile getFile(String name) { |
||||
return fileMap.get(name); |
||||
} |
||||
|
||||
@Override |
||||
public List<MultipartFile> getAllFile() { |
||||
return this.allFile; |
||||
} |
||||
} |
@ -0,0 +1,70 @@ |
||||
package com.gitee.sop.gatewaycommon.param; |
||||
|
||||
import java.io.Serializable; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
public interface Param extends Serializable { |
||||
|
||||
/** |
||||
* 获取接口名 |
||||
* @return 返回接口名 |
||||
*/ |
||||
String fetchName(); |
||||
|
||||
/** |
||||
* 获取版本号 |
||||
* @return 返回版本号 |
||||
*/ |
||||
String fetchVersion(); |
||||
|
||||
/** |
||||
* 获取appKey |
||||
* @return 返回appKey |
||||
*/ |
||||
String fetchAppKey(); |
||||
|
||||
/** |
||||
* 获取业务参数 |
||||
* @return 返回业务参数 |
||||
*/ |
||||
String fetchData(); |
||||
|
||||
/** |
||||
* 获取时间戳 |
||||
* @return 返回时间戳 |
||||
*/ |
||||
String fetchTimestamp(); |
||||
|
||||
/** |
||||
* 获取签名串 |
||||
* @return 返回签名串 |
||||
*/ |
||||
String fetchSign(); |
||||
|
||||
/** |
||||
* 获取格式化类型 |
||||
* @return 返回格式化类型 |
||||
*/ |
||||
String fetchFormat(); |
||||
|
||||
/** |
||||
* 获取accessToken |
||||
* @return 返回accessToken |
||||
*/ |
||||
String fetchAccessToken(); |
||||
|
||||
/** |
||||
* 获取签名方式 |
||||
* @return 返回签名方式 |
||||
*/ |
||||
String fetchSignMethod(); |
||||
|
||||
/** |
||||
* 请求使用的编码格式,如utf-8,gbk,gb2312等 |
||||
* @return 请求使用的编码格式,如utf-8,gbk,gb2312等 |
||||
*/ |
||||
String fetchCharset(); |
||||
|
||||
} |
@ -0,0 +1,46 @@ |
||||
package com.gitee.sop.gatewaycommon.param; |
||||
|
||||
/** |
||||
* 请求参数名定义 |
||||
* |
||||
* 参数 类型 是否必填 最大长度 描述 示例值 |
||||
* app_id String 是 32 支付宝分配给开发者的应用ID 2014072300007148 |
||||
* method String 是 128 接口名称 alipay.trade.fastpay.refund.query |
||||
* format String 否 40 仅支持JSON JSON |
||||
* charset String 是 10 请求使用的编码格式,如utf-8,gbk,gb2312等 utf-8 |
||||
* sign_type String 是 10 商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2 RSA2 |
||||
* sign String 是 344 商户请求参数的签名串,详见签名 详见示例 |
||||
* timestamp String 是 19 发送请求的时间,格式"yyyy-MM-dd HH:mm:ss" 2014-07-24 03:07:50 |
||||
* version String 是 3 调用的接口版本,固定为:1.0 1.0 |
||||
* app_auth_token String 否 40 详见应用授权概述 |
||||
* biz_content String 是 请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递,具体参照各产品快速接入文档 |
||||
* |
||||
* @author tanghc |
||||
*/ |
||||
public class ParamNames { |
||||
/** 分配给开发者的应用ID */ |
||||
public static String APP_KEY_NAME = "app_id"; |
||||
/** 接口名称 */ |
||||
public static String API_NAME = "method"; |
||||
/** 仅支持JSON */ |
||||
public static String FORMAT_NAME = "format"; |
||||
/** 请求使用的编码格式 */ |
||||
public static String CHARSET_NAME = "charset"; |
||||
/** 商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2 */ |
||||
public static String SIGN_TYPE_NAME = "sign_type"; |
||||
/** 商户请求参数的签名串 */ |
||||
public static String SIGN_NAME = "sign"; |
||||
/** 发送请求的时间 */ |
||||
public static String TIMESTAMP_NAME = "timestamp"; |
||||
/** 调用的接口版本 */ |
||||
public static String VERSION_NAME = "version"; |
||||
/** OAuth 2.0授权token */ |
||||
public static String APP_AUTH_TOKEN_NAME = "app_auth_token"; |
||||
/** 请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递,具体参照各产品快速接入文档 */ |
||||
public static String BIZ_CONTENT_NAME = "biz_content"; |
||||
|
||||
/** */ |
||||
public static String TIMESTAMP_PATTERN = "yyyy-MM-dd HH:mm:ss"; |
||||
|
||||
|
||||
} |
@ -0,0 +1,19 @@ |
||||
package com.gitee.sop.gatewaycommon.param; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
|
||||
/** |
||||
* 负责解析参数 |
||||
* @author tanghc |
||||
* |
||||
*/ |
||||
public interface ParamParser { |
||||
|
||||
/** |
||||
* 从request提取参数 |
||||
* @param request |
||||
* @return 返回ApiParam |
||||
* @throws Exception |
||||
*/ |
||||
ApiParam parse(HttpServletRequest request); |
||||
} |
@ -0,0 +1,36 @@ |
||||
package com.gitee.sop.gatewaycommon.param; |
||||
|
||||
import org.springframework.web.multipart.MultipartFile; |
||||
|
||||
import java.util.List; |
||||
|
||||
/** |
||||
* 获取上传文件 |
||||
* |
||||
* @author tanghc |
||||
*/ |
||||
public interface UploadContext { |
||||
/** |
||||
* 根据索引获取上传文件,从0开始 |
||||
* |
||||
* @param index |
||||
* @return 返回上传文件信息 |
||||
*/ |
||||
MultipartFile getFile(int index); |
||||
|
||||
/** |
||||
* 根据表单名获取上传文件 |
||||
* |
||||
* @param name |
||||
* 表单名称 |
||||
* @return 返回上传文件信息 |
||||
*/ |
||||
MultipartFile getFile(String name); |
||||
|
||||
/** |
||||
* 获取所有的上传文件 |
||||
* |
||||
* @return 返回所有的上传文件 |
||||
*/ |
||||
List<MultipartFile> getAllFile(); |
||||
} |
@ -0,0 +1,77 @@ |
||||
package com.gitee.sop.gatewaycommon.result; |
||||
|
||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum; |
||||
import com.thoughtworks.xstream.annotations.XStreamAlias; |
||||
import lombok.Data; |
||||
|
||||
/** |
||||
* 默认的结果封装类. |
||||
* <pre> |
||||
* |
||||
* xml返回结果: |
||||
* <response> |
||||
* <code>50</code> |
||||
* <msg>Remote service error</msg> |
||||
* <sub_code>isv.invalid-parameter</sub_code> |
||||
* <sub_msg>非法参数</sub_msg> |
||||
* </response> |
||||
* 成功情况: |
||||
* <response> |
||||
* <code>0</code> |
||||
* <msg>成功消息</msg> |
||||
* <data> |
||||
* ...返回内容 |
||||
* </data> |
||||
* </response> |
||||
* |
||||
* json返回格式: |
||||
* { |
||||
* "code":"50", |
||||
* "msg":"Remote service error", |
||||
* "sub_code":"isv.invalid-parameter", |
||||
* "sub_msg":"非法参数" |
||||
* } |
||||
* 成功情况: |
||||
* { |
||||
* "code":"0", |
||||
* "msg":"成功消息内容。。。", |
||||
* "data":{ |
||||
* ...返回内容 |
||||
* } |
||||
* } |
||||
* </pre> |
||||
* <p> |
||||
* 字段说明: |
||||
* code:网关异常码 <br> |
||||
* msg:网关异常信息 <br> |
||||
* sub_code:业务异常码 <br> |
||||
* sub_msg:业务异常信息 <br> |
||||
* |
||||
* @author tanghc |
||||
*/ |
||||
@XStreamAlias("response") |
||||
@Data |
||||
public class ApiResult implements Result { |
||||
|
||||
/** |
||||
* 网关异常码,范围0~100 成功返回"0" |
||||
*/ |
||||
private String code = ErrorEnum.SUCCESS.getErrorMeta().getCode(); |
||||
|
||||
/** |
||||
* 网关异常信息 |
||||
*/ |
||||
private String msg; |
||||
|
||||
/** |
||||
* 业务异常码 |
||||
*/ |
||||
private String sub_msg; |
||||
|
||||
/** |
||||
* 业务异常信息 |
||||
*/ |
||||
private String sub_code; |
||||
|
||||
private String sign; |
||||
} |
@ -0,0 +1,125 @@ |
||||
package com.gitee.sop.gatewaycommon.result; |
||||
|
||||
import com.alibaba.fastjson.JSON; |
||||
import com.alibaba.fastjson.JSONObject; |
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext; |
||||
import com.gitee.sop.gatewaycommon.exception.ApiException; |
||||
import com.gitee.sop.gatewaycommon.message.Error; |
||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum; |
||||
import com.gitee.sop.gatewaycommon.message.ErrorMeta; |
||||
import com.gitee.sop.gatewaycommon.param.ApiParam; |
||||
import com.gitee.sop.gatewaycommon.param.ParamNames; |
||||
import com.netflix.zuul.exception.ZuulException; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springframework.http.HttpStatus; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
@Slf4j |
||||
public class ApiResultExecutor implements ResultExecutor { |
||||
|
||||
private static final ErrorMeta SUCCESS_META = ErrorEnum.SUCCESS.getErrorMeta(); |
||||
private static final ErrorMeta ISP_UNKNOW_ERROR_META = ErrorEnum.ISP_UNKNOW_ERROR.getErrorMeta(); |
||||
private static final ErrorMeta ISP_BIZ_ERROR = ErrorEnum.BIZ_ERROR.getErrorMeta(); |
||||
|
||||
|
||||
public static final int BIZ_ERROR_STATUS = 4000; |
||||
private static final char DOT = '.'; |
||||
private static final char UNDERLINE = '_'; |
||||
public static final String GATEWAY_CODE_NAME = "code"; |
||||
public static final String GATEWAY_MSG_NAME = "msg"; |
||||
public static final String DATA_SUFFIX = "_response"; |
||||
|
||||
@Override |
||||
public String mergeResult(int responseStatus, String responseData) { |
||||
if (responseStatus == HttpStatus.OK.value() || responseStatus == BIZ_ERROR_STATUS) { |
||||
return mergeSuccess(responseStatus, responseData); |
||||
} else { |
||||
// 微服务端有可能返回500错误
|
||||
// {"path":"/book/getBook3","error":"Internal Server Error","message":"id不能为空","timestamp":"2019-02-13T07:41:00.495+0000","status":500}
|
||||
return mergeError(responseData); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public String mergeError(Throwable throwable) { |
||||
Error error = null; |
||||
if (throwable instanceof ZuulException) { |
||||
ZuulException ex = (ZuulException) throwable; |
||||
Throwable cause = ex.getCause(); |
||||
if (cause instanceof ApiException) { |
||||
ApiException apiException = (ApiException) cause; |
||||
error = apiException.getError(); |
||||
} |
||||
} |
||||
if (error == null) { |
||||
error = ErrorEnum.AOP_UNKNOW_ERROR.getErrorMeta().getError(); |
||||
} |
||||
JSONObject jsonObject = (JSONObject)JSON.toJSON(error); |
||||
return this.merge(jsonObject); |
||||
} |
||||
|
||||
/* |
||||
成功示例 |
||||
{ |
||||
"alipay_trade_fastpay_refund_query_response": { |
||||
"code": "10000", |
||||
"msg": "Success", |
||||
"trade_no": "2014112611001004680073956707", |
||||
"out_trade_no": "20150320010101001", |
||||
"out_request_no": "20150320010101001", |
||||
"refund_reason": "用户退款请求", |
||||
"total_amount": 100.2, |
||||
"refund_amount": 12.33 |
||||
}, |
||||
"sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE" |
||||
} |
||||
*/ |
||||
public String mergeSuccess(int responseStatus, String serviceResult) { |
||||
JSONObject jsonObjectService; |
||||
// 如果是业务出错
|
||||
if (responseStatus == BIZ_ERROR_STATUS) { |
||||
jsonObjectService = JSON.parseObject(serviceResult); |
||||
jsonObjectService.put(GATEWAY_CODE_NAME, ISP_BIZ_ERROR.getCode()); |
||||
jsonObjectService.put(GATEWAY_MSG_NAME, ISP_BIZ_ERROR.getError().getMsg()); |
||||
} else { |
||||
// 200正常返回
|
||||
jsonObjectService = JSON.parseObject(serviceResult); |
||||
jsonObjectService.put(GATEWAY_CODE_NAME, SUCCESS_META.getCode()); |
||||
jsonObjectService.put(GATEWAY_MSG_NAME, SUCCESS_META.getError().getMsg()); |
||||
} |
||||
return this.merge(jsonObjectService); |
||||
} |
||||
|
||||
/* |
||||
异常示例 |
||||
{ |
||||
"alipay_trade_fastpay_refund_query_response": { |
||||
"code": "20000", |
||||
"msg": "Service Currently Unavailable", |
||||
"sub_code": "isp.unknow-error", |
||||
"sub_msg": "系统繁忙" |
||||
}, |
||||
"sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE" |
||||
} |
||||
*/ |
||||
public String mergeError(String serviceResult) { |
||||
JSONObject jsonObjectService = new JSONObject(); |
||||
jsonObjectService.put(GATEWAY_CODE_NAME, ISP_UNKNOW_ERROR_META.getCode()); |
||||
jsonObjectService.put(GATEWAY_MSG_NAME, ISP_UNKNOW_ERROR_META.getError().getMsg()); |
||||
return this.merge(jsonObjectService); |
||||
} |
||||
|
||||
private String merge(JSONObject jsonObjectService) { |
||||
JSONObject ret = new JSONObject(); |
||||
ApiParam apiParam = ApiContext.getApiParam(); |
||||
// 点换成下划线
|
||||
String apiName = apiParam.fetchName().replace(DOT, UNDERLINE); |
||||
ret.put(apiName + DATA_SUFFIX, jsonObjectService); |
||||
ret.put(ParamNames.SIGN_NAME, apiParam.fetchSign()); |
||||
return ret.toJSONString(); |
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,16 @@ |
||||
package com.gitee.sop.gatewaycommon.result; |
||||
|
||||
import com.alibaba.fastjson.JSON; |
||||
|
||||
/** |
||||
* 序列化json |
||||
* @author tanghc |
||||
*/ |
||||
public class JsonResultSerializer implements ResultSerializer { |
||||
|
||||
@Override |
||||
public String serialize(Object obj) { |
||||
return JSON.toJSONString(obj); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,12 @@ |
||||
package com.gitee.sop.gatewaycommon.result; |
||||
|
||||
import java.io.Serializable; |
||||
|
||||
/** |
||||
* 返回结果,后续自定义的返回类需要实现这个接口。 |
||||
* |
||||
* @author tanghc |
||||
*/ |
||||
public interface Result extends Serializable { |
||||
|
||||
} |
@ -0,0 +1,27 @@ |
||||
package com.gitee.sop.gatewaycommon.result; |
||||
|
||||
import com.gitee.sop.gatewaycommon.message.Error; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
public interface ResultBuilder { |
||||
|
||||
|
||||
/** |
||||
* 构建网关错误返回结果 |
||||
* |
||||
* @param throwable 异常 |
||||
* @return 返回最终结果 |
||||
*/ |
||||
Result buildGatewayError(Throwable throwable); |
||||
|
||||
/** |
||||
* 构建网关错误返回结果 |
||||
* |
||||
* @param error error |
||||
* @return 返回最终结果 |
||||
*/ |
||||
Result buildGatewayError(Error error); |
||||
|
||||
} |
@ -0,0 +1,10 @@ |
||||
package com.gitee.sop.gatewaycommon.result; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
public interface ResultExecutor { |
||||
String mergeResult(int responseStatus, String responseData); |
||||
|
||||
String mergeError(Throwable throwable); |
||||
} |
@ -0,0 +1,16 @@ |
||||
package com.gitee.sop.gatewaycommon.result; |
||||
|
||||
/** |
||||
* 对象序列化 |
||||
* @author tanghc |
||||
* |
||||
*/ |
||||
public interface ResultSerializer { |
||||
/** |
||||
* 序列化 |
||||
* |
||||
* @param obj |
||||
* @return 返回序列化后的结果 |
||||
*/ |
||||
String serialize(Object obj); |
||||
} |
@ -0,0 +1,16 @@ |
||||
package com.gitee.sop.gatewaycommon.result; |
||||
|
||||
import com.gitee.sop.gatewaycommon.util.XmlUtil; |
||||
|
||||
/** |
||||
* 序列化成xml |
||||
* @author tanghc |
||||
*/ |
||||
public class XmlResultSerializer implements ResultSerializer { |
||||
|
||||
@Override |
||||
public String serialize(Object obj) { |
||||
return XmlUtil.serialize(obj); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,32 @@ |
||||
package com.gitee.sop.gatewaycommon.secret; |
||||
|
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* 负责秘钥管理 |
||||
* @author tanghc |
||||
*/ |
||||
public interface AppSecretManager { |
||||
|
||||
/** |
||||
* 初始化秘钥数据 |
||||
* @param appSecretStore |
||||
*/ |
||||
void addAppSecret(Map<String, String> appSecretStore); |
||||
|
||||
/** |
||||
* 获取应用程序的密钥 |
||||
* |
||||
* @param appKey |
||||
* @return 返回秘钥 |
||||
*/ |
||||
String getSecret(String appKey); |
||||
|
||||
/** |
||||
* 是否是合法的appKey |
||||
* |
||||
* @param appKey |
||||
* @return 返回appKey |
||||
*/ |
||||
boolean isValidAppKey(String appKey); |
||||
} |
@ -0,0 +1,33 @@ |
||||
package com.gitee.sop.gatewaycommon.secret; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* appkey,secret默认管理,简单放在map中,如果要放在redis中,可以参照此方式实现AppSecretManager,然后在ApiConfig中setAppSecretManager() |
||||
* @author tanghc |
||||
* |
||||
*/ |
||||
public class CacheAppSecretManager implements AppSecretManager { |
||||
|
||||
private Map<String, String> secretMap = new HashMap<String, String>(64); |
||||
|
||||
@Override |
||||
public void addAppSecret(Map<String, String> appSecretStore) { |
||||
secretMap.putAll(appSecretStore); |
||||
} |
||||
|
||||
@Override |
||||
public String getSecret(String appKey) { |
||||
return secretMap.get(appKey); |
||||
} |
||||
|
||||
@Override |
||||
public boolean isValidAppKey(String appKey) { |
||||
if (appKey == null) { |
||||
return false; |
||||
} |
||||
return getSecret(appKey) != null; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,56 @@ |
||||
package com.gitee.sop.gatewaycommon.secret; |
||||
|
||||
import org.springframework.core.io.DefaultResourceLoader; |
||||
import org.springframework.core.io.Resource; |
||||
import org.springframework.core.io.support.PropertiesLoaderUtils; |
||||
|
||||
import java.io.IOException; |
||||
import java.util.Map; |
||||
import java.util.Properties; |
||||
|
||||
/** |
||||
* appkey,secret文件管理,功能同CacheAppSecretManager,这个是将appKey,secret放在属性文件中<br> |
||||
* key为appKey,value为secret |
||||
* @author tanghc |
||||
* |
||||
*/ |
||||
public class FileAppSecretManager implements AppSecretManager { |
||||
|
||||
private String appSecretFile = "appSecret.properties"; |
||||
|
||||
private Properties properties; |
||||
|
||||
@Override |
||||
public void addAppSecret(Map<String, String> appSecretStore) { |
||||
properties.putAll(appSecretStore); |
||||
} |
||||
|
||||
@Override |
||||
public String getSecret(String appKey) { |
||||
if (properties == null) { |
||||
try { |
||||
// 默认加载class根目录的appSecret.properties文件
|
||||
DefaultResourceLoader resourceLoader = new DefaultResourceLoader(); |
||||
Resource resource = resourceLoader.getResource(appSecretFile); |
||||
properties = PropertiesLoaderUtils.loadProperties(resource); |
||||
} catch (IOException e) { |
||||
throw new RuntimeException("在类路径下找不到appSecret.properties的应用密钥的属性文件"); |
||||
} |
||||
} |
||||
|
||||
return properties.getProperty(appKey); |
||||
} |
||||
|
||||
public void setAppSecretFile(String appSecretFile) { |
||||
this.appSecretFile = appSecretFile; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isValidAppKey(String appKey) { |
||||
if(appKey == null){ |
||||
return false; |
||||
} |
||||
return getSecret(appKey) != null; |
||||
} |
||||
} |
||||
|
@ -0,0 +1,242 @@ |
||||
package com.gitee.sop.gatewaycommon.session; |
||||
|
||||
import org.springframework.util.Assert; |
||||
|
||||
import javax.servlet.ServletContext; |
||||
import javax.servlet.http.HttpSession; |
||||
import javax.servlet.http.HttpSessionBindingEvent; |
||||
import javax.servlet.http.HttpSessionBindingListener; |
||||
import javax.servlet.http.HttpSessionContext; |
||||
import java.io.Serializable; |
||||
import java.util.Enumeration; |
||||
import java.util.HashMap; |
||||
import java.util.Iterator; |
||||
import java.util.LinkedHashMap; |
||||
import java.util.Map; |
||||
import java.util.UUID; |
||||
import java.util.Vector; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
@SuppressWarnings("deprecation") |
||||
public class ApiHttpSession implements HttpSession, Serializable { |
||||
private static final long serialVersionUID = 946272038219216222L; |
||||
|
||||
private final String id; |
||||
|
||||
private final long creationTime = System.currentTimeMillis(); |
||||
|
||||
private int maxInactiveInterval; |
||||
|
||||
private long lastAccessedTime = System.currentTimeMillis(); |
||||
|
||||
private final ServletContext servletContext; |
||||
|
||||
private final Map<String, Object> attributes = new LinkedHashMap<String, Object>(); |
||||
|
||||
private boolean invalid = false; |
||||
|
||||
private boolean isNew = true; |
||||
|
||||
/** |
||||
* Create a new ApiHttpSession |
||||
*/ |
||||
public ApiHttpSession() { |
||||
this(null); |
||||
} |
||||
|
||||
/** |
||||
* Create a new ApiHttpSession. |
||||
* |
||||
* @param servletContext |
||||
* the ServletContext that the session runs in |
||||
*/ |
||||
public ApiHttpSession(ServletContext servletContext) { |
||||
this(servletContext, null); |
||||
} |
||||
|
||||
/** |
||||
* Create a new ApiHttpSession. |
||||
* |
||||
* @param servletContext |
||||
* the ServletContext that the session runs in |
||||
* @param id |
||||
* a unique identifier for this session |
||||
*/ |
||||
public ApiHttpSession(ServletContext servletContext, String id) { |
||||
this.servletContext = servletContext; |
||||
this.id = this.buildId(id); |
||||
} |
||||
|
||||
protected String buildId(String id) { |
||||
return (id != null ? id : UUID.randomUUID().toString().replace("-", "").toUpperCase()); |
||||
} |
||||
|
||||
@Override |
||||
public long getCreationTime() { |
||||
return this.creationTime; |
||||
} |
||||
|
||||
@Override |
||||
public String getId() { |
||||
return this.id; |
||||
} |
||||
|
||||
public void access() { |
||||
this.lastAccessedTime = System.currentTimeMillis(); |
||||
this.isNew = false; |
||||
} |
||||
|
||||
@Override |
||||
public long getLastAccessedTime() { |
||||
return this.lastAccessedTime; |
||||
} |
||||
|
||||
@Override |
||||
public ServletContext getServletContext() { |
||||
return this.servletContext; |
||||
} |
||||
|
||||
@Override |
||||
public void setMaxInactiveInterval(int interval) { |
||||
this.maxInactiveInterval = interval; |
||||
} |
||||
|
||||
@Override |
||||
public int getMaxInactiveInterval() { |
||||
return this.maxInactiveInterval; |
||||
} |
||||
|
||||
@Override |
||||
public HttpSessionContext getSessionContext() { |
||||
throw new UnsupportedOperationException("getSessionContext"); |
||||
} |
||||
|
||||
@Override |
||||
public Object getAttribute(String name) { |
||||
Assert.notNull(name, "Attribute name must not be null"); |
||||
return this.attributes.get(name); |
||||
} |
||||
|
||||
@Override |
||||
public Object getValue(String name) { |
||||
return getAttribute(name); |
||||
} |
||||
|
||||
@Override |
||||
public Enumeration<String> getAttributeNames() { |
||||
return new Vector<String>(this.attributes.keySet()).elements(); |
||||
} |
||||
|
||||
@Override |
||||
public String[] getValueNames() { |
||||
return this.attributes.keySet().toArray(new String[this.attributes.size()]); |
||||
} |
||||
|
||||
@Override |
||||
public void setAttribute(String name, Object value) { |
||||
Assert.notNull(name, "Attribute name must not be null"); |
||||
if (value != null) { |
||||
this.attributes.put(name, value); |
||||
if (value instanceof HttpSessionBindingListener) { |
||||
((HttpSessionBindingListener) value).valueBound(new HttpSessionBindingEvent(this, name, value)); |
||||
} |
||||
} else { |
||||
removeAttribute(name); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void putValue(String name, Object value) { |
||||
setAttribute(name, value); |
||||
} |
||||
|
||||
@Override |
||||
public void removeAttribute(String name) { |
||||
Assert.notNull(name, "Attribute name must not be null"); |
||||
Object value = this.attributes.remove(name); |
||||
if (value instanceof HttpSessionBindingListener) { |
||||
((HttpSessionBindingListener) value).valueUnbound(new HttpSessionBindingEvent(this, name, value)); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void removeValue(String name) { |
||||
removeAttribute(name); |
||||
} |
||||
|
||||
/** |
||||
* Clear all of this session's attributes. |
||||
*/ |
||||
public void clearAttributes() { |
||||
for (Iterator<Map.Entry<String, Object>> it = this.attributes.entrySet().iterator(); it.hasNext();) { |
||||
Map.Entry<String, Object> entry = it.next(); |
||||
String name = entry.getKey(); |
||||
Object value = entry.getValue(); |
||||
it.remove(); |
||||
if (value instanceof HttpSessionBindingListener) { |
||||
((HttpSessionBindingListener) value).valueUnbound(new HttpSessionBindingEvent(this, name, value)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void invalidate() { |
||||
this.invalid = true; |
||||
clearAttributes(); |
||||
} |
||||
|
||||
public boolean isInvalid() { |
||||
return this.invalid; |
||||
} |
||||
|
||||
public void setNew(boolean value) { |
||||
this.isNew = value; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isNew() { |
||||
return this.isNew; |
||||
} |
||||
|
||||
/** |
||||
* Serialize the attributes of this session into an object that can be |
||||
* turned into a byte array with standard Java serialization. |
||||
* |
||||
* @return a representation of this session's serialized state |
||||
*/ |
||||
public Serializable serializeState() { |
||||
HashMap<String, Serializable> state = new HashMap<String, Serializable>(); |
||||
for (Iterator<Map.Entry<String, Object>> it = this.attributes.entrySet().iterator(); it.hasNext();) { |
||||
Map.Entry<String, Object> entry = it.next(); |
||||
String name = entry.getKey(); |
||||
Object value = entry.getValue(); |
||||
it.remove(); |
||||
if (value instanceof Serializable) { |
||||
state.put(name, (Serializable) value); |
||||
} else { |
||||
// Not serializable... Servlet containers usually automatically
|
||||
// unbind the attribute in this case.
|
||||
if (value instanceof HttpSessionBindingListener) { |
||||
((HttpSessionBindingListener) value).valueUnbound(new HttpSessionBindingEvent(this, name, value)); |
||||
} |
||||
} |
||||
} |
||||
return state; |
||||
} |
||||
|
||||
/** |
||||
* Deserialize the attributes of this session from a state object created by |
||||
* {@link #serializeState()}. |
||||
* |
||||
* @param state |
||||
* a representation of this session's serialized state |
||||
*/ |
||||
@SuppressWarnings("unchecked") |
||||
public void deserializeState(Serializable state) { |
||||
Assert.isTrue(state instanceof Map, "Serialized state needs to be of type [java.util.Map]"); |
||||
this.attributes.putAll((Map<String, Object>) state); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,30 @@ |
||||
package com.gitee.sop.gatewaycommon.session; |
||||
|
||||
import org.springframework.data.redis.connection.DefaultStringRedisConnection; |
||||
import org.springframework.data.redis.connection.RedisConnection; |
||||
import org.springframework.data.redis.connection.RedisConnectionFactory; |
||||
import org.springframework.data.redis.core.RedisTemplate; |
||||
import org.springframework.data.redis.serializer.RedisSerializer; |
||||
import org.springframework.data.redis.serializer.StringRedisSerializer; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
public class ApiRedisTemplate extends RedisTemplate<String, Object> { |
||||
public ApiRedisTemplate() { |
||||
RedisSerializer<String> stringSerializer = new StringRedisSerializer(); |
||||
setKeySerializer(stringSerializer); |
||||
setHashKeySerializer(stringSerializer); |
||||
} |
||||
|
||||
public ApiRedisTemplate(RedisConnectionFactory connectionFactory) { |
||||
this(); |
||||
setConnectionFactory(connectionFactory); |
||||
afterPropertiesSet(); |
||||
} |
||||
|
||||
@Override |
||||
protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) { |
||||
return new DefaultStringRedisConnection(connection); |
||||
} |
||||
} |
@ -0,0 +1,100 @@ |
||||
package com.gitee.sop.gatewaycommon.session; |
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext; |
||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum; |
||||
import com.google.common.cache.CacheBuilder; |
||||
import com.google.common.cache.CacheLoader; |
||||
import com.google.common.cache.LoadingCache; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
import javax.servlet.ServletContext; |
||||
import javax.servlet.http.HttpSession; |
||||
import java.util.concurrent.TimeUnit; |
||||
|
||||
/** |
||||
* session管理,默认存放的是{@link ApiHttpSession}。采用谷歌guava缓存实现。 |
||||
* |
||||
* @author tanghc |
||||
* |
||||
*/ |
||||
public class ApiSessionManager implements SessionManager { |
||||
private static Logger logger = LoggerFactory.getLogger(ApiSessionManager.class); |
||||
|
||||
private int sessionTimeout = 20; |
||||
|
||||
private LoadingCache<String, HttpSession> cache; |
||||
|
||||
public ApiSessionManager() { |
||||
cache = this.buildCache(); |
||||
} |
||||
|
||||
@Override |
||||
public HttpSession getSession(String sessionId) { |
||||
if(sessionId == null) { |
||||
return this.createSession(sessionId); |
||||
} |
||||
try { |
||||
HttpSession session = cache.get(sessionId); |
||||
return session; |
||||
} catch (Exception e) { |
||||
logger.error(e.getMessage(), e); |
||||
throw ErrorEnum.AOP_UNKNOW_ERROR.getErrorMeta().getException(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 创建一个session |
||||
* |
||||
* @param sessionId 传null将返回一个新session |
||||
* @return 返回session |
||||
*/ |
||||
protected HttpSession createSession(String sessionId) { |
||||
ServletContext servletContext = getServletContext(); |
||||
HttpSession session = this.newSession(sessionId, servletContext); |
||||
session.setMaxInactiveInterval(getSessionTimeout()); |
||||
this.cache.put(session.getId(), session); |
||||
return session; |
||||
} |
||||
|
||||
/** |
||||
* 返回新的session实例 |
||||
* |
||||
* @param sessionId |
||||
* @param servletContext |
||||
* @return 返回session |
||||
*/ |
||||
protected HttpSession newSession(String sessionId, ServletContext servletContext) { |
||||
return new ApiHttpSession(servletContext, sessionId); |
||||
} |
||||
|
||||
protected ServletContext getServletContext() { |
||||
return ApiContext.getServletContext(); |
||||
} |
||||
|
||||
protected LoadingCache<String, HttpSession> buildCache() { |
||||
return CacheBuilder.newBuilder().expireAfterAccess(getSessionTimeout(), TimeUnit.MINUTES) |
||||
.build(new CacheLoader<String, HttpSession>() { |
||||
// 找不到sessionId对应的HttpSession时,进入这个方法
|
||||
// 找不到就新建一个
|
||||
@Override |
||||
public HttpSession load(String sessionId) throws Exception { |
||||
return createSession(sessionId); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
public void setSessionTimeout(int sessionTimeout) { |
||||
this.sessionTimeout = sessionTimeout; |
||||
} |
||||
|
||||
/** |
||||
* 过期时间,分钟,默认20分钟 |
||||
* |
||||
* @return 返回过期时间 |
||||
*/ |
||||
public int getSessionTimeout() { |
||||
return sessionTimeout; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,264 @@ |
||||
package com.gitee.sop.gatewaycommon.session; |
||||
|
||||
import org.springframework.data.redis.core.RedisTemplate; |
||||
import org.springframework.util.Assert; |
||||
|
||||
import javax.servlet.ServletContext; |
||||
import javax.servlet.http.HttpSession; |
||||
import javax.servlet.http.HttpSessionContext; |
||||
import java.io.Serializable; |
||||
import java.util.Collections; |
||||
import java.util.Enumeration; |
||||
import java.util.HashSet; |
||||
import java.util.Set; |
||||
import java.util.UUID; |
||||
import java.util.concurrent.TimeUnit; |
||||
|
||||
/** |
||||
* RedisHttpSession |
||||
* |
||||
* @author tanghc |
||||
*/ |
||||
@SuppressWarnings("deprecation") |
||||
public class RedisHttpSession implements HttpSession, Serializable { |
||||
private static final long serialVersionUID = -8081963657251144855L; |
||||
|
||||
private static final int SEC60 = 60; |
||||
|
||||
private static final String SESSION_ATTR = "session_attr:"; |
||||
private static final String CREATION_TIME = "creationTime"; |
||||
private static final String LAST_ACCESSED_TIME = "lastAccessedTime"; |
||||
private static final String MAX_INACTIVE_INTERVAL = "maxInactiveInterval"; |
||||
|
||||
/** |
||||
* 存入redis的key |
||||
*/ |
||||
private String key; |
||||
|
||||
/** |
||||
* sessionId |
||||
*/ |
||||
private String id; |
||||
|
||||
private ServletContext servletContext; |
||||
|
||||
private RedisTemplate redisTemplate; |
||||
|
||||
private RedisHttpSession() { |
||||
} |
||||
|
||||
|
||||
protected static String buildId(String id) { |
||||
return (id != null ? id : UUID.randomUUID().toString().replace("-", "").toUpperCase()); |
||||
} |
||||
|
||||
public static String buildKey(String keyPrefix, String sessionId) { |
||||
Assert.notNull(keyPrefix, "sessionPrefix不能为null"); |
||||
return keyPrefix + sessionId; |
||||
} |
||||
|
||||
/** |
||||
* 创建新的session |
||||
* |
||||
* @param servletContext |
||||
* @param sessionId |
||||
* @param sessionTimeout 过期时间,单位秒 |
||||
* @param redisTemplate redis客户端 |
||||
* @param keyPrefix 存入的key前缀 |
||||
* @return 返回session |
||||
*/ |
||||
public static RedisHttpSession createNewSession(ServletContext servletContext, String sessionId, int sessionTimeout, RedisTemplate redisTemplate, String keyPrefix) { |
||||
Assert.notNull(redisTemplate, "redisTemplate can not null."); |
||||
Assert.notNull(sessionId, "sessionId can not null."); |
||||
Assert.notNull(keyPrefix, "keyPrefix can not null."); |
||||
RedisHttpSession redisHttpSession = new RedisHttpSession(); |
||||
redisHttpSession.setId(sessionId); |
||||
redisHttpSession.setKey(buildKey(keyPrefix, sessionId)); |
||||
redisHttpSession.setRedisTemplate(redisTemplate); |
||||
redisHttpSession.setServletContext(servletContext); |
||||
|
||||
long creationTime = System.currentTimeMillis(); |
||||
// 过期时间,分转换成秒
|
||||
int maxInactiveInterval = sessionTimeout * SEC60; |
||||
|
||||
redisHttpSession.setCreationTime(creationTime); |
||||
redisHttpSession.setLastAccessedTime(creationTime); |
||||
redisHttpSession.setMaxInactiveInterval(maxInactiveInterval); |
||||
|
||||
redisHttpSession.refresh(); |
||||
|
||||
return redisHttpSession; |
||||
} |
||||
|
||||
/** |
||||
* 创建已经存在的session,数据在redis里面 |
||||
* |
||||
* @param sessionId |
||||
* @param servletContext |
||||
* @param redisTemplate redis客户端 |
||||
* @param keyPrefix 存入的key前缀 |
||||
* @return 返回session |
||||
*/ |
||||
public static RedisHttpSession createExistSession(String sessionId, ServletContext servletContext, RedisTemplate redisTemplate, String keyPrefix) { |
||||
Assert.notNull(redisTemplate, "redisTemplate can not null."); |
||||
Assert.notNull(sessionId, "sessionId can not null."); |
||||
Assert.notNull(keyPrefix, "keyPrefix can not null."); |
||||
RedisHttpSession redisHttpSession = new RedisHttpSession(); |
||||
redisHttpSession.setId(sessionId); |
||||
redisHttpSession.setKey(buildKey(keyPrefix, sessionId)); |
||||
redisHttpSession.setRedisTemplate(redisTemplate); |
||||
redisHttpSession.setServletContext(servletContext); |
||||
|
||||
redisHttpSession.refresh(); |
||||
|
||||
return redisHttpSession; |
||||
} |
||||
|
||||
public void setId(String id) { |
||||
this.id = id; |
||||
} |
||||
|
||||
|
||||
public void setServletContext(ServletContext servletContext) { |
||||
this.servletContext = servletContext; |
||||
} |
||||
|
||||
public void setCreationTime(long creationTime) { |
||||
this.redisTemplate.opsForHash().put(key, CREATION_TIME, String.valueOf(creationTime)); |
||||
} |
||||
|
||||
@Override |
||||
public long getCreationTime() { |
||||
Object createTime = this.redisTemplate.opsForHash().get(key, CREATION_TIME); |
||||
return Long.valueOf(String.valueOf(createTime)); |
||||
} |
||||
|
||||
@Override |
||||
public String getId() { |
||||
return id; |
||||
} |
||||
|
||||
@Override |
||||
public long getLastAccessedTime() { |
||||
Object lastAccessedTime = this.redisTemplate.opsForHash().get(key, LAST_ACCESSED_TIME); |
||||
return Long.valueOf(String.valueOf(lastAccessedTime)); |
||||
} |
||||
|
||||
@Override |
||||
public ServletContext getServletContext() { |
||||
return servletContext; |
||||
} |
||||
|
||||
@Override |
||||
public void setMaxInactiveInterval(int interval) { |
||||
this.redisTemplate.opsForHash().put(key, MAX_INACTIVE_INTERVAL, String.valueOf(interval)); |
||||
} |
||||
|
||||
@Override |
||||
public int getMaxInactiveInterval() { |
||||
Object maxInactiveInterval = this.redisTemplate.opsForHash().get(key, MAX_INACTIVE_INTERVAL); |
||||
return Integer.valueOf(String.valueOf(maxInactiveInterval)); |
||||
} |
||||
|
||||
@Override |
||||
public HttpSessionContext getSessionContext() { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public Object getAttribute(String name) { |
||||
return this.redisTemplate.opsForHash().get(key, SESSION_ATTR + name); |
||||
} |
||||
|
||||
@Override |
||||
public Object getValue(String name) { |
||||
return getAttribute(name); |
||||
} |
||||
|
||||
@Override |
||||
public Enumeration<String> getAttributeNames() { |
||||
return Collections.enumeration(getAttributeKeys()); |
||||
} |
||||
|
||||
private Set<String> getAttributeKeys() { |
||||
Set keys = this.redisTemplate.opsForHash().keys(key); |
||||
Set<String> attrNames = new HashSet<>(); |
||||
for (Object key : keys) { |
||||
String k = String.valueOf(key); |
||||
if (k.startsWith(SESSION_ATTR)) { |
||||
attrNames.add(k.substring(SESSION_ATTR.length())); |
||||
} |
||||
} |
||||
return attrNames; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public String[] getValueNames() { |
||||
return getAttributeKeys().toArray(new String[0]); |
||||
} |
||||
|
||||
@Override |
||||
public void setAttribute(String name, Object value) { |
||||
this.redisTemplate.opsForHash().put(key, SESSION_ATTR + name, value); |
||||
} |
||||
|
||||
@Override |
||||
public void putValue(String name, Object value) { |
||||
setAttribute(name, value); |
||||
} |
||||
|
||||
@Override |
||||
public void removeAttribute(String name) { |
||||
this.redisTemplate.opsForHash().delete(key, name); |
||||
} |
||||
|
||||
@Override |
||||
public void removeValue(String name) { |
||||
removeAttribute(name); |
||||
} |
||||
|
||||
@Override |
||||
public void invalidate() { |
||||
this.redisTemplate.delete(key); |
||||
} |
||||
|
||||
@Override |
||||
public boolean isNew() { |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* update expireTime,accessTime |
||||
*/ |
||||
public void refresh() { |
||||
// token更新过期时间
|
||||
this.redisTemplate.expire(key, getMaxInactiveInterval(), TimeUnit.SECONDS); |
||||
// 设置访问时间
|
||||
this.setLastAccessedTime(System.currentTimeMillis()); |
||||
} |
||||
|
||||
|
||||
public void setLastAccessedTime(long lastAccessedTime) { |
||||
this.redisTemplate.opsForHash().put(key, LAST_ACCESSED_TIME, String.valueOf(lastAccessedTime)); |
||||
} |
||||
|
||||
public String getKey() { |
||||
return key; |
||||
} |
||||
|
||||
public void setKey(String key) { |
||||
this.key = key; |
||||
} |
||||
|
||||
public boolean isInvalidated() { |
||||
return !this.redisTemplate.hasKey(key); |
||||
} |
||||
|
||||
|
||||
public void setRedisTemplate(RedisTemplate redisTemplate) { |
||||
this.redisTemplate = redisTemplate; |
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,100 @@ |
||||
package com.gitee.sop.gatewaycommon.session; |
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext; |
||||
import org.springframework.data.redis.core.RedisTemplate; |
||||
import org.springframework.util.Assert; |
||||
|
||||
import javax.servlet.ServletContext; |
||||
import javax.servlet.http.HttpSession; |
||||
import java.util.UUID; |
||||
|
||||
/** |
||||
* SessionManager的redis实现,使用redis管理session |
||||
* |
||||
* @author tanghc |
||||
* |
||||
*/ |
||||
public class RedisSessionManager implements SessionManager { |
||||
|
||||
private ApiRedisTemplate redisTemplate; |
||||
|
||||
/** 过期时间,30分钟 */ |
||||
private int sessionTimeout = 30; |
||||
/** 存入redis中key的前缀 */ |
||||
private String keyPrefix = "session:"; |
||||
|
||||
public RedisSessionManager(@SuppressWarnings("rawtypes") RedisTemplate redisTemplate) { |
||||
Assert.notNull(redisTemplate, "RedisSessionManager中的redisTemplate不能为null"); |
||||
this.redisTemplate = new ApiRedisTemplate(redisTemplate.getConnectionFactory()); |
||||
} |
||||
|
||||
@Override |
||||
public HttpSession getSession(String sessionId) { |
||||
return this.getSession(sessionId, this.keyPrefix); |
||||
} |
||||
|
||||
public HttpSession getSession(String sessionId, String keyPrefix) { |
||||
if (this.hasKey(sessionId)) { |
||||
return RedisHttpSession.createExistSession(sessionId, getServletContext(), redisTemplate, keyPrefix); |
||||
} else { |
||||
sessionId = this.buildSessionId(sessionId); |
||||
return RedisHttpSession.createNewSession(getServletContext(), sessionId, this.getSessionTimeout(), |
||||
redisTemplate, keyPrefix); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 构建sessionId |
||||
* @param id |
||||
* @return 返回sessionid |
||||
*/ |
||||
public String buildSessionId(String id) { |
||||
return (id != null ? id : UUID.randomUUID().toString().replace("-", "").toUpperCase()); |
||||
} |
||||
|
||||
public boolean hasKey(String sessionId) { |
||||
if (sessionId == null) { |
||||
return false; |
||||
} else { |
||||
String key = RedisHttpSession.buildKey(this.keyPrefix, sessionId); |
||||
return redisTemplate.hasKey(key); |
||||
} |
||||
} |
||||
|
||||
public ServletContext getServletContext() { |
||||
return ApiContext.getServletContext(); |
||||
} |
||||
|
||||
public int getSessionTimeout() { |
||||
return sessionTimeout; |
||||
} |
||||
|
||||
/** |
||||
* 设置session过期时间,单位分钟 |
||||
* @param sessionTimeout |
||||
*/ |
||||
public void setSessionTimeout(int sessionTimeout) { |
||||
this.sessionTimeout = sessionTimeout; |
||||
} |
||||
|
||||
public ApiRedisTemplate getRedisTemplate() { |
||||
return redisTemplate; |
||||
} |
||||
|
||||
public void setRedisTemplate(ApiRedisTemplate redisTemplate) { |
||||
this.redisTemplate = redisTemplate; |
||||
} |
||||
|
||||
public String getKeyPrefix() { |
||||
return keyPrefix; |
||||
} |
||||
|
||||
/** |
||||
* 设置存入redis中key的前缀,默认为"session:" |
||||
* @param keyPrefix |
||||
*/ |
||||
public void setKeyPrefix(String keyPrefix) { |
||||
this.keyPrefix = keyPrefix; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,20 @@ |
||||
package com.gitee.sop.gatewaycommon.session; |
||||
|
||||
import javax.servlet.http.HttpSession; |
||||
|
||||
/** |
||||
* session管理 |
||||
* |
||||
* @author tanghc |
||||
* |
||||
*/ |
||||
public interface SessionManager { |
||||
|
||||
/** |
||||
* 根据sessionId获取session |
||||
* |
||||
* @param sessionId 客户端传过来的sessionId,为null时创建一个新session |
||||
* @return 返回session |
||||
*/ |
||||
HttpSession getSession(String sessionId); |
||||
} |
@ -0,0 +1,146 @@ |
||||
package com.gitee.sop.gatewaycommon.util; |
||||
|
||||
import org.apache.commons.codec.binary.Base64; |
||||
import org.apache.commons.codec.binary.Hex; |
||||
|
||||
import javax.crypto.Cipher; |
||||
import javax.crypto.SecretKey; |
||||
import javax.crypto.spec.SecretKeySpec; |
||||
|
||||
/** |
||||
* AES-128 ECB加密.<br> |
||||
* |
||||
* <pre> |
||||
* 字符集:UTF-8 |
||||
* 算法模式:ECB |
||||
* 数据块:128位 |
||||
* 补码方式:PKCS5Padding |
||||
* 加密结果编码方式:Base64 |
||||
* </pre> |
||||
* |
||||
* @author tanghc |
||||
* |
||||
*/ |
||||
public class AESUtil { |
||||
private static final String UTF8 = "UTF-8"; |
||||
private static final String ALGORITHM = "AES"; |
||||
/** 默认的加密算法 */ |
||||
private static final String ALGORITHM_CIPHER = "AES/ECB/PKCS5Padding"; |
||||
|
||||
private static final int LIMIT_LEN = 16; |
||||
|
||||
/** |
||||
* 生成一个SecretKey |
||||
* @param password 长度必须小于等于16 |
||||
* @return 返回SecretKey |
||||
*/ |
||||
public static SecretKey getSecretKey(String password) { |
||||
byte[] passwordData = password.getBytes(); |
||||
if(passwordData.length > LIMIT_LEN) { |
||||
throw new IllegalArgumentException("password 长度必须小于等于16"); |
||||
} |
||||
// 创建一个空的16位字节数组(默认值为0),16byte(128bit)
|
||||
byte[] keyData = new byte[16]; |
||||
System.arraycopy(passwordData, 0, keyData, 0, passwordData.length); |
||||
|
||||
return new SecretKeySpec(keyData, ALGORITHM); |
||||
} |
||||
|
||||
/** |
||||
* 加密 |
||||
* @param data 待加密数据 |
||||
* @param password 密码 |
||||
* @return 返回加密成功后数据 |
||||
* @throws Exception |
||||
*/ |
||||
public static byte[] encrypt(byte[] data, String password) throws Exception { |
||||
SecretKey secretKey = getSecretKey(password); |
||||
// Ciphr完成加密或解密工作类
|
||||
Cipher cipher = Cipher.getInstance(ALGORITHM_CIPHER); |
||||
// 对Cipher初始化,解密模式
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey); |
||||
// 加密data
|
||||
return cipher.doFinal(data); |
||||
} |
||||
|
||||
/** |
||||
* 解密 |
||||
* @param data 待解密数据 |
||||
* @param password 密码 |
||||
* @return 返回解密后的数据 |
||||
* @throws Exception |
||||
*/ |
||||
public static byte[] decrypt(byte[] data, String password) throws Exception { |
||||
SecretKey secretKey = getSecretKey(password); |
||||
// Cipher完成加密或解密工作类
|
||||
Cipher cipher = Cipher.getInstance(ALGORITHM_CIPHER); |
||||
// 对Cipher初始化,解密模式
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKey); |
||||
// 解密data
|
||||
return cipher.doFinal(data); |
||||
} |
||||
|
||||
/** |
||||
* 文本加密 |
||||
* @param content 明文 |
||||
* @param password 密码 |
||||
* @return 返回base64内容 |
||||
* @throws Exception |
||||
*/ |
||||
public static String encryptToBase64String(String content, String password) throws Exception { |
||||
byte[] data = content.getBytes(UTF8); |
||||
byte[] result = encrypt(data, password); |
||||
return Base64.encodeBase64String(result); |
||||
} |
||||
|
||||
/** |
||||
* 文本解密 |
||||
* @param base64String 待解密文本 |
||||
* @param password 密码 |
||||
* @return 返回明文 |
||||
* @throws Exception |
||||
*/ |
||||
public static String decryptFromBase64String(String base64String, String password) throws Exception { |
||||
byte[] data = Base64.decodeBase64(base64String); |
||||
byte[] contentData = decrypt(data, password); |
||||
return new String(contentData, UTF8); |
||||
} |
||||
|
||||
/** |
||||
* 文本加密 |
||||
* @param content 明文 |
||||
* @param password 密码 |
||||
* @return 返回16进制内容 |
||||
* @throws Exception |
||||
*/ |
||||
public static String encryptToHex(String content, String password) throws Exception { |
||||
byte[] data = content.getBytes(UTF8); |
||||
byte[] result = encrypt(data, password); |
||||
return Hex.encodeHexString(result); |
||||
} |
||||
|
||||
/** |
||||
* 文本解密 |
||||
* @param hex 待解密文本 |
||||
* @param password 密码 |
||||
* @return 返回明文 |
||||
* @throws Exception |
||||
*/ |
||||
public static String decryptFromHex(String hex, String password) throws Exception { |
||||
byte[] data = Hex.decodeHex(hex); |
||||
byte[] contentData = decrypt(data, password); |
||||
return new String(contentData,UTF8); |
||||
} |
||||
|
||||
/*public static void main(String[] args) throws Exception { |
||||
String content = "我爱你"; |
||||
String password = "1234567890123456"; |
||||
System.out.println("password:" + password); |
||||
|
||||
String ret2 = encryptToBase64String(content, password); |
||||
System.out.println("密文:" + ret2); |
||||
String content3 = decryptFromBase64String(ret2, password); |
||||
System.out.println(content.equals(content3)); |
||||
}*/ |
||||
|
||||
} |
@ -0,0 +1,25 @@ |
||||
package com.gitee.sop.gatewaycommon.util; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
public class KeyStore { |
||||
private String publicKey; |
||||
private String privateKey; |
||||
|
||||
public String getPublicKey() { |
||||
return publicKey; |
||||
} |
||||
|
||||
public void setPublicKey(String publicKey) { |
||||
this.publicKey = publicKey; |
||||
} |
||||
|
||||
public String getPrivateKey() { |
||||
return privateKey; |
||||
} |
||||
|
||||
public void setPrivateKey(String privateKey) { |
||||
this.privateKey = privateKey; |
||||
} |
||||
} |
@ -0,0 +1,208 @@ |
||||
package com.gitee.sop.gatewaycommon.util; |
||||
|
||||
import org.apache.commons.codec.binary.Base64; |
||||
|
||||
import javax.crypto.Cipher; |
||||
import java.security.KeyFactory; |
||||
import java.security.KeyPair; |
||||
import java.security.KeyPairGenerator; |
||||
import java.security.interfaces.RSAPrivateKey; |
||||
import java.security.interfaces.RSAPublicKey; |
||||
import java.security.spec.PKCS8EncodedKeySpec; |
||||
import java.security.spec.X509EncodedKeySpec; |
||||
|
||||
/** |
||||
* RSA加解密工具<br> |
||||
* @author tanghc |
||||
*/ |
||||
public class RSANewUtil { |
||||
public static final String RSA_ALGORITHM = "RSA"; |
||||
public static final String UTF8 = "UTF-8"; |
||||
|
||||
/** |
||||
* 创建公钥私钥 |
||||
* |
||||
* @return 返回公私钥对 |
||||
* @throws Exception |
||||
*/ |
||||
public static KeyStore createKeys() throws Exception { |
||||
KeyPairGenerator keyPairGeno = KeyPairGenerator.getInstance(RSA_ALGORITHM); |
||||
keyPairGeno.initialize(1024); |
||||
KeyPair keyPair = keyPairGeno.generateKeyPair(); |
||||
|
||||
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); |
||||
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); |
||||
|
||||
KeyStore keyStore = new KeyStore(); |
||||
keyStore.setPublicKey(Base64.encodeBase64String(publicKey.getEncoded())); |
||||
keyStore.setPrivateKey(Base64.encodeBase64String(privateKey.getEncoded())); |
||||
return keyStore; |
||||
} |
||||
|
||||
/** |
||||
* 获取公钥对象 |
||||
* |
||||
* @param pubKeyData 公钥数据 |
||||
* @return 公钥对象 |
||||
* @throws Exception |
||||
*/ |
||||
public static RSAPublicKey getPublicKey(byte[] pubKeyData) throws Exception { |
||||
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(pubKeyData); |
||||
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM); |
||||
return (RSAPublicKey) keyFactory.generatePublic(keySpec); |
||||
} |
||||
|
||||
/** |
||||
* 获取公钥对象 |
||||
* |
||||
* @param pubKey |
||||
* 公钥 |
||||
* @return 返回公钥对象 |
||||
* @throws Exception |
||||
*/ |
||||
public static RSAPublicKey getPublicKey(String pubKey) throws Exception { |
||||
return getPublicKey(Base64.decodeBase64(pubKey)); |
||||
|
||||
} |
||||
|
||||
/** |
||||
* 获取私钥对象 |
||||
* |
||||
* @param priKey |
||||
* 私钥 |
||||
* @return 返回私钥对象 |
||||
* @throws Exception |
||||
*/ |
||||
public static RSAPrivateKey getPrivateKey(String priKey) throws Exception { |
||||
return getPrivateKey(Base64.decodeBase64(priKey)); |
||||
} |
||||
|
||||
/** |
||||
* 通过私钥byte[]将公钥还原,适用于RSA算法 |
||||
* |
||||
* @param keyBytes 私钥数据 |
||||
* @return 返回公钥对象 |
||||
* @throws Exception |
||||
*/ |
||||
public static RSAPrivateKey getPrivateKey(byte[] keyBytes) throws Exception { |
||||
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); |
||||
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM); |
||||
return (RSAPrivateKey) keyFactory.generatePrivate(keySpec); |
||||
|
||||
} |
||||
|
||||
public static String encryptByPublicKey(String data, String publicKey) throws Exception { |
||||
return encryptByPublicKey(data, getPublicKey(publicKey)); |
||||
} |
||||
|
||||
/** |
||||
* 公钥加密 |
||||
* |
||||
* @param data 内容 |
||||
* @param publicKey 公钥 |
||||
* @return 返回密文 |
||||
* @throws Exception |
||||
*/ |
||||
public static String encryptByPublicKey(String data, RSAPublicKey publicKey) throws Exception { |
||||
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM); |
||||
cipher.init(Cipher.ENCRYPT_MODE, publicKey); |
||||
byte[] bytes = cipher.doFinal(data.getBytes(UTF8)); |
||||
return Base64.encodeBase64String(bytes); |
||||
} |
||||
|
||||
public static String decryptByPublicKey(String data, String rsaPublicKey) throws Exception { |
||||
return decryptByPublicKey(data, getPublicKey(rsaPublicKey)); |
||||
} |
||||
|
||||
/** |
||||
* 公钥解密 |
||||
* |
||||
* @param data 待解密内容 |
||||
* @param rsaPublicKey 公钥 |
||||
* @return 返回明文 |
||||
* @throws Exception |
||||
*/ |
||||
public static String decryptByPublicKey(String data, RSAPublicKey rsaPublicKey) throws Exception { |
||||
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM); |
||||
cipher.init(Cipher.DECRYPT_MODE, rsaPublicKey); |
||||
byte[] inputData = Base64.decodeBase64(data); |
||||
byte[] bytes = cipher.doFinal(inputData); |
||||
return new String(bytes, UTF8); |
||||
} |
||||
|
||||
public static String encryptByPrivateKey(String data, String privateKey) throws Exception { |
||||
return encryptByPrivateKey(data, getPrivateKey(privateKey)); |
||||
} |
||||
|
||||
/** |
||||
* 私钥加密 |
||||
* |
||||
* @param data 内容 |
||||
* @param privateKey 私钥 |
||||
* @return 返回密文 |
||||
* @throws Exception |
||||
*/ |
||||
public static String encryptByPrivateKey(String data, RSAPrivateKey privateKey) throws Exception { |
||||
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM); |
||||
cipher.init(Cipher.ENCRYPT_MODE, privateKey); |
||||
byte[] bytes = cipher.doFinal(data.getBytes(UTF8)); |
||||
return Base64.encodeBase64String(bytes); |
||||
} |
||||
|
||||
public static String decryptByPrivateKey(String data, String privateKey) throws Exception { |
||||
return decryptByPrivateKey(data, getPrivateKey(privateKey)); |
||||
} |
||||
|
||||
/** |
||||
* 私钥解密 |
||||
* |
||||
* @param data 待解密内容 |
||||
* @param privateKey 私钥 |
||||
* @return 返回明文 |
||||
* @throws Exception |
||||
*/ |
||||
public static String decryptByPrivateKey(String data, RSAPrivateKey privateKey) throws Exception { |
||||
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM); |
||||
cipher.init(Cipher.DECRYPT_MODE, privateKey); |
||||
byte[] inputData = Base64.decodeBase64(data); |
||||
byte[] bytes = cipher.doFinal(inputData); |
||||
return new String(bytes, UTF8); |
||||
} |
||||
|
||||
|
||||
/* |
||||
pubKey: |
||||
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCG/iIZZzb16PxKqslkDMYa4tVFb3IVPBpLj4BgHQmDfe843sG4gkJIPXCm7+t6QxIbfDfynBpqZJLvu0c6E7TqlCtynBIlRFOBZrQVNEFkaanR2Kln3vd3CIidR571UstOC32XDyqAQNlvjD19zeIDVfmLa0Q+Or0zaxY99QwBHwIDAQAB |
||||
priKey: |
||||
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIb+IhlnNvXo/EqqyWQMxhri1UVvchU8GkuPgGAdCYN97zjewbiCQkg9cKbv63pDEht8N/KcGmpkku+7RzoTtOqUK3KcEiVEU4FmtBU0QWRpqdHYqWfe93cIiJ1HnvVSy04LfZcPKoBA2W+MPX3N4gNV+YtrRD46vTNrFj31DAEfAgMBAAECgYBiNPQdwwcq86rHr2QAE4L0AF3ju+YlKKqAmg9s3PMU5ENq/jO0xZ7u6zPPXu/S7IR51m7lY0ecazqyiW6SA9AzYH7ImWWkZ4stZ03beTB2US3cSeJIkugoexoN5fQRAGZiZezTLs91CeJivESOZyDKnnQdgJ49mveBV5OvievD8QJBAMztpqiWWavdR4tqQ+plat+rwYoXqejsK3Hyfg0pVJqEdazve2sr74rla7yI9P47ZAh1sklCv0CO//ctICv366UCQQCoop3T0FeZtbKJG+fHzZvpAe63tXpdhLMaQvTBuXLG8vi78Wyfhg5r7HOWR0Z1V7nzF1gzMywL53Pmkq9tB65zAkAiHu/A4kfL9ewTqn3kaT6CP3baJ1aDEc+qCVYzms4bbDKruLQ0A/y+g7SMj8E7E2h0gCRPTm3JsgWsgjb5Gy6BAkAA8mjQd6sGQe7utilnBdCKTmh4v5wgSk53J0kYjWIHm/WpmIFzo90Q3hMIFP5gSk3Q/6CPKQpmRrZv5QL3KcPhAkEAuMoQbij/7hyLlIxRHZs2SMXxfHPiZgDc6rVi1KNxeq8HXTlERi7Npc2Uz5TeWN4JwBBx9uA50zowk9iS05nclQ==
|
||||
用公钥加密mi : c3B0jtMdvkqrgaPxHZCK2cXMUQC2QzLud2ouLMNx0nBAj9k2/ytOuVJViTGe/DozB/ky5jvl4spD9Ey6aTMrwLHfQVhn0gRJ+wHcmx/51dXQDIgsldt6bf7YpdPdnghBjQz2+P5RhqSkeFDbTZKkl2BNaLE78a/OyWWeCGwN+4s= |
||||
true |
||||
用私钥加密mi2 : QU5vDnQ1ukj8GsauokFlgcB/g61U882tj82wHGrrqHEnvaga+4cXjML9RhjpZtKqwDGZTCujsmpynDk4qek6IGOQ/oxdWLwV4ZNjfa/oqA8OFDothVUT8wpqCu9kOYHrTdGybmXD0dB2Iy1/AMQTAgPNNXXiRXdvsz9xWYTV6z8= |
||||
true |
||||
*/ |
||||
/*public static void main(String[] args) throws Exception { |
||||
KeyStore keys = createKeys(); |
||||
String pubKey = keys.getPublicKey(); |
||||
System.out.println("pubKey:"); |
||||
System.out.println(pubKey); |
||||
String priKey = keys.getPrivateKey(); |
||||
System.out.println("priKey:"); |
||||
System.out.println(priKey); |
||||
|
||||
String ming = "1234567890123456"; |
||||
// 用公钥加密
|
||||
String mi = encryptByPublicKey(ming, pubKey); |
||||
System.out.println("用公钥加密mi : " + mi); |
||||
// 用私钥解密
|
||||
System.out.println(ming.equals(decryptByPrivateKey(mi, priKey))); |
||||
|
||||
// 用私钥加密
|
||||
String mi2 = encryptByPrivateKey(ming, priKey); |
||||
|
||||
System.out.println("用私钥加密mi2 : " + mi2); |
||||
// 用公钥解密
|
||||
String ming2 = decryptByPublicKey(mi2, pubKey); |
||||
System.out.println(ming.equals(ming2)); |
||||
}*/ |
||||
|
||||
} |
@ -0,0 +1,312 @@ |
||||
package com.gitee.sop.gatewaycommon.util; |
||||
|
||||
import org.apache.commons.codec.binary.Base64; |
||||
|
||||
import javax.crypto.Cipher; |
||||
import java.security.KeyFactory; |
||||
import java.security.KeyPair; |
||||
import java.security.KeyPairGenerator; |
||||
import java.security.interfaces.RSAPrivateKey; |
||||
import java.security.interfaces.RSAPublicKey; |
||||
import java.security.spec.PKCS8EncodedKeySpec; |
||||
import java.security.spec.X509EncodedKeySpec; |
||||
|
||||
/** |
||||
* RSA加解密工具<br> |
||||
* @author tanghc |
||||
*/ |
||||
public class RSAUtil { |
||||
public static String RSA_ALGORITHM = "RSA"; |
||||
|
||||
/** |
||||
* 创建公钥私钥 |
||||
* |
||||
* @return 返回公私钥对 |
||||
* @throws Exception |
||||
*/ |
||||
public static KeyStore createKeys() throws Exception { |
||||
KeyPairGenerator keyPairGeno = KeyPairGenerator.getInstance(RSA_ALGORITHM); |
||||
keyPairGeno.initialize(1024); |
||||
KeyPair keyPair = keyPairGeno.generateKeyPair(); |
||||
|
||||
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); |
||||
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); |
||||
|
||||
KeyStore keyStore = new KeyStore(); |
||||
keyStore.setPublicKey(Base64.encodeBase64String(publicKey.getEncoded())); |
||||
keyStore.setPrivateKey(Base64.encodeBase64String(privateKey.getEncoded())); |
||||
return keyStore; |
||||
} |
||||
|
||||
/** |
||||
* 获取公钥对象 |
||||
* |
||||
* @param pubKeyData 公钥 |
||||
* @return 返回公钥对象 |
||||
* @throws Exception |
||||
*/ |
||||
public static RSAPublicKey getPublicKey(byte[] pubKeyData) throws Exception { |
||||
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(pubKeyData); |
||||
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM); |
||||
return (RSAPublicKey) keyFactory.generatePublic(keySpec); |
||||
} |
||||
|
||||
/** |
||||
* 获取公钥对象 |
||||
* |
||||
* @param pubKey |
||||
* 公钥 |
||||
* @return 返回私钥对象 |
||||
* @throws Exception |
||||
*/ |
||||
public static RSAPublicKey getPublicKey(String pubKey) throws Exception { |
||||
return getPublicKey(Base64.decodeBase64(pubKey)); |
||||
|
||||
} |
||||
|
||||
/** |
||||
* 获取私钥对象 |
||||
* |
||||
* @param priKey |
||||
* 私钥 |
||||
* @return 私钥对象 |
||||
* @throws Exception |
||||
*/ |
||||
public static RSAPrivateKey getPrivateKey(String priKey) throws Exception { |
||||
return getPrivateKey(Base64.decodeBase64(priKey)); |
||||
} |
||||
|
||||
/** |
||||
* 通过私钥byte[]将公钥还原,适用于RSA算法 |
||||
* |
||||
* @param keyBytes |
||||
* @return 返回私钥 |
||||
* @throws Exception |
||||
*/ |
||||
public static RSAPrivateKey getPrivateKey(byte[] keyBytes) throws Exception { |
||||
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); |
||||
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM); |
||||
return (RSAPrivateKey) keyFactory.generatePrivate(keySpec); |
||||
|
||||
} |
||||
|
||||
/** |
||||
* 公钥加密 |
||||
* |
||||
* @param data 待加密内容 |
||||
* @param publicKey 公钥 |
||||
* @return 返回密文 |
||||
* @throws Exception |
||||
*/ |
||||
public static String encryptByPublicKey(String data, RSAPublicKey publicKey) throws Exception { |
||||
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM); |
||||
cipher.init(Cipher.ENCRYPT_MODE, publicKey); |
||||
// 模长
|
||||
int key_len = publicKey.getModulus().bitLength() / 8; |
||||
// 加密数据长度 <= 模长-11
|
||||
String[] datas = splitString(data, key_len - 11); |
||||
String mi = ""; |
||||
// 如果明文长度大于模长-11则要分组加密
|
||||
for (String s : datas) { |
||||
mi += bcd2Str(cipher.doFinal(s.getBytes())); |
||||
} |
||||
return mi; |
||||
} |
||||
public static String encryptByPrivateKey(String data, String privateKey) throws Exception { |
||||
return encryptByPrivateKey(data, getPrivateKey(privateKey)); |
||||
} |
||||
|
||||
/** |
||||
* 私钥加密 |
||||
* |
||||
* @param data 待加密数据 |
||||
* @param privateKey 私钥 |
||||
* @return 返回密文 |
||||
* @throws Exception |
||||
*/ |
||||
public static String encryptByPrivateKey(String data, RSAPrivateKey privateKey) throws Exception { |
||||
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM); |
||||
cipher.init(Cipher.ENCRYPT_MODE, privateKey); |
||||
// 模长
|
||||
int key_len = privateKey.getModulus().bitLength() / 8; |
||||
// 加密数据长度 <= 模长-11
|
||||
String[] datas = splitString(data, key_len - 11); |
||||
String mi = ""; |
||||
// 如果明文长度大于模长-11则要分组加密
|
||||
for (String s : datas) { |
||||
mi += bcd2Str(cipher.doFinal(s.getBytes())); |
||||
} |
||||
return mi; |
||||
} |
||||
|
||||
public static String decryptByPrivateKey(String data, String privateKey) throws Exception { |
||||
return decryptByPrivateKey(data, getPrivateKey(privateKey)); |
||||
} |
||||
|
||||
/** |
||||
* 私钥解密 |
||||
* |
||||
* @param data 待解密内容 |
||||
* @param privateKey 私钥 |
||||
* @return 返回明文 |
||||
* @throws Exception |
||||
*/ |
||||
public static String decryptByPrivateKey(String data, RSAPrivateKey privateKey) throws Exception { |
||||
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM); |
||||
cipher.init(Cipher.DECRYPT_MODE, privateKey); |
||||
// 模长
|
||||
int key_len = privateKey.getModulus().bitLength() / 8; |
||||
byte[] bytes = data.getBytes(); |
||||
byte[] bcd = ASCII_To_BCD(bytes, bytes.length); |
||||
// 如果密文长度大于模长则要分组解密
|
||||
String ming = ""; |
||||
byte[][] arrays = splitArray(bcd, key_len); |
||||
for (byte[] arr : arrays) { |
||||
ming += new String(cipher.doFinal(arr)); |
||||
} |
||||
return ming; |
||||
} |
||||
|
||||
/** |
||||
* 公钥解密 |
||||
* |
||||
* @param data 待解密内容 |
||||
* @param rsaPublicKey 公钥 |
||||
* @return 返回明文 |
||||
* @throws Exception |
||||
*/ |
||||
public static String decryptByPublicKey(String data, RSAPublicKey rsaPublicKey) throws Exception { |
||||
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM); |
||||
cipher.init(Cipher.DECRYPT_MODE, rsaPublicKey); |
||||
// 模长
|
||||
int key_len = rsaPublicKey.getModulus().bitLength() / 8; |
||||
byte[] bytes = data.getBytes(); |
||||
byte[] bcd = ASCII_To_BCD(bytes, bytes.length); |
||||
// 如果密文长度大于模长则要分组解密
|
||||
String ming = ""; |
||||
byte[][] arrays = splitArray(bcd, key_len); |
||||
for (byte[] arr : arrays) { |
||||
ming += new String(cipher.doFinal(arr)); |
||||
} |
||||
return ming; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* ASCII码转BCD码 |
||||
* |
||||
*/ |
||||
public static byte[] ASCII_To_BCD(byte[] ascii, int asc_len) { |
||||
byte[] bcd = new byte[asc_len / 2]; |
||||
int j = 0; |
||||
for (int i = 0; i < (asc_len + 1) / 2; i++) { |
||||
bcd[i] = asc_to_bcd(ascii[j++]); |
||||
bcd[i] = (byte) (((j >= asc_len) ? 0x00 : asc_to_bcd(ascii[j++]) & 0xff) + (bcd[i] << 4)); |
||||
} |
||||
return bcd; |
||||
} |
||||
|
||||
public static byte asc_to_bcd(byte asc) { |
||||
byte bcd; |
||||
|
||||
if ((asc >= '0') && (asc <= '9')) { |
||||
bcd = (byte) (asc - '0'); |
||||
} else if ((asc >= 'A') && (asc <= 'F')) { |
||||
bcd = (byte) (asc - 'A' + 10); |
||||
} else if ((asc >= 'a') && (asc <= 'f')) { |
||||
bcd = (byte) (asc - 'a' + 10); |
||||
} else { |
||||
bcd = (byte) (asc - 48); |
||||
} |
||||
return bcd; |
||||
} |
||||
|
||||
/** |
||||
* BCD转字符串 |
||||
*/ |
||||
public static String bcd2Str(byte[] bytes) { |
||||
char[] temp = new char[bytes.length * 2]; |
||||
char val; |
||||
|
||||
for (int i = 0; i < bytes.length; i++) { |
||||
val = (char) (((bytes[i] & 0xf0) >> 4) & 0x0f); |
||||
temp[i * 2] = (char) (val > 9 ? val + 'A' - 10 : val + '0'); |
||||
|
||||
val = (char) (bytes[i] & 0x0f); |
||||
temp[i * 2 + 1] = (char) (val > 9 ? val + 'A' - 10 : val + '0'); |
||||
} |
||||
return new String(temp); |
||||
} |
||||
|
||||
/** |
||||
* 拆分字符串 |
||||
*/ |
||||
public static String[] splitString(String string, int len) { |
||||
int x = string.length() / len; |
||||
int y = string.length() % len; |
||||
int z = 0; |
||||
if (y != 0) { |
||||
z = 1; |
||||
} |
||||
String[] strings = new String[x + z]; |
||||
String str = ""; |
||||
for (int i = 0; i < x + z; i++) { |
||||
if (i == x + z - 1 && y != 0) { |
||||
str = string.substring(i * len, i * len + y); |
||||
} else { |
||||
str = string.substring(i * len, i * len + len); |
||||
} |
||||
strings[i] = str; |
||||
} |
||||
return strings; |
||||
} |
||||
|
||||
/** |
||||
* 拆分数组 |
||||
*/ |
||||
public static byte[][] splitArray(byte[] data, int len) { |
||||
int x = data.length / len; |
||||
int y = data.length % len; |
||||
int z = 0; |
||||
if (y != 0) { |
||||
z = 1; |
||||
} |
||||
byte[][] arrays = new byte[x + z][]; |
||||
byte[] arr; |
||||
for (int i = 0; i < x + z; i++) { |
||||
arr = new byte[len]; |
||||
if (i == x + z - 1 && y != 0) { |
||||
System.arraycopy(data, i * len, arr, 0, y); |
||||
} else { |
||||
System.arraycopy(data, i * len, arr, 0, len); |
||||
} |
||||
arrays[i] = arr; |
||||
} |
||||
return arrays; |
||||
} |
||||
|
||||
/*public static void main(String[] args) throws Exception { |
||||
KeyStore keys = createKeys(); |
||||
String pubKey = keys.getPublicKey(); |
||||
System.out.println("pubKey:"); |
||||
System.out.println(pubKey); |
||||
String priKey = keys.getPrivateKey(); |
||||
System.out.println("priKey:"); |
||||
System.out.println(priKey); |
||||
|
||||
String ming = "6460201d23954f8e90cf79b818844ca0"; |
||||
// 用公钥加密
|
||||
String mi = encryptByPublicKey(ming, getPublicKey(pubKey)); |
||||
System.out.println("mi : " + mi); |
||||
// 用私钥解密
|
||||
System.out.println("ming : " + decryptByPrivateKey(mi, getPrivateKey(priKey))); |
||||
|
||||
// 用私钥加密
|
||||
String mi2 = encryptByPrivateKey(ming, getPrivateKey(priKey)); |
||||
|
||||
System.out.println("mi2 : " + mi2); |
||||
// 用公钥解密
|
||||
System.out.println("ming2 : " + decryptByPublicKey(mi2, getPublicKey(pubKey))); |
||||
}*/ |
||||
|
||||
} |
@ -0,0 +1,139 @@ |
||||
package com.gitee.sop.gatewaycommon.util; |
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils; |
||||
import org.springframework.data.redis.core.RedisTemplate; |
||||
import org.springframework.data.redis.core.script.RedisScript; |
||||
|
||||
import java.util.Collections; |
||||
|
||||
/** |
||||
* redis分布式锁,详见:https://blog.csdn.net/thc1987/article/details/80355155<br>
|
||||
* 思路: |
||||
* <pre> |
||||
* 用SETNX命令,SETNX只有在key不存在时才返回成功。这意味着只有一个线程可以成功运行SETNX命令,而其他线程会失败,然后不断重试,直到它们能建立锁。 |
||||
* 然后使用脚本来创建锁,因为一个redis脚本同一时刻只能运行一次。 |
||||
* 创建锁代码: |
||||
* <code> |
||||
-- KEYS[1] key, |
||||
-- ARGV[1] value, |
||||
-- ARGV[2] expireTimeMilliseconds |
||||
|
||||
if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then |
||||
redis.call('pexpire', KEYS[1], ARGV[2]) |
||||
return 1 |
||||
else |
||||
return 0 |
||||
end |
||||
* </code> |
||||
* 最后使用脚本来解锁。 |
||||
* 解锁代码: |
||||
* |
||||
* <code> |
||||
-- KEYS[1] key, |
||||
-- ARGV[1] value |
||||
if redis.call("get", KEYS[1]) == ARGV[1] |
||||
then |
||||
return redis.call("del", KEYS[1]) |
||||
else |
||||
return 0 |
||||
end |
||||
* </code> |
||||
* </pre> |
||||
* |
||||
* @author tanghc |
||||
*/ |
||||
public class RedisLockUtil { |
||||
|
||||
private static final Long SUCCESS = 1L; |
||||
|
||||
/** 加锁脚本 */ |
||||
private static final String SCRIPT_LOCK = "if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then redis.call('pexpire', KEYS[1], ARGV[2]) return 1 else return 0 end"; |
||||
/** 解锁脚本 */ |
||||
private static final String SCRIPT_UNLOCK = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; |
||||
/** 加锁脚本sha1值 */ |
||||
private static final String SCRIPT_LOCK_SHA1 = DigestUtils.sha1Hex(SCRIPT_LOCK); |
||||
/** 解锁脚本sha1值 */ |
||||
private static final String SCRIPT_UNLOCK_SHA1 = DigestUtils.sha1Hex(SCRIPT_UNLOCK); |
||||
|
||||
/** |
||||
* 尝试获取分布式锁 |
||||
* |
||||
* @param redisTemplate |
||||
* Redis客户端 |
||||
* @param lockKey |
||||
* 锁 |
||||
* @param requestId |
||||
* 请求标识 |
||||
* @param expireTimeMilliseconds |
||||
* 超期时间,多少毫秒后这把锁自动释放 |
||||
* @return 返回true表示拿到锁 |
||||
*/ |
||||
@SuppressWarnings("unchecked") |
||||
public static boolean tryGetDistributedLock(@SuppressWarnings("rawtypes") final RedisTemplate redisTemplate, |
||||
final String lockKey, final String requestId, final int expireTimeMilliseconds) { |
||||
|
||||
Object result = redisTemplate.execute(new RedisScript<Long>() { |
||||
@Override |
||||
public String getSha1() { |
||||
return SCRIPT_LOCK_SHA1; |
||||
} |
||||
|
||||
@Override |
||||
public Class<Long> getResultType() { |
||||
return Long.class; |
||||
} |
||||
|
||||
@Override |
||||
public String getScriptAsString() { |
||||
return SCRIPT_LOCK; |
||||
} |
||||
|
||||
}, |
||||
// KEYS[1]
|
||||
Collections.singletonList(lockKey), |
||||
// ARGV[1]
|
||||
requestId, |
||||
// ARGV[2]
|
||||
String.valueOf(expireTimeMilliseconds) |
||||
); |
||||
|
||||
return SUCCESS.equals(result); |
||||
} |
||||
|
||||
/** |
||||
* 释放分布式锁 |
||||
* |
||||
* @param redisTemplate |
||||
* Redis客户端 |
||||
* @param lockKey |
||||
* 锁 |
||||
* @param requestId |
||||
* 请求标识 |
||||
* @return 返回true表示释放锁成功 |
||||
*/ |
||||
@SuppressWarnings("unchecked") |
||||
public static boolean releaseDistributedLock(@SuppressWarnings("rawtypes") RedisTemplate redisTemplate, |
||||
String lockKey, String requestId) { |
||||
|
||||
Object result = redisTemplate.execute(new RedisScript<Long>() { |
||||
@Override |
||||
public String getSha1() { |
||||
return SCRIPT_UNLOCK_SHA1; |
||||
} |
||||
|
||||
@Override |
||||
public Class<Long> getResultType() { |
||||
return Long.class; |
||||
} |
||||
|
||||
@Override |
||||
public String getScriptAsString() { |
||||
return SCRIPT_UNLOCK; |
||||
} |
||||
|
||||
}, Collections.singletonList(lockKey), requestId); |
||||
|
||||
return SUCCESS.equals(result); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,137 @@ |
||||
package com.gitee.sop.gatewaycommon.util; |
||||
|
||||
import com.alibaba.fastjson.JSON; |
||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum; |
||||
import org.apache.commons.io.IOUtils; |
||||
import org.apache.commons.lang.StringUtils; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
import java.io.IOException; |
||||
import java.net.InetAddress; |
||||
import java.net.UnknownHostException; |
||||
import java.util.Collections; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
import java.util.Map.Entry; |
||||
import java.util.Set; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
public class RequestUtil { |
||||
|
||||
private static final String CONTENT_TYPE_URLENCODED = "application/x-www-form-urlencoded"; |
||||
private static final String CONTENT_TYPE_JSON = "application/json"; |
||||
private static final String CONTENT_TYPE_TEXT = "text/plain"; |
||||
|
||||
private static final String UTF8 = "UTF-8"; |
||||
private static final String GET = "get"; |
||||
|
||||
private static final String UNKOWN = "unknown"; |
||||
private static final String LOCAL_IP = "127.0.0.1"; |
||||
private static final int IP_LEN = 15; |
||||
|
||||
public static String getText(HttpServletRequest request) throws Exception { |
||||
return IOUtils.toString(request.getInputStream(), UTF8); |
||||
} |
||||
|
||||
/** |
||||
* 从request中获取json。如果提交方式是application/x-www-form-urlencoded,则组装成json格式。 |
||||
* |
||||
* @param request request对象 |
||||
* @return 返回json |
||||
* @throws IOException |
||||
*/ |
||||
public static String getJson(HttpServletRequest request) throws Exception { |
||||
String requestJson = null; |
||||
String contectType = request.getContentType(); |
||||
if (StringUtils.isBlank(contectType)) { |
||||
throw ErrorEnum.ISV_INVALID_CONTENT_TYPE.getErrorMeta().getException(contectType); |
||||
} |
||||
|
||||
contectType = contectType.toLowerCase(); |
||||
|
||||
if (contectType.contains(CONTENT_TYPE_JSON) || contectType.contains(CONTENT_TYPE_TEXT)) { |
||||
requestJson = getText(request); |
||||
} else if (contectType.contains(CONTENT_TYPE_URLENCODED)) { |
||||
Map<String, Object> params = convertRequestParamsToMap(request); |
||||
requestJson = JSON.toJSONString(params); |
||||
} else { |
||||
throw ErrorEnum.ISV_INVALID_CONTENT_TYPE.getErrorMeta().getException(contectType); |
||||
} |
||||
return requestJson; |
||||
} |
||||
|
||||
/** |
||||
* request中的参数转换成map |
||||
* |
||||
* @param request request对象 |
||||
* @return 返回参数键值对 |
||||
*/ |
||||
public static Map<String, Object> convertRequestParamsToMap(HttpServletRequest request) { |
||||
Map<String, String[]> paramMap = request.getParameterMap(); |
||||
if(paramMap == null || paramMap.isEmpty()) { |
||||
return Collections.emptyMap(); |
||||
} |
||||
Map<String, Object> retMap = new HashMap<String, Object>(paramMap.size()); |
||||
|
||||
Set<Entry<String, String[]>> entrySet = paramMap.entrySet(); |
||||
|
||||
for (Entry<String, String[]> entry : entrySet) { |
||||
String name = entry.getKey(); |
||||
String[] values = entry.getValue(); |
||||
if (values.length == 1) { |
||||
retMap.put(name, values[0]); |
||||
} |
||||
} |
||||
return retMap; |
||||
} |
||||
|
||||
/** |
||||
* 获取客户端真实IP |
||||
* |
||||
* @param request request对象 |
||||
* @return 返回ip |
||||
*/ |
||||
public static String getClientIP(HttpServletRequest request) { |
||||
String ipAddress = request.getHeader("x-forwarded-for"); |
||||
if (ipAddress == null || ipAddress.length() == 0 || UNKOWN.equalsIgnoreCase(ipAddress)) { |
||||
ipAddress = request.getHeader("Proxy-Client-IP"); |
||||
} |
||||
if (ipAddress == null || ipAddress.length() == 0 || UNKOWN.equalsIgnoreCase(ipAddress)) { |
||||
ipAddress = request.getHeader("WL-Proxy-Client-IP"); |
||||
} |
||||
if (ipAddress == null || ipAddress.length() == 0 || UNKOWN.equalsIgnoreCase(ipAddress)) { |
||||
ipAddress = request.getRemoteAddr(); |
||||
if (LOCAL_IP.equals(ipAddress)) { |
||||
// 根据网卡取本机配置的IP
|
||||
try { |
||||
InetAddress inet = InetAddress.getLocalHost(); |
||||
ipAddress = inet.getHostAddress(); |
||||
} catch (UnknownHostException e) { |
||||
// ignore
|
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
// 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
|
||||
if (ipAddress != null && ipAddress.length() > IP_LEN) { |
||||
if (ipAddress.indexOf(",") > 0) { |
||||
ipAddress = ipAddress.substring(0, ipAddress.indexOf(",")); |
||||
} |
||||
} |
||||
return ipAddress; |
||||
|
||||
} |
||||
|
||||
/** |
||||
* 是否是get请求 |
||||
* @param request request对象 |
||||
* @return true,是 |
||||
*/ |
||||
public static boolean isGetRequest(HttpServletRequest request) { |
||||
return GET.equalsIgnoreCase(request.getMethod()); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,119 @@ |
||||
package com.gitee.sop.gatewaycommon.util; |
||||
|
||||
import com.alibaba.fastjson.JSON; |
||||
import com.alibaba.fastjson.JSONArray; |
||||
import com.alibaba.fastjson.JSONObject; |
||||
import lombok.Data; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
import org.springframework.http.MediaType; |
||||
|
||||
import javax.servlet.http.HttpServletResponse; |
||||
import java.io.IOException; |
||||
import java.util.Arrays; |
||||
import java.util.Iterator; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.Set; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
public class ResponseUtil { |
||||
public static final String UTF_8 = "UTF-8"; |
||||
|
||||
private static Logger log = LoggerFactory.getLogger(ResponseUtil.class); |
||||
|
||||
public static void writeJson(HttpServletResponse response, Object result) { |
||||
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); |
||||
response.setCharacterEncoding(UTF_8); |
||||
try { |
||||
response.getWriter().write(JSON.toJSONString(result)); |
||||
} catch (IOException e) { |
||||
log.error("doWriter", e); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* map转成xml |
||||
* |
||||
* @param parameters |
||||
* @return |
||||
*/ |
||||
public static String mapToXml(JSONObject parameters) { |
||||
String content = doMap2xml(parameters); |
||||
return content; |
||||
} |
||||
|
||||
private static String doMap2xml(JSONObject parameters) { |
||||
StringBuffer sb = new StringBuffer(); |
||||
Set es = parameters.entrySet(); |
||||
Iterator it = es.iterator(); |
||||
while (it.hasNext()) { |
||||
Map.Entry entry = (Map.Entry) it.next(); |
||||
String k = (String) entry.getKey(); |
||||
Object v = entry.getValue(); |
||||
if (v instanceof JSONObject) { |
||||
sb.append("<").append(k).append(">") |
||||
.append(doMap2xml((JSONObject) v)) |
||||
.append("</").append(k).append(">"); |
||||
} else if (v instanceof JSONArray) { |
||||
JSONArray collection = (JSONArray) v; |
||||
String items = buildItems(k + "_item", collection); |
||||
sb.append("<").append(k).append(">") |
||||
.append(items) |
||||
.append("</").append(k).append(">"); |
||||
} else { |
||||
sb.append("<").append(k).append("><![CDATA[") |
||||
.append(v) |
||||
.append("]]></").append(k).append(">"); |
||||
} |
||||
} |
||||
return sb.toString(); |
||||
} |
||||
|
||||
private static String buildItems(String key, JSONArray collection) { |
||||
StringBuffer sb = new StringBuffer(); |
||||
for (int i = 0; i < collection.size(); i++) { |
||||
Object jsonObject = collection.get(i); |
||||
sb.append("<").append(key).append(">"); |
||||
if (jsonObject instanceof JSONObject) { |
||||
sb.append(doMap2xml((JSONObject) jsonObject)); |
||||
} else { |
||||
sb.append(jsonObject); |
||||
} |
||||
sb.append("</").append(key).append(">"); |
||||
} |
||||
return sb.toString(); |
||||
} |
||||
|
||||
public static void main(String[] args) { |
||||
Persion persion = new Persion(); |
||||
persion.setId(1); |
||||
persion.setName("aaa"); |
||||
|
||||
String jsonString = JSON.toJSONString(persion); |
||||
JSONObject jsonObject = JSON.parseObject(jsonString); |
||||
|
||||
String xml = mapToXml(jsonObject); |
||||
System.out.println(xml); |
||||
|
||||
} |
||||
|
||||
@Data |
||||
public static class Persion { |
||||
int id; |
||||
String name; |
||||
List<String> items = Arrays.asList("item1", "item2"); |
||||
List<Man> child = Arrays.asList(new Man("Jim"), new Man("Tom")); |
||||
} |
||||
|
||||
@Data |
||||
public static class Man{ |
||||
String name; |
||||
|
||||
public Man(String name) { |
||||
this.name = name; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,32 @@ |
||||
package com.gitee.sop.gatewaycommon.util; |
||||
|
||||
import com.gitee.sop.gatewaycommon.result.ApiResult; |
||||
import com.thoughtworks.xstream.XStream; |
||||
import com.thoughtworks.xstream.io.naming.NoNameCoder; |
||||
import com.thoughtworks.xstream.io.xml.StaxDriver; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
public class XmlUtil { |
||||
private static XStream xStream = new XStream(new StaxDriver(new NoNameCoder())); |
||||
static { |
||||
xStream.processAnnotations(ApiResult.class); |
||||
xStream.aliasSystemAttribute(null, "class"); |
||||
} |
||||
|
||||
public static String serialize(Object obj) { |
||||
return getXStream().toXML(obj); |
||||
} |
||||
|
||||
public static <T> T unserialize(String xml, Class<T> clazz) { |
||||
xStream.processAnnotations(clazz); |
||||
Object object = xStream.fromXML(xml); |
||||
T cast = clazz.cast(object); |
||||
return cast; |
||||
} |
||||
|
||||
public static XStream getXStream() { |
||||
return xStream; |
||||
} |
||||
} |
@ -0,0 +1,36 @@ |
||||
package com.gitee.sop.gatewaycommon.validate; |
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext; |
||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum; |
||||
import com.gitee.sop.gatewaycommon.param.ApiParam; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.apache.commons.lang.StringUtils; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
@Slf4j |
||||
public abstract class AbstractSigner implements Signer { |
||||
|
||||
/** |
||||
* 构建服务端签名串 |
||||
* |
||||
* @param params |
||||
* @param secret |
||||
* @return |
||||
*/ |
||||
protected abstract String buildServerSign(ApiParam params, String secret); |
||||
|
||||
@Override |
||||
public boolean checkSign(HttpServletRequest request, String secret) { |
||||
ApiParam apiParam = ApiContext.getApiParam(); |
||||
String clientSign = apiParam.fetchSignAndRemove(); |
||||
if (StringUtils.isBlank(clientSign)) { |
||||
throw ErrorEnum.ISV_MISSING_SIGNATURE.getErrorMeta().getException(); |
||||
} |
||||
String serverSign = buildServerSign(apiParam, secret); |
||||
return clientSign.equals(serverSign); |
||||
} |
||||
} |
@ -0,0 +1,60 @@ |
||||
package com.gitee.sop.gatewaycommon.validate; |
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils; |
||||
import com.gitee.sop.gatewaycommon.util.AESUtil; |
||||
import com.gitee.sop.gatewaycommon.util.RSANewUtil; |
||||
import com.gitee.sop.gatewaycommon.util.RSAUtil; |
||||
|
||||
/** |
||||
* 负责各类加解密 |
||||
* @author tanghc |
||||
* |
||||
*/ |
||||
public class ApiEncrypter implements Encrypter { |
||||
|
||||
@Override |
||||
public String aesEncryptToHex(String content, String password) throws Exception { |
||||
return AESUtil.encryptToHex(content, password); |
||||
} |
||||
|
||||
@Override |
||||
public String aesDecryptFromHex(String hex, String password) throws Exception { |
||||
return AESUtil.decryptFromHex(hex, password); |
||||
} |
||||
|
||||
@Override |
||||
public String aesEncryptToBase64String(String content, String password) throws Exception { |
||||
return AESUtil.encryptToBase64String(content, password); |
||||
} |
||||
|
||||
@Override |
||||
public String aesDecryptFromBase64String(String base64String, String password) throws Exception { |
||||
return AESUtil.decryptFromBase64String(base64String, password); |
||||
} |
||||
|
||||
@Override |
||||
public String rsaDecryptByPrivateKey(String data, String privateKey) throws Exception { |
||||
return RSAUtil.decryptByPrivateKey(data, privateKey); |
||||
} |
||||
|
||||
@Override |
||||
public String rsaEncryptByPrivateKey(String data, String privateKey) throws Exception { |
||||
return RSAUtil.encryptByPrivateKey(data, privateKey); |
||||
} |
||||
|
||||
@Override |
||||
public String rsaDecryptByPrivateKeyNew(String data, String privateKey) throws Exception { |
||||
return RSANewUtil.decryptByPrivateKey(data, privateKey); |
||||
} |
||||
|
||||
@Override |
||||
public String rsaEncryptByPrivateKeyNew(String data, String privateKey) throws Exception { |
||||
return RSANewUtil.encryptByPrivateKey(data, privateKey); |
||||
} |
||||
|
||||
@Override |
||||
public String md5(String value) { |
||||
return DigestUtils.md5Hex(value); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,56 @@ |
||||
package com.gitee.sop.gatewaycommon.validate; |
||||
|
||||
|
||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum; |
||||
import com.gitee.sop.gatewaycommon.param.ApiParam; |
||||
import org.apache.tomcat.util.buf.HexUtils; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Collections; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.Set; |
||||
|
||||
/** |
||||
* 签名验证实现 |
||||
* |
||||
* @author tanghc |
||||
*/ |
||||
public class ApiSigner extends AbstractSigner { |
||||
|
||||
private Map<String, SignEncipher> signEncipherMap = new HashMap<>(); |
||||
|
||||
public ApiSigner() { |
||||
signEncipherMap.put("md5", new SignEncipherMD5()); |
||||
signEncipherMap.put("hmac", new SignEncipherHMAC_MD5()); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public String buildServerSign(ApiParam param, String secret) { |
||||
String signMethod = param.fetchSignMethod(); |
||||
SignEncipher signEncipher = signEncipherMap.get(signMethod); |
||||
if (signEncipher == null) { |
||||
throw ErrorEnum.ISV_INVALID_SIGNATURE_TYPE.getErrorMeta().getException(signMethod); |
||||
} |
||||
|
||||
// 第一步:参数排序
|
||||
Set<String> keySet = param.keySet(); |
||||
List<String> paramNames = new ArrayList<String>(keySet); |
||||
Collections.sort(paramNames); |
||||
|
||||
// 第二步:把所有参数名和参数值串在一起
|
||||
StringBuilder paramNameValue = new StringBuilder(); |
||||
for (String paramName : paramNames) { |
||||
paramNameValue.append(paramName).append(param.get(paramName)); |
||||
} |
||||
|
||||
// 第三步:使用MD5/HMAC加密
|
||||
String source = paramNameValue.toString(); |
||||
byte[] bytes = signEncipher.encrypt(source, secret); |
||||
|
||||
// 第四步:把二进制转化为大写的十六进制
|
||||
return HexUtils.toHexString(bytes).toUpperCase(); |
||||
} |
||||
} |
@ -0,0 +1,150 @@ |
||||
package com.gitee.sop.gatewaycommon.validate; |
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiConfig; |
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext; |
||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum; |
||||
import com.gitee.sop.gatewaycommon.param.ApiParam; |
||||
import com.gitee.sop.gatewaycommon.param.ParamNames; |
||||
import com.gitee.sop.gatewaycommon.param.UploadContext; |
||||
import com.gitee.sop.gatewaycommon.secret.AppSecretManager; |
||||
import org.apache.commons.codec.digest.DigestUtils; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.StringUtils; |
||||
import org.springframework.web.multipart.MultipartFile; |
||||
|
||||
import java.io.IOException; |
||||
import java.text.ParseException; |
||||
import java.text.SimpleDateFormat; |
||||
import java.util.Arrays; |
||||
import java.util.Date; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* 负责校验,校验工作都在这里 |
||||
* |
||||
* @author tanghc |
||||
*/ |
||||
public class ApiValidator implements Validator { |
||||
private static final Logger logger = LoggerFactory.getLogger(ApiValidator.class); |
||||
|
||||
private static final int MILLISECOND_OF_ONE_SECOND = 1000; |
||||
|
||||
|
||||
private static List<String> FORMAT_LIST = Arrays.asList("json", "xml"); |
||||
|
||||
|
||||
@Override |
||||
public void validate(ApiParam param) { |
||||
ApiConfig apiConfig = ApiContext.getApiConfig(); |
||||
if (apiConfig.isIgnoreValidate() || param.fetchIgnoreValidate()) { |
||||
logger.debug("忽略所有验证(ignoreValidate=true), name:{}, version:{}", param.fetchName(), param.fetchVersion()); |
||||
return; |
||||
} |
||||
if (param.fetchIgnoreSign()) { |
||||
logger.debug("忽略签名验证, name:{}, version:{}", param.fetchName(), param.fetchVersion()); |
||||
} else { |
||||
// 需要验证签名
|
||||
checkAppKey(param); |
||||
checkSign(param); |
||||
} |
||||
checkUploadFile(param); |
||||
checkTimeout(param); |
||||
checkFormat(param); |
||||
} |
||||
|
||||
/** |
||||
* 校验上传文件内容 |
||||
* |
||||
* @param param |
||||
*/ |
||||
protected void checkUploadFile(ApiParam param) { |
||||
UploadContext uploadContext = ApiContext.getUploadContext(); |
||||
if (uploadContext != null) { |
||||
try { |
||||
List<MultipartFile> files = uploadContext.getAllFile(); |
||||
for (MultipartFile file : files) { |
||||
// 客户端传来的文件md5
|
||||
String clientMd5 = param.getString(file.getName()); |
||||
if (clientMd5 != null) { |
||||
String fileMd5 = DigestUtils.md5Hex(file.getBytes()); |
||||
if (!clientMd5.equals(fileMd5)) { |
||||
throw ErrorEnum.ISV_UPLOAD_FAIL.getErrorMeta().getException(); |
||||
} |
||||
} |
||||
} |
||||
} catch (IOException e) { |
||||
logger.error("验证上传文件MD5错误", e); |
||||
throw ErrorEnum.ISV_UPLOAD_FAIL.getErrorMeta().getException(); |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
protected void checkTimeout(ApiParam param) { |
||||
int timeoutSeconds = ApiContext.getApiConfig().getTimeoutSeconds(); |
||||
// 如果设置为0,表示不校验
|
||||
if (timeoutSeconds == 0) { |
||||
return; |
||||
} |
||||
if (timeoutSeconds < 0) { |
||||
throw new IllegalArgumentException("服务端timeoutSeconds设置错误"); |
||||
} |
||||
String requestTime = param.fetchTimestamp(); |
||||
try { |
||||
Date requestDate = new SimpleDateFormat(ParamNames.TIMESTAMP_PATTERN).parse(requestTime); |
||||
long requestMilliseconds = requestDate.getTime(); |
||||
if (System.currentTimeMillis() - requestMilliseconds > timeoutSeconds * MILLISECOND_OF_ONE_SECOND) { |
||||
throw ErrorEnum.ISV_INVALID_TIMESTAMP.getErrorMeta().getException(); |
||||
} |
||||
} catch (ParseException e) { |
||||
throw ErrorEnum.ISV_INVALID_TIMESTAMP.getErrorMeta().getException(param.fetchNameVersion()); |
||||
} |
||||
} |
||||
|
||||
protected void checkAppKey(ApiParam param) { |
||||
if (StringUtils.isEmpty(param.fetchAppKey())) { |
||||
throw ErrorEnum.ISV_MISSING_APP_ID.getErrorMeta().getException(); |
||||
} |
||||
AppSecretManager appSecretManager = ApiContext.getApiConfig().getAppSecretManager(); |
||||
Assert.notNull(appSecretManager, "appSecretManager未初始化"); |
||||
boolean isTrueAppKey = appSecretManager.isValidAppKey(param.fetchAppKey()); |
||||
if (!isTrueAppKey) { |
||||
throw ErrorEnum.ISV_INVALID_APP_ID.getErrorMeta().getException(); |
||||
} |
||||
} |
||||
|
||||
protected void checkSign(ApiParam param) { |
||||
String clientSign = param.fetchSign(); |
||||
try { |
||||
if (StringUtils.isEmpty(param.fetchSign())) { |
||||
throw ErrorEnum.ISV_MISSING_SIGNATURE.getErrorMeta().getException(param.fetchNameVersion(), ParamNames.SIGN_NAME); |
||||
} |
||||
String secret = ApiContext.getApiConfig().getAppSecretManager().getSecret(param.fetchAppKey()); |
||||
if (StringUtils.isEmpty(secret)) { |
||||
throw ErrorEnum.ISV_MISSING_SIGNATURE_CONFIG.getErrorMeta().getException(); |
||||
} |
||||
Signer signer = ApiContext.getApiConfig().getSigner(); |
||||
boolean isRightSign = signer.checkSign(ApiContext.getRequest(), secret); |
||||
// 错误的sign
|
||||
if (!isRightSign) { |
||||
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(param.fetchNameVersion()); |
||||
} |
||||
} finally { |
||||
// 校验过程中会移除sign,这里需要重新设置进去
|
||||
param.setSign(clientSign); |
||||
} |
||||
} |
||||
|
||||
|
||||
protected void checkFormat(ApiParam param) { |
||||
String format = param.fetchFormat(); |
||||
boolean contains = FORMAT_LIST.contains(format.toLowerCase()); |
||||
|
||||
if (!contains) { |
||||
throw ErrorEnum.ISV_INVALID_FORMAT.getErrorMeta().getException(param.fetchNameVersion(), format); |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,95 @@ |
||||
package com.gitee.sop.gatewaycommon.validate; |
||||
|
||||
/** |
||||
* 负责加解密 |
||||
* |
||||
* @author tanghc |
||||
*/ |
||||
public interface Encrypter { |
||||
|
||||
/** |
||||
* AES文本加密 |
||||
* |
||||
* @param content 明文 |
||||
* @param password 密码 |
||||
* @return 返回16进制内容 |
||||
* @throws Exception |
||||
*/ |
||||
String aesEncryptToHex(String content, String password) throws Exception; |
||||
|
||||
/** |
||||
* AES文本解密 |
||||
* |
||||
* @param hex 待解密文本,16进制内容 |
||||
* @param password 密码 |
||||
* @return 返回明文 |
||||
* @throws Exception |
||||
*/ |
||||
String aesDecryptFromHex(String hex, String password) throws Exception; |
||||
|
||||
/** |
||||
* AES文本加密 |
||||
* |
||||
* @param content 明文 |
||||
* @param password 密码 |
||||
* @return 返回base64内容 |
||||
* @throws Exception |
||||
*/ |
||||
String aesEncryptToBase64String(String content, String password) throws Exception; |
||||
|
||||
/** |
||||
* AES文本解密 |
||||
* |
||||
* @param base64String 待解密文本,16进制内容 |
||||
* @param password 密码 |
||||
* @return 返回明文 |
||||
* @throws Exception |
||||
*/ |
||||
String aesDecryptFromBase64String(String base64String, String password) throws Exception; |
||||
|
||||
/** |
||||
* RSA私钥解密 |
||||
* |
||||
* @param data 解密内容 |
||||
* @param privateKey 私钥 |
||||
* @return 返回明文 |
||||
* @throws Exception |
||||
*/ |
||||
String rsaDecryptByPrivateKey(String data, String privateKey) throws Exception; |
||||
|
||||
/** |
||||
* 新版rsa私钥解密 |
||||
* @param data 解密内容 |
||||
* @param privateKey 私钥 |
||||
* @return 返回明文 |
||||
* @throws Exception |
||||
*/ |
||||
String rsaDecryptByPrivateKeyNew(String data, String privateKey) throws Exception; |
||||
|
||||
/** |
||||
* RSA私钥加密 |
||||
* |
||||
* @param data 明文 |
||||
* @param privateKey 私钥 |
||||
* @return 返回密文 |
||||
* @throws Exception |
||||
*/ |
||||
String rsaEncryptByPrivateKey(String data, String privateKey) throws Exception; |
||||
|
||||
/** |
||||
* 新版rsa私钥加密 |
||||
* @param data 明文 |
||||
* @param privateKey 私钥 |
||||
* @return 返回密文 |
||||
* @throws Exception |
||||
*/ |
||||
String rsaEncryptByPrivateKeyNew(String data, String privateKey) throws Exception; |
||||
|
||||
/** |
||||
* md5加密,全部小写 |
||||
* |
||||
* @param value |
||||
* @return 返回md5内容 |
||||
*/ |
||||
String md5(String value); |
||||
} |
@ -0,0 +1,13 @@ |
||||
package com.gitee.sop.gatewaycommon.validate; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
public interface SignEncipher { |
||||
/** |
||||
* 签名的摘要算法 |
||||
* @param input 待签名数据 |
||||
* @return 返回加密后的数据 |
||||
*/ |
||||
byte[] encrypt(String input, String secret); |
||||
} |
@ -0,0 +1,37 @@ |
||||
package com.gitee.sop.gatewaycommon.validate; |
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.SopConstants; |
||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
|
||||
import javax.crypto.Mac; |
||||
import javax.crypto.SecretKey; |
||||
import javax.crypto.spec.SecretKeySpec; |
||||
import java.security.InvalidKeyException; |
||||
import java.security.NoSuchAlgorithmException; |
||||
|
||||
/** |
||||
* HMAC_MD5加密 |
||||
* @author tanghc |
||||
*/ |
||||
@Slf4j |
||||
public class SignEncipherHMAC_MD5 implements SignEncipher { |
||||
|
||||
public static final String HMAC_MD5 = "HmacMD5"; |
||||
|
||||
@Override |
||||
public byte[] encrypt(String input, String secret) { |
||||
try { |
||||
SecretKey secretKey = new SecretKeySpec(secret.getBytes(SopConstants.CHARSET_UTF8), HMAC_MD5); |
||||
Mac mac = Mac.getInstance(secretKey.getAlgorithm()); |
||||
mac.init(secretKey); |
||||
return mac.doFinal(input.getBytes(SopConstants.CHARSET_UTF8)); |
||||
} catch (NoSuchAlgorithmException e) { |
||||
log.error("HMAC_MD5加密加密失败NoSuchAlgorithmException", e); |
||||
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(); |
||||
} catch (InvalidKeyException e) { |
||||
log.error("HMAC_MD5加密加密失败InvalidKeyException", e); |
||||
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,16 @@ |
||||
package com.gitee.sop.gatewaycommon.validate; |
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils; |
||||
|
||||
import java.nio.charset.StandardCharsets; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
public class SignEncipherMD5 implements SignEncipher { |
||||
@Override |
||||
public byte[] encrypt(String input, String secret) { |
||||
String source = secret + input + secret; |
||||
return DigestUtils.md5(source.getBytes(StandardCharsets.UTF_8)); |
||||
} |
||||
} |
@ -0,0 +1,20 @@ |
||||
package com.gitee.sop.gatewaycommon.validate; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
|
||||
/** |
||||
* 负责签名校验 |
||||
* @author tanghc |
||||
* |
||||
*/ |
||||
public interface Signer { |
||||
|
||||
/** |
||||
* 签名校验 |
||||
* @param request |
||||
* @param secret 秘钥 |
||||
* @return true签名正确 |
||||
*/ |
||||
boolean checkSign(HttpServletRequest request, String secret); |
||||
|
||||
} |
@ -0,0 +1,18 @@ |
||||
package com.gitee.sop.gatewaycommon.validate; |
||||
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam; |
||||
|
||||
/** |
||||
* 校验接口 |
||||
* |
||||
* @author tanghc |
||||
* |
||||
*/ |
||||
public interface Validator { |
||||
/** |
||||
* 接口验证 |
||||
* @param param 接口参数 |
||||
*/ |
||||
void validate(ApiParam param); |
||||
|
||||
} |
@ -0,0 +1,97 @@ |
||||
/** |
||||
* Alipay.com Inc. |
||||
* Copyright (c) 2004-2012 All Rights Reserved. |
||||
*/ |
||||
package com.gitee.sop.gatewaycommon.validate.alipay; |
||||
|
||||
/** |
||||
* |
||||
* @author runzhi |
||||
*/ |
||||
public class AlipayConstants { |
||||
|
||||
public static final String SIGN_TYPE = "sign_type"; |
||||
|
||||
public static final String SIGN_TYPE_RSA = "RSA"; |
||||
|
||||
/** |
||||
* sha256WithRsa 算法请求类型 |
||||
*/ |
||||
public static final String SIGN_TYPE_RSA2 = "RSA2"; |
||||
|
||||
public static final String SIGN_ALGORITHMS = "SHA1WithRSA"; |
||||
|
||||
public static final String SIGN_SHA256RSA_ALGORITHMS = "SHA256WithRSA"; |
||||
|
||||
public static final String ENCRYPT_TYPE_AES = "AES"; |
||||
|
||||
public static final String APP_ID = "app_id"; |
||||
|
||||
public static final String FORMAT = "format"; |
||||
|
||||
public static final String METHOD = "method"; |
||||
|
||||
public static final String TIMESTAMP = "timestamp"; |
||||
|
||||
public static final String VERSION = "version"; |
||||
|
||||
public static final String SIGN = "sign"; |
||||
|
||||
public static final String ALIPAY_SDK = "alipay_sdk"; |
||||
|
||||
public static final String ACCESS_TOKEN = "auth_token"; |
||||
|
||||
public static final String APP_AUTH_TOKEN = "app_auth_token"; |
||||
|
||||
public static final String TERMINAL_TYPE = "terminal_type"; |
||||
|
||||
public static final String TERMINAL_INFO = "terminal_info"; |
||||
|
||||
public static final String CHARSET = "charset"; |
||||
|
||||
public static final String NOTIFY_URL = "notify_url"; |
||||
|
||||
public static final String RETURN_URL = "return_url"; |
||||
|
||||
public static final String ENCRYPT_TYPE = "encrypt_type"; |
||||
|
||||
//-----===-------///
|
||||
|
||||
public static final String BIZ_CONTENT_KEY = "biz_content"; |
||||
|
||||
/** 默认时间格式 **/ |
||||
public static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; |
||||
|
||||
/** Date默认时区 **/ |
||||
public static final String DATE_TIMEZONE = "GMT+8"; |
||||
|
||||
/** UTF-8字符集 **/ |
||||
public static final String CHARSET_UTF8 = "UTF-8"; |
||||
|
||||
/** GBK字符集 **/ |
||||
public static final String CHARSET_GBK = "GBK"; |
||||
|
||||
/** JSON 应格式 */ |
||||
public static final String FORMAT_JSON = "json"; |
||||
|
||||
/** XML 应格式 */ |
||||
public static final String FORMAT_XML = "xml"; |
||||
|
||||
/** SDK版本号 */ |
||||
public static final String SDK_VERSION = "alipay-sdk-java-3.6.0.ALL"; |
||||
|
||||
public static final String PROD_CODE = "prod_code"; |
||||
|
||||
/** 老版本失败节点 */ |
||||
public static final String ERROR_RESPONSE = "error_response"; |
||||
|
||||
/** 新版本节点后缀 */ |
||||
public static final String RESPONSE_SUFFIX = "_response"; |
||||
|
||||
/** 加密后XML返回报文的节点名字 */ |
||||
public static final String RESPONSE_XML_ENCRYPT_NODE_NAME = "response_encrypted"; |
||||
|
||||
/** 批量请求id **/ |
||||
public static final String BATCH_REQUEST_ID = "batch_request_id"; |
||||
|
||||
} |
@ -0,0 +1,616 @@ |
||||
/** |
||||
* Alipay.com Inc. |
||||
* Copyright (c) 2004-2012 All Rights Reserved. |
||||
*/ |
||||
package com.gitee.sop.gatewaycommon.validate.alipay; |
||||
|
||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum; |
||||
import org.apache.commons.codec.binary.Base64; |
||||
|
||||
import javax.crypto.Cipher; |
||||
import java.io.ByteArrayInputStream; |
||||
import java.io.ByteArrayOutputStream; |
||||
import java.io.InputStream; |
||||
import java.io.InputStreamReader; |
||||
import java.io.StringWriter; |
||||
import java.security.KeyFactory; |
||||
import java.security.PrivateKey; |
||||
import java.security.PublicKey; |
||||
import java.security.spec.InvalidKeySpecException; |
||||
import java.security.spec.PKCS8EncodedKeySpec; |
||||
import java.security.spec.X509EncodedKeySpec; |
||||
import java.util.ArrayList; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* @author runzhi |
||||
*/ |
||||
public class AlipaySignature { |
||||
|
||||
/** |
||||
* RSA最大加密明文大小 |
||||
*/ |
||||
private static final int MAX_ENCRYPT_BLOCK = 117; |
||||
|
||||
/** |
||||
* RSA最大解密密文大小 |
||||
*/ |
||||
private static final int MAX_DECRYPT_BLOCK = 128; |
||||
|
||||
|
||||
/** |
||||
* @param sortedParams |
||||
* @return |
||||
*/ |
||||
public static String getSignContent(Map<String, Object> sortedParams) { |
||||
StringBuffer content = new StringBuffer(); |
||||
List<String> keys = new ArrayList<String>(sortedParams.keySet()); |
||||
Collections.sort(keys); |
||||
int index = 0; |
||||
for (int i = 0; i < keys.size(); i++) { |
||||
String key = keys.get(i); |
||||
String value = String.valueOf(sortedParams.get(key)); |
||||
if (StringUtils.areNotEmpty(key, value)) { |
||||
content.append((index == 0 ? "" : "&") + key + "=" + value); |
||||
index++; |
||||
} |
||||
} |
||||
return content.toString(); |
||||
} |
||||
|
||||
/** |
||||
* rsa内容签名 |
||||
* |
||||
* @param content |
||||
* @param publicKey |
||||
* @param charset |
||||
* @return |
||||
*/ |
||||
public static String rsaSign(String content, String publicKey, String charset, |
||||
String signType) { |
||||
|
||||
if (AlipayConstants.SIGN_TYPE_RSA.equals(signType)) { |
||||
|
||||
return rsaSign(content, publicKey, charset); |
||||
} else if (AlipayConstants.SIGN_TYPE_RSA2.equals(signType)) { |
||||
|
||||
return rsa256Sign(content, publicKey, charset); |
||||
} else { |
||||
throw ErrorEnum.ISV_INVALID_SIGNATURE_TYPE.getErrorMeta().getException(); |
||||
// throw new AlipayApiException("Sign Type is Not Support : signType=" + signType);
|
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* sha256WithRsa 加签 |
||||
* |
||||
* @param content |
||||
* @param publicKey |
||||
* @param charset |
||||
* @return |
||||
*/ |
||||
public static String rsa256Sign(String content, String publicKey, |
||||
String charset) { |
||||
|
||||
try { |
||||
PrivateKey priKey = getPrivateKeyFromPKCS8(AlipayConstants.SIGN_TYPE_RSA, |
||||
new ByteArrayInputStream(publicKey.getBytes())); |
||||
|
||||
java.security.Signature signature = java.security.Signature |
||||
.getInstance(AlipayConstants.SIGN_SHA256RSA_ALGORITHMS); |
||||
|
||||
signature.initSign(priKey); |
||||
|
||||
if (StringUtils.isEmpty(charset)) { |
||||
signature.update(content.getBytes()); |
||||
} else { |
||||
signature.update(content.getBytes(charset)); |
||||
} |
||||
|
||||
byte[] signed = signature.sign(); |
||||
|
||||
return new String(Base64.encodeBase64(signed)); |
||||
} catch (Exception e) { |
||||
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(e); |
||||
// throw new AlipayApiException("RSAcontent = " + content + "; charset = " + charset, e);
|
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* sha1WithRsa 加签 |
||||
* |
||||
* @param content |
||||
* @param publicKey |
||||
* @param charset |
||||
* @return |
||||
*/ |
||||
public static String rsaSign(String content, String publicKey, |
||||
String charset) { |
||||
try { |
||||
PrivateKey priKey = getPrivateKeyFromPKCS8(AlipayConstants.SIGN_TYPE_RSA, |
||||
new ByteArrayInputStream(publicKey.getBytes())); |
||||
|
||||
java.security.Signature signature = java.security.Signature |
||||
.getInstance(AlipayConstants.SIGN_ALGORITHMS); |
||||
|
||||
signature.initSign(priKey); |
||||
|
||||
if (StringUtils.isEmpty(charset)) { |
||||
signature.update(content.getBytes()); |
||||
} else { |
||||
signature.update(content.getBytes(charset)); |
||||
} |
||||
|
||||
byte[] signed = signature.sign(); |
||||
|
||||
return new String(Base64.encodeBase64(signed)); |
||||
} catch (InvalidKeySpecException ie) { |
||||
throw ErrorEnum.ISV_INVALID_SIGNATURE_TYPE.getErrorMeta().getException(ie); |
||||
// throw new AlipayApiException("RSA私钥格式不正确,请检查是否正确配置了PKCS8格式的私钥", ie);
|
||||
} catch (Exception e) { |
||||
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(e); |
||||
// throw new AlipayApiException("RSAcontent = " + content + "; charset = " + charset, e);
|
||||
} |
||||
} |
||||
|
||||
public static String rsaSign(Map<String, Object> params, String publicKey, |
||||
String charset, String signType) { |
||||
String signContent = getSignContent(params); |
||||
|
||||
return rsaSign(signContent, publicKey, charset, signType); |
||||
|
||||
} |
||||
|
||||
public static PrivateKey getPrivateKeyFromPKCS8(String algorithm, |
||||
InputStream ins) throws Exception { |
||||
if (ins == null || StringUtils.isEmpty(algorithm)) { |
||||
return null; |
||||
} |
||||
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(algorithm); |
||||
|
||||
byte[] encodedKey = StreamUtil.readText(ins, "UTF-8").getBytes(); |
||||
|
||||
encodedKey = Base64.decodeBase64(encodedKey); |
||||
|
||||
return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodedKey)); |
||||
} |
||||
|
||||
public static String getSignCheckContentV1(Map<String, String> params) { |
||||
if (params == null) { |
||||
return null; |
||||
} |
||||
|
||||
params.remove("sign"); |
||||
params.remove("sign_type"); |
||||
|
||||
StringBuffer content = new StringBuffer(); |
||||
List<String> keys = new ArrayList<String>(params.keySet()); |
||||
Collections.sort(keys); |
||||
|
||||
for (int i = 0; i < keys.size(); i++) { |
||||
String key = keys.get(i); |
||||
String value = params.get(key); |
||||
content.append((i == 0 ? "" : "&") + key + "=" + value); |
||||
} |
||||
|
||||
return content.toString(); |
||||
} |
||||
|
||||
public static String getSignCheckContentV2(Map<String, ?> params) { |
||||
if (params == null) { |
||||
return null; |
||||
} |
||||
|
||||
params.remove("sign"); |
||||
|
||||
StringBuffer content = new StringBuffer(); |
||||
List<String> keys = new ArrayList<String>(params.keySet()); |
||||
Collections.sort(keys); |
||||
|
||||
for (int i = 0; i < keys.size(); i++) { |
||||
String key = keys.get(i); |
||||
String value = String.valueOf(params.get(key)); |
||||
content.append((i == 0 ? "" : "&") + key + "=" + value); |
||||
} |
||||
|
||||
return content.toString(); |
||||
} |
||||
|
||||
public static boolean rsaCheckV1(Map<String, String> params, String publicKey, |
||||
String charset) { |
||||
String sign = params.get("sign"); |
||||
String content = getSignCheckContentV1(params); |
||||
|
||||
return rsaCheckContent(content, sign, publicKey, charset); |
||||
} |
||||
|
||||
public static boolean rsaCheckV1(Map<String, String> params, String publicKey, |
||||
String charset, String signType) { |
||||
String sign = params.get("sign"); |
||||
String content = getSignCheckContentV1(params); |
||||
|
||||
return rsaCheck(content, sign, publicKey, charset, signType); |
||||
} |
||||
|
||||
public static boolean rsaCheckV2(Map<String, String> params, String publicKey, |
||||
String charset) { |
||||
String sign = params.get("sign"); |
||||
String content = getSignCheckContentV2(params); |
||||
|
||||
return rsaCheckContent(content, sign, publicKey, charset); |
||||
} |
||||
|
||||
public static boolean rsaCheckV2(Map<String, ?> params, String publicKey, |
||||
String charset, String signType) { |
||||
String sign = String.valueOf(params.get("sign")); |
||||
String content = getSignCheckContentV2(params); |
||||
|
||||
return rsaCheck(content, sign, publicKey, charset, signType); |
||||
} |
||||
|
||||
public static boolean rsaCheck(String content, String sign, String publicKey, String charset, |
||||
String signType) { |
||||
|
||||
if (AlipayConstants.SIGN_TYPE_RSA.equals(signType)) { |
||||
|
||||
return rsaCheckContent(content, sign, publicKey, charset); |
||||
|
||||
} else if (AlipayConstants.SIGN_TYPE_RSA2.equals(signType)) { |
||||
|
||||
return rsa256CheckContent(content, sign, publicKey, charset); |
||||
|
||||
} else { |
||||
throw ErrorEnum.ISV_INVALID_SIGNATURE_TYPE.getErrorMeta().getException(); |
||||
// throw new AlipayApiException("Sign Type is Not Support : signType=" + signType);
|
||||
} |
||||
|
||||
} |
||||
|
||||
public static boolean rsa256CheckContent(String content, String sign, String publicKey, |
||||
String charset) { |
||||
try { |
||||
PublicKey pubKey = getPublicKeyFromX509("RSA", |
||||
new ByteArrayInputStream(publicKey.getBytes())); |
||||
|
||||
java.security.Signature signature = java.security.Signature |
||||
.getInstance(AlipayConstants.SIGN_SHA256RSA_ALGORITHMS); |
||||
|
||||
signature.initVerify(pubKey); |
||||
|
||||
if (StringUtils.isEmpty(charset)) { |
||||
signature.update(content.getBytes()); |
||||
} else { |
||||
signature.update(content.getBytes(charset)); |
||||
} |
||||
|
||||
return signature.verify(Base64.decodeBase64(sign.getBytes())); |
||||
} catch (Exception e) { |
||||
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(e); |
||||
// throw new AlipayApiException(
|
||||
// "RSAcontent = " + content + ",sign=" + sign + ",charset = " + charset, e);
|
||||
} |
||||
} |
||||
|
||||
public static boolean rsaCheckContent(String content, String sign, String publicKey, |
||||
String charset) { |
||||
try { |
||||
PublicKey pubKey = getPublicKeyFromX509("RSA", |
||||
new ByteArrayInputStream(publicKey.getBytes())); |
||||
|
||||
java.security.Signature signature = java.security.Signature |
||||
.getInstance(AlipayConstants.SIGN_ALGORITHMS); |
||||
|
||||
signature.initVerify(pubKey); |
||||
|
||||
if (StringUtils.isEmpty(charset)) { |
||||
signature.update(content.getBytes()); |
||||
} else { |
||||
signature.update(content.getBytes(charset)); |
||||
} |
||||
|
||||
return signature.verify(Base64.decodeBase64(sign.getBytes())); |
||||
} catch (Exception e) { |
||||
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(e); |
||||
// throw new AlipayApiException(
|
||||
// "RSAcontent = " + content + ",sign=" + sign + ",charset = " + charset, e);
|
||||
} |
||||
} |
||||
|
||||
public static PublicKey getPublicKeyFromX509(String algorithm, |
||||
InputStream ins) throws Exception { |
||||
KeyFactory keyFactory = KeyFactory.getInstance(algorithm); |
||||
|
||||
StringWriter writer = new StringWriter(); |
||||
StreamUtil.io(new InputStreamReader(ins), writer); |
||||
|
||||
byte[] encodedKey = writer.toString().getBytes(); |
||||
|
||||
encodedKey = Base64.decodeBase64(encodedKey); |
||||
|
||||
return keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey)); |
||||
} |
||||
|
||||
/** |
||||
* 验签并解密 |
||||
* <p> |
||||
* <b>目前适用于公众号</b><br> |
||||
* params参数示例: |
||||
* <br>{ |
||||
* <br>biz_content=M0qGiGz+8kIpxe8aF4geWJdBn0aBTuJRQItLHo9R7o5JGhpic/MIUjvXo2BLB++BbkSq2OsJCEQFDZ0zK5AJYwvBgeRX30gvEj6eXqXRt16/IkB9HzAccEqKmRHrZJ7PjQWE0KfvDAHsJqFIeMvEYk1Zei2QkwSQPlso7K0oheo/iT+HYE8aTATnkqD/ByD9iNDtGg38pCa2xnnns63abKsKoV8h0DfHWgPH62urGY7Pye3r9FCOXA2Ykm8X4/Bl1bWFN/PFCEJHWe/HXj8KJKjWMO6ttsoV0xRGfeyUO8agu6t587Dl5ux5zD/s8Lbg5QXygaOwo3Fz1G8EqmGhi4+soEIQb8DBYanQOS3X+m46tVqBGMw8Oe+hsyIMpsjwF4HaPKMr37zpW3fe7xOMuimbZ0wq53YP/jhQv6XWodjT3mL0H5ACqcsSn727B5ztquzCPiwrqyjUHjJQQefFTzOse8snaWNQTUsQS7aLsHq0FveGpSBYORyA90qPdiTjXIkVP7mAiYiAIWW9pCEC7F3XtViKTZ8FRMM9ySicfuAlf3jtap6v2KPMtQv70X+hlmzO/IXB6W0Ep8DovkF5rB4r/BJYJLw/6AS0LZM9w5JfnAZhfGM2rKzpfNsgpOgEZS1WleG4I2hoQC0nxg9IcP0Hs+nWIPkEUcYNaiXqeBc=, |
||||
* <br>sign=rlqgA8O+RzHBVYLyHmrbODVSANWPXf3pSrr82OCO/bm3upZiXSYrX5fZr6UBmG6BZRAydEyTIguEW6VRuAKjnaO/sOiR9BsSrOdXbD5Rhos/Xt7/mGUWbTOt/F+3W0/XLuDNmuYg1yIC/6hzkg44kgtdSTsQbOC9gWM7ayB4J4c=, |
||||
* sign_type=RSA, |
||||
* <br>charset=UTF-8 |
||||
* <br>} |
||||
* </p> |
||||
* |
||||
* @param params |
||||
* @param alipayPublicKey 支付宝公钥 |
||||
* @param cusPrivateKey 商户私钥 |
||||
* @param isCheckSign 是否验签 |
||||
* @param isDecrypt 是否解密 |
||||
* @return 解密后明文,验签失败则异常抛出 |
||||
*/ |
||||
public static String checkSignAndDecrypt(Map<String, String> params, String alipayPublicKey, |
||||
String cusPrivateKey, boolean isCheckSign, |
||||
boolean isDecrypt) { |
||||
String charset = params.get("charset"); |
||||
String bizContent = params.get("biz_content"); |
||||
if (isCheckSign) { |
||||
if (!rsaCheckV2(params, alipayPublicKey, charset)) { |
||||
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(); |
||||
// throw new AlipayApiException("rsaCheck failure:rsaParams=" + params);
|
||||
} |
||||
} |
||||
|
||||
if (isDecrypt) { |
||||
return rsaDecrypt(bizContent, cusPrivateKey, charset); |
||||
} |
||||
|
||||
return bizContent; |
||||
} |
||||
|
||||
/** |
||||
* 验签并解密 |
||||
* <p> |
||||
* <b>目前适用于公众号</b><br> |
||||
* params参数示例: |
||||
* <br>{ |
||||
* <br>biz_content=M0qGiGz+8kIpxe8aF4geWJdBn0aBTuJRQItLHo9R7o5JGhpic/MIUjvXo2BLB++BbkSq2OsJCEQFDZ0zK5AJYwvBgeRX30gvEj6eXqXRt16/IkB9HzAccEqKmRHrZJ7PjQWE0KfvDAHsJqFIeMvEYk1Zei2QkwSQPlso7K0oheo/iT+HYE8aTATnkqD/ByD9iNDtGg38pCa2xnnns63abKsKoV8h0DfHWgPH62urGY7Pye3r9FCOXA2Ykm8X4/Bl1bWFN/PFCEJHWe/HXj8KJKjWMO6ttsoV0xRGfeyUO8agu6t587Dl5ux5zD/s8Lbg5QXygaOwo3Fz1G8EqmGhi4+soEIQb8DBYanQOS3X+m46tVqBGMw8Oe+hsyIMpsjwF4HaPKMr37zpW3fe7xOMuimbZ0wq53YP/jhQv6XWodjT3mL0H5ACqcsSn727B5ztquzCPiwrqyjUHjJQQefFTzOse8snaWNQTUsQS7aLsHq0FveGpSBYORyA90qPdiTjXIkVP7mAiYiAIWW9pCEC7F3XtViKTZ8FRMM9ySicfuAlf3jtap6v2KPMtQv70X+hlmzO/IXB6W0Ep8DovkF5rB4r/BJYJLw/6AS0LZM9w5JfnAZhfGM2rKzpfNsgpOgEZS1WleG4I2hoQC0nxg9IcP0Hs+nWIPkEUcYNaiXqeBc=, |
||||
* <br>sign=rlqgA8O+RzHBVYLyHmrbODVSANWPXf3pSrr82OCO/bm3upZiXSYrX5fZr6UBmG6BZRAydEyTIguEW6VRuAKjnaO/sOiR9BsSrOdXbD5Rhos/Xt7/mGUWbTOt/F+3W0/XLuDNmuYg1yIC/6hzkg44kgtdSTsQbOC9gWM7ayB4J4c=, |
||||
* sign_type=RSA, |
||||
* <br>charset=UTF-8 |
||||
* <br>} |
||||
* </p> |
||||
* |
||||
* @param params |
||||
* @param alipayPublicKey 支付宝公钥 |
||||
* @param cusPrivateKey 商户私钥 |
||||
* @param isCheckSign 是否验签 |
||||
* @param isDecrypt 是否解密 |
||||
* @return 解密后明文,验签失败则异常抛出 |
||||
*/ |
||||
public static String checkSignAndDecrypt(Map<String, String> params, String alipayPublicKey, |
||||
String cusPrivateKey, boolean isCheckSign, |
||||
boolean isDecrypt, String signType) { |
||||
String charset = params.get("charset"); |
||||
String bizContent = params.get("biz_content"); |
||||
if (isCheckSign) { |
||||
if (!rsaCheckV2(params, alipayPublicKey, charset, signType)) { |
||||
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(); |
||||
// throw new AlipayApiException("rsaCheck failure:rsaParams=" + params);
|
||||
} |
||||
} |
||||
|
||||
if (isDecrypt) { |
||||
return rsaDecrypt(bizContent, cusPrivateKey, charset); |
||||
} |
||||
|
||||
return bizContent; |
||||
} |
||||
|
||||
/** |
||||
* 加密并签名<br> |
||||
* <b>目前适用于公众号</b> |
||||
* |
||||
* @param bizContent 待加密、签名内容 |
||||
* @param alipayPublicKey 支付宝公钥 |
||||
* @param cusPrivateKey 商户私钥 |
||||
* @param charset 字符集,如UTF-8, GBK, GB2312 |
||||
* @param isEncrypt 是否加密,true-加密 false-不加密 |
||||
* @param isSign 是否签名,true-签名 false-不签名 |
||||
* @return 加密、签名后xml内容字符串 |
||||
* <p> |
||||
* 返回示例: |
||||
* <alipay> |
||||
* <response>密文</response> |
||||
* <encryption_type>RSA</encryption_type> |
||||
* <sign>sign</sign> |
||||
* <sign_type>RSA</sign_type> |
||||
* </alipay> |
||||
* </p> |
||||
*/ |
||||
public static String encryptAndSign(String bizContent, String alipayPublicKey, |
||||
String cusPrivateKey, String charset, boolean isEncrypt, |
||||
boolean isSign) { |
||||
StringBuilder sb = new StringBuilder(); |
||||
if (StringUtils.isEmpty(charset)) { |
||||
charset = AlipayConstants.CHARSET_GBK; |
||||
} |
||||
sb.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>"); |
||||
if (isEncrypt) {// 加密
|
||||
sb.append("<alipay>"); |
||||
String encrypted = rsaEncrypt(bizContent, alipayPublicKey, charset); |
||||
sb.append("<response>" + encrypted + "</response>"); |
||||
sb.append("<encryption_type>RSA</encryption_type>"); |
||||
if (isSign) { |
||||
String sign = rsaSign(encrypted, cusPrivateKey, charset); |
||||
sb.append("<sign>" + sign + "</sign>"); |
||||
sb.append("<sign_type>RSA</sign_type>"); |
||||
} |
||||
sb.append("</alipay>"); |
||||
} else if (isSign) {// 不加密,但需要签名
|
||||
sb.append("<alipay>"); |
||||
sb.append("<response>" + bizContent + "</response>"); |
||||
String sign = rsaSign(bizContent, cusPrivateKey, charset); |
||||
sb.append("<sign>" + sign + "</sign>"); |
||||
sb.append("<sign_type>RSA</sign_type>"); |
||||
sb.append("</alipay>"); |
||||
} else {// 不加密,不加签
|
||||
sb.append(bizContent); |
||||
} |
||||
return sb.toString(); |
||||
} |
||||
|
||||
/** |
||||
* 加密并签名<br> |
||||
* <b>目前适用于公众号</b> |
||||
* |
||||
* @param bizContent 待加密、签名内容 |
||||
* @param alipayPublicKey 支付宝公钥 |
||||
* @param cusPrivateKey 商户私钥 |
||||
* @param charset 字符集,如UTF-8, GBK, GB2312 |
||||
* @param isEncrypt 是否加密,true-加密 false-不加密 |
||||
* @param isSign 是否签名,true-签名 false-不签名 |
||||
* @return 加密、签名后xml内容字符串 |
||||
* <p> |
||||
* 返回示例: |
||||
* <alipay> |
||||
* <response>密文</response> |
||||
* <encryption_type>RSA</encryption_type> |
||||
* <sign>sign</sign> |
||||
* <sign_type>RSA</sign_type> |
||||
* </alipay> |
||||
* </p> |
||||
*/ |
||||
public static String encryptAndSign(String bizContent, String alipayPublicKey, |
||||
String cusPrivateKey, String charset, boolean isEncrypt, |
||||
boolean isSign, String signType) { |
||||
StringBuilder sb = new StringBuilder(); |
||||
if (StringUtils.isEmpty(charset)) { |
||||
charset = AlipayConstants.CHARSET_GBK; |
||||
} |
||||
sb.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>"); |
||||
if (isEncrypt) {// 加密
|
||||
sb.append("<alipay>"); |
||||
String encrypted = rsaEncrypt(bizContent, alipayPublicKey, charset); |
||||
sb.append("<response>" + encrypted + "</response>"); |
||||
sb.append("<encryption_type>RSA</encryption_type>"); |
||||
if (isSign) { |
||||
String sign = rsaSign(encrypted, cusPrivateKey, charset, signType); |
||||
sb.append("<sign>" + sign + "</sign>"); |
||||
sb.append("<sign_type>"); |
||||
sb.append(signType); |
||||
sb.append("</sign_type>"); |
||||
} |
||||
sb.append("</alipay>"); |
||||
} else if (isSign) {// 不加密,但需要签名
|
||||
sb.append("<alipay>"); |
||||
sb.append("<response>" + bizContent + "</response>"); |
||||
String sign = rsaSign(bizContent, cusPrivateKey, charset, signType); |
||||
sb.append("<sign>" + sign + "</sign>"); |
||||
sb.append("<sign_type>"); |
||||
sb.append(signType); |
||||
sb.append("</sign_type>"); |
||||
sb.append("</alipay>"); |
||||
} else {// 不加密,不加签
|
||||
sb.append(bizContent); |
||||
} |
||||
return sb.toString(); |
||||
} |
||||
|
||||
/** |
||||
* 公钥加密 |
||||
* |
||||
* @param content 待加密内容 |
||||
* @param publicKey 公钥 |
||||
* @param charset 字符集,如UTF-8, GBK, GB2312 |
||||
* @return 密文内容 |
||||
*/ |
||||
public static String rsaEncrypt(String content, String publicKey, |
||||
String charset) { |
||||
try { |
||||
PublicKey pubKey = getPublicKeyFromX509(AlipayConstants.SIGN_TYPE_RSA, |
||||
new ByteArrayInputStream(publicKey.getBytes())); |
||||
Cipher cipher = Cipher.getInstance(AlipayConstants.SIGN_TYPE_RSA); |
||||
cipher.init(Cipher.ENCRYPT_MODE, pubKey); |
||||
byte[] data = StringUtils.isEmpty(charset) ? content.getBytes() |
||||
: content.getBytes(charset); |
||||
int inputLen = data.length; |
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(); |
||||
int offSet = 0; |
||||
byte[] cache; |
||||
int i = 0; |
||||
// 对数据分段加密
|
||||
while (inputLen - offSet > 0) { |
||||
if (inputLen - offSet > MAX_ENCRYPT_BLOCK) { |
||||
cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK); |
||||
} else { |
||||
cache = cipher.doFinal(data, offSet, inputLen - offSet); |
||||
} |
||||
out.write(cache, 0, cache.length); |
||||
i++; |
||||
offSet = i * MAX_ENCRYPT_BLOCK; |
||||
} |
||||
byte[] encryptedData = Base64.encodeBase64(out.toByteArray()); |
||||
out.close(); |
||||
|
||||
return StringUtils.isEmpty(charset) ? new String(encryptedData) |
||||
: new String(encryptedData, charset); |
||||
} catch (Exception e) { |
||||
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(e); |
||||
// throw new AlipayApiException("EncryptContent = " + content + ",charset = " + charset,
|
||||
// e);
|
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 私钥解密 |
||||
* |
||||
* @param content 待解密内容 |
||||
* @param publicKey 私钥 |
||||
* @param charset 字符集,如UTF-8, GBK, GB2312 |
||||
* @return 明文内容 |
||||
*/ |
||||
public static String rsaDecrypt(String content, String publicKey, |
||||
String charset) { |
||||
try { |
||||
PrivateKey priKey = getPrivateKeyFromPKCS8(AlipayConstants.SIGN_TYPE_RSA, |
||||
new ByteArrayInputStream(publicKey.getBytes())); |
||||
Cipher cipher = Cipher.getInstance(AlipayConstants.SIGN_TYPE_RSA); |
||||
cipher.init(Cipher.DECRYPT_MODE, priKey); |
||||
byte[] encryptedData = StringUtils.isEmpty(charset) |
||||
? Base64.decodeBase64(content.getBytes()) |
||||
: Base64.decodeBase64(content.getBytes(charset)); |
||||
int inputLen = encryptedData.length; |
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(); |
||||
int offSet = 0; |
||||
byte[] cache; |
||||
int i = 0; |
||||
// 对数据分段解密
|
||||
while (inputLen - offSet > 0) { |
||||
if (inputLen - offSet > MAX_DECRYPT_BLOCK) { |
||||
cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK); |
||||
} else { |
||||
cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet); |
||||
} |
||||
out.write(cache, 0, cache.length); |
||||
i++; |
||||
offSet = i * MAX_DECRYPT_BLOCK; |
||||
} |
||||
byte[] decryptedData = out.toByteArray(); |
||||
out.close(); |
||||
|
||||
return StringUtils.isEmpty(charset) ? new String(decryptedData) |
||||
: new String(decryptedData, charset); |
||||
} catch (Exception e) { |
||||
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(e); |
||||
// throw new AlipayApiException("EncodeContent = " + content + ",charset = " + charset, e);
|
||||
} |
||||
} |
||||
|
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue