diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a2a3040
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,31 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**
+!**/src/test/**
+
+### 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/
+
+### VS Code ###
+.vscode/
diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java
new file mode 100644
index 0000000..a45eb6b
--- /dev/null
+++ b/.mvn/wrapper/MavenWrapperDownloader.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2007-present the original author or authors.
+ *
+ * Licensed 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
+ *
+ * https://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.
+ */
+
+import java.net.*;
+import java.io.*;
+import java.nio.channels.*;
+import java.util.Properties;
+
+public class MavenWrapperDownloader {
+
+ private static final String WRAPPER_VERSION = "0.5.6";
+ /**
+ * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
+ */
+ private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
+
+ /**
+ * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
+ * use instead of the default one.
+ */
+ private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
+ ".mvn/wrapper/maven-wrapper.properties";
+
+ /**
+ * Path where the maven-wrapper.jar will be saved to.
+ */
+ private static final String MAVEN_WRAPPER_JAR_PATH =
+ ".mvn/wrapper/maven-wrapper.jar";
+
+ /**
+ * Name of the property which should be used to override the default download url for the wrapper.
+ */
+ private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+
+ public static void main(String args[]) {
+ System.out.println("- Downloader started");
+ File baseDirectory = new File(args[0]);
+ System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
+
+ // If the maven-wrapper.properties exists, read it and check if it contains a custom
+ // wrapperUrl parameter.
+ File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
+ String url = DEFAULT_DOWNLOAD_URL;
+ if (mavenWrapperPropertyFile.exists()) {
+ FileInputStream mavenWrapperPropertyFileInputStream = null;
+ try {
+ mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
+ Properties mavenWrapperProperties = new Properties();
+ mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
+ url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
+ } catch (IOException e) {
+ System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
+ } finally {
+ try {
+ if (mavenWrapperPropertyFileInputStream != null) {
+ mavenWrapperPropertyFileInputStream.close();
+ }
+ } catch (IOException e) {
+ // Ignore ...
+ }
+ }
+ }
+ System.out.println("- Downloading from: " + url);
+
+ File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
+ if (!outputFile.getParentFile().exists()) {
+ if (!outputFile.getParentFile().mkdirs()) {
+ System.out.println(
+ "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
+ }
+ }
+ System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
+ try {
+ downloadFileFromURL(url, outputFile);
+ System.out.println("Done");
+ System.exit(0);
+ } catch (Throwable e) {
+ System.out.println("- Error downloading");
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ private static void downloadFileFromURL(String urlString, File destination) throws Exception {
+ if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
+ String username = System.getenv("MVNW_USERNAME");
+ char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
+ Authenticator.setDefault(new Authenticator() {
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication(username, password);
+ }
+ });
+ }
+ URL website = new URL(urlString);
+ ReadableByteChannel rbc;
+ rbc = Channels.newChannel(website.openStream());
+ FileOutputStream fos = new FileOutputStream(destination);
+ fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+ fos.close();
+ rbc.close();
+ }
+
+}
diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 0000000..2cc7d4a
Binary files /dev/null and b/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 0000000..642d572
--- /dev/null
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,2 @@
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
diff --git a/mvnw b/mvnw
new file mode 100644
index 0000000..a16b543
--- /dev/null
+++ b/mvnw
@@ -0,0 +1,310 @@
+#!/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
+#
+# https://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.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven 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)`"
+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
+ if [ -n "$MVNW_REPOURL" ]; then
+ jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ else
+ jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ fi
+ 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 $cygwin; then
+ wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+ fi
+
+ if command -v wget > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found wget ... using wget"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget "$jarUrl" -O "$wrapperJarPath"
+ else
+ wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
+ fi
+ elif command -v curl > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found curl ... using curl"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl -o "$wrapperJarPath" "$jarUrl" -f
+ else
+ curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+ fi
+
+ else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Falling back to using Java to download"
+ fi
+ javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaClass=`cygpath --path --windows "$javaClass"`
+ fi
+ 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
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+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 "$@"
diff --git a/mvnw.cmd b/mvnw.cmd
new file mode 100644
index 0000000..c8d4337
--- /dev/null
+++ b/mvnw.cmd
@@ -0,0 +1,182 @@
+@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 https://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 Maven 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 keystroke 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 by 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.5.6/maven-wrapper-0.5.6.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% (
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Found %WRAPPER_JAR%
+ )
+) else (
+ if not "%MVNW_REPOURL%" == "" (
+ SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %DOWNLOAD_URL%
+ )
+
+ powershell -Command "&{"^
+ "$webclient = new-object System.Net.WebClient;"^
+ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+ "}"^
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%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%
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..c36beed
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,228 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.2.4.RELEASE
+
+
+ com.codesdream
+ ase
+ 0.0.1-SNAPSHOT
+ ase
+ All Staff Education
+
+
+ 1.8
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.boot
+ spring-boot-starter-thymeleaf
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ mysql
+ mysql-connector-java
+ runtime
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+
+
+
+
+
+ org.springframework.security
+ spring-security-test
+ test
+
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+
+ commons-codec
+ commons-codec
+ 1.11
+
+
+
+ org.springframework
+ spring-test
+ 5.2.3.RELEASE
+ compile
+
+
+
+ org.springframework.boot
+ spring-boot-test
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ 5.5.2
+ compile
+
+
+
+ junit
+ junit
+
+
+
+ org.springframework.boot
+ spring-boot-test-autoconfigure
+
+
+
+ org.hsqldb
+ hsqldb
+ runtime
+
+
+
+ org.projectlombok
+ lombok
+ 1.16.10
+
+
+
+ org.apache.poi
+ poi
+ 3.14
+
+
+
+
+ org.apache.poi
+ poi-ooxml
+ 3.14
+
+
+
+ com.alibaba
+ fastjson
+ 1.2.61
+
+
+
+ com.alibaba
+ fastjson
+ 1.1.71.android
+
+
+
+ org.mariadb.jdbc
+ mariadb-java-client
+ 2.5.4
+
+
+
+
+ com.h2database
+ h2
+ runtime
+
+
+
+
+ com.google.zxing
+ core
+ 3.2.0
+
+
+
+ com.google.zxing
+ javase
+ 3.2.0
+
+
+
+
+ io.springfox
+ springfox-swagger2
+ 2.9.2
+
+
+
+ io.springfox
+ springfox-swagger-ui
+ 2.9.2
+
+
+
+ com.github.java-json-tools
+ json-patch
+ 1.12
+
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ true
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ 1.8
+
+
+ ${JAVA_HOME}/jre/lib/rt.jar
+
+
+
+
+
+
+
+
+ AlibabaMaven
+ Maven Aliyun Mirror
+ http://maven.aliyun.com/nexus/content/repositories/central/
+
+ true
+
+
+ false
+
+
+
+
+
diff --git a/src/main/java/com/codesdream/ase/AseApplication.java b/src/main/java/com/codesdream/ase/AseApplication.java
new file mode 100644
index 0000000..9130640
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/AseApplication.java
@@ -0,0 +1,13 @@
+package com.codesdream.ase;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class AseApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(AseApplication.class, args);
+ }
+
+}
diff --git a/src/main/java/com/codesdream/ase/component/ASESpringUtil.java b/src/main/java/com/codesdream/ase/component/ASESpringUtil.java
new file mode 100644
index 0000000..e4b44cf
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/ASESpringUtil.java
@@ -0,0 +1,22 @@
+package com.codesdream.ase.component;
+
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+
+/**
+ * 手动获得Bean的工具类
+ */
+@Component
+public class ASESpringUtil {
+ @Resource
+ private ApplicationContext applicationContext;
+
+ public T getBean(Class tClass){
+ return applicationContext.getBean(tClass);
+ }
+
+}
diff --git a/src/main/java/com/codesdream/ase/component/activity/ActivityConverter.java b/src/main/java/com/codesdream/ase/component/activity/ActivityConverter.java
new file mode 100644
index 0000000..5d2783f
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/activity/ActivityConverter.java
@@ -0,0 +1,178 @@
+package com.codesdream.ase.component.activity;
+
+import com.alibaba.fastjson.JSONObject;
+import com.codesdream.ase.exception.innerservererror.DataInvalidFormatException;
+import com.codesdream.ase.model.activity.Activity;
+import com.codesdream.ase.model.activity.Attendance;
+import com.codesdream.ase.model.activity.Period;
+import com.codesdream.ase.model.permission.User;
+import com.codesdream.ase.service.ActivityService;
+import com.codesdream.ase.service.AttendanceService;
+import com.codesdream.ase.service.PeriodService;
+import com.codesdream.ase.service.UserService;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+
+/**
+ * JSONObject-Activity转化类
+ */
+@Component
+public class ActivityConverter {
+
+ @Resource
+ UserService userService;
+
+ @Resource
+ PeriodService periodService;
+
+ @Resource
+ AttendanceService attendanceService;
+
+ /**
+ * @param json 一个Optional的json对象,用以转化为Activity对象,此过程中进行值的合法校验
+ * @return 一个可以被直接存储在数据库中的合法的Activity对象
+ */
+ public Activity convertToActivity(Optional json) {
+ if (!json.isPresent()) {
+ throw new NullPointerException();
+ }
+ Activity activity = new Activity();
+ JSONObject jsonObject = json.get();
+
+ //设置活动创建人
+ int userId = (int) jsonObject.get("creator");
+ Optional creator = userService.findUserById(userId);
+ activity.setCreator(creator.get());
+
+ //设置参与人员
+ List participateGroupFromJson = (List) jsonObject.get("participate-group");
+ Set participateGroup = new HashSet<>();
+ for (int id : participateGroupFromJson) {
+ Optional user = userService.findUserById(id);
+ participateGroup.add(user.get());
+ }
+ activity.setParticipateGroup(participateGroup);
+
+ //设置活动标题
+ String title = (String) jsonObject.get("title");
+ activity.setTitle(title);
+
+ //设置主要负责人
+ int chiefManagerId = (int) jsonObject.get("chief-manager");
+ Optional chiefManager = userService.findUserById(chiefManagerId);
+ activity.setChiefManager(chiefManager.get());
+
+ //设置次要负责人
+ List assistManagersFromJSON = (List) jsonObject.get("assist-managers");
+ Set assistManager = new HashSet<>();
+ for (int id : assistManagersFromJSON) {
+ Optional user = userService.findUserById(id);
+ assistManager.add(user.get());
+ }
+ activity.setAssistManagers(assistManager);
+
+ //设置活动类型
+ String type = (String) jsonObject.get("type");
+ activity.setType(type);
+
+ //设置
+ String startTimeFromJSON = (String) jsonObject.get("start-time");
+ String endTimeFromJSON = (String) jsonObject.get("end-time");
+ LocalDateTime startTime = LocalDateTime.parse(startTimeFromJSON, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+ LocalDateTime endTime = LocalDateTime.parse(endTimeFromJSON, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+ Period period = new Period(startTime, endTime);
+ period.setEnabled(false);
+ period = periodService.save(period);
+ activity.setPlanPeriod(period);
+
+ String cycle = (String) jsonObject.get("cycle");
+ activity.setCycle(cycle);
+
+ String description = (String) jsonObject.get("description");
+ activity.setDescription(description);
+
+ List signGroupFromJSON = (List) jsonObject.get("sign-group");
+ Set signGroup = new HashSet<>();
+ for (int id : signGroupFromJSON) {
+ Optional user = userService.findUserById(id);
+ signGroup.add(user.get());
+ }
+ activity.setSignGroup(signGroup);
+
+ List informGroupFromJSON = (List) jsonObject.get("inform-group");
+ if (informGroupFromJSON == null) {
+ participateGroupFromJson.removeAll(signGroupFromJSON);
+ participateGroupFromJson.addAll(signGroupFromJSON);
+ informGroupFromJSON = participateGroupFromJson;
+ }
+ Set informGroup = new HashSet<>();
+ for (int id : informGroupFromJSON) {
+ Optional user = userService.findUserById(id);
+ informGroup.add(user.get());
+ }
+ activity.setInformGroup(informGroup);
+
+ List visibleGroupFromJSON = (List) jsonObject.get("visible-group");
+ Set visibleGroup = new HashSet<>();
+ for (int id : visibleGroupFromJSON) {
+ Optional user = userService.findUserById(id);
+ visibleGroup.add(user.get());
+ }
+ activity.setVisibleGroup(informGroup);
+
+ String remindTimeFromJSON = (String) jsonObject.get("remind-time");
+ String numStr = remindTimeFromJSON.substring(0, remindTimeFromJSON.length() - 1);
+ int num = Integer.parseInt(numStr);
+ char unit = remindTimeFromJSON.charAt(remindTimeFromJSON.length() - 1);
+ switch (unit) {
+ case 'w': {
+ activity.setRemindTime(activity.getPlanPeriod().getStartTime().minusWeeks(num));
+ break;
+ }
+ case 'd': {
+ activity.setRemindTime(activity.getPlanPeriod().getStartTime().minusDays(num));
+ break;
+ }
+ case 'm': {
+ activity.setRemindTime(activity.getPlanPeriod().getStartTime().minusMinutes(num));
+ break;
+ }
+ case 'h': {
+ activity.setRemindTime(activity.getPlanPeriod().getStartTime().minusHours(num));
+ break;
+ }
+ case 's': {
+ activity.setRemindTime(activity.getPlanPeriod().getStartTime().minusSeconds(num));
+ }
+ }
+
+ Set periods = new HashSet<>();
+ String[] attendanceTimes = (String[]) jsonObject.get("attendance");
+ boolean attendanceOnLine = (boolean) jsonObject.get("attendance-online");
+ if ((attendanceTimes.length & 1) == 1) {
+ throw new DataInvalidFormatException();
+ }
+ for (int i = 0; i < attendanceTimes.length; i += 2) {
+ LocalDateTime start = LocalDateTime.parse(attendanceTimes[i], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+ LocalDateTime end = LocalDateTime.parse(attendanceTimes[i + 1], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+ Period period1 = new Period(start, end);
+ periods.add(period1);
+ }
+ Attendance attendance = new Attendance();
+ attendance.setClockInPeriods(periods);
+ attendance.setOnline(attendanceOnLine);
+
+ attendance = attendanceService.save(attendance);
+ activity.setAttendance(attendance);
+
+ activity.setOn(false);
+ activity.setOff(false);
+
+ return activity;
+
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/activity/CurrentUserGetter.java b/src/main/java/com/codesdream/ase/component/activity/CurrentUserGetter.java
new file mode 100644
index 0000000..cfebcbc
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/activity/CurrentUserGetter.java
@@ -0,0 +1,26 @@
+package com.codesdream.ase.component.activity;
+
+import com.codesdream.ase.component.ASESpringUtil;
+import com.codesdream.ase.model.permission.User;
+import com.codesdream.ase.repository.permission.UserRepository;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import java.security.Principal;
+import java.util.Optional;
+
+//获取当前用户的用户名
+@Component
+public class CurrentUserGetter {
+ private Optional user;
+ public Optional getCurrentUser(HttpServletRequest request){
+
+ Principal principal = request.getUserPrincipal();
+ String username = principal.getName();
+ ASESpringUtil aseSpringUtil = new ASESpringUtil();
+ UserRepository userRepository = aseSpringUtil.getBean(UserRepository.class);
+ this.user = userRepository.findByUsername(username);
+ return this.user;
+ }
+
+}
diff --git a/src/main/java/com/codesdream/ase/component/activity/FileUtils.java b/src/main/java/com/codesdream/ase/component/activity/FileUtils.java
new file mode 100644
index 0000000..656d3d2
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/activity/FileUtils.java
@@ -0,0 +1,129 @@
+package com.codesdream.ase.component.activity;
+
+import org.springframework.stereotype.Component;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.imageio.ImageIO;
+import javax.imageio.stream.ImageOutputStream;
+import java.awt.image.BufferedImage;
+import java.io.*;
+
+@Component
+public class FileUtils {
+ /**
+ * 得到图片字节流 数组大小
+ */
+ public static byte[] readStream(InputStream inStream) throws Exception {
+ ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+ byte[] buffer = new byte[1024];
+ int len = -1;
+ while ((len = inStream.read(buffer)) != -1) {
+ outStream.write(buffer, 0, len);
+ }
+ outStream.close();
+ inStream.close();
+ return outStream.toByteArray();
+ }
+
+ /**
+ * 将文件转换成Byte数组
+ *
+ * @param file
+ * @return
+ */
+ public static byte[] getBytesByFile(File file) {
+ try {
+ FileInputStream fis = new FileInputStream(file);
+ ByteArrayOutputStream bos = new ByteArrayOutputStream(1000);
+ byte[] b = new byte[1000];
+ int n;
+ while ((n = fis.read(b)) != -1) {
+ bos.write(b, 0, n);
+ }
+ fis.close();
+ byte[] data = bos.toByteArray();
+ bos.close();
+ return data;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * MultipartFile转File
+ *
+ * @param param
+ * @return
+ */
+ public static File transfer(MultipartFile param) {
+ if (!param.isEmpty()) {
+ File file = null;
+ try {
+ InputStream in = param.getInputStream();
+ file = new File(param.getOriginalFilename());
+ OutputStream out = new FileOutputStream(file);
+ int bytesRead = 0;
+ byte[] buffer = new byte[8192];
+ while ((bytesRead = in.read(buffer, 0, 8192)) != -1) {
+ out.write(buffer, 0, bytesRead);
+ }
+ in.close();
+ out.close();
+ return file;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return file;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 获取指定文件的输入流
+ *
+ * @param logoPath 文件的路径
+ * @return
+ */
+ public static InputStream getResourceAsStream(String logoPath) {
+ return FileUtils.class.getResourceAsStream(logoPath);
+ }
+
+ /**
+ * 将InputStream写入到File中
+ *
+ * @param ins
+ * @param file
+ * @throws IOException
+ */
+ public void inputStreamToFile(InputStream ins, File file) throws IOException {
+ OutputStream os = new FileOutputStream(file);
+ int bytesRead = 0;
+ byte[] buffer = new byte[8192];
+ while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
+ os.write(buffer, 0, bytesRead);
+ }
+ os.close();
+ ins.close();
+ }
+
+ /**
+ * 将图片转化成输入流
+ *
+ * @param image 图片
+ * @return inputStream 图片转化之后的输入流
+ */
+ public static InputStream getImageStream(BufferedImage image) {
+ InputStream inputStream = null;
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ ImageOutputStream imageOutputStream;
+ try {
+ imageOutputStream = ImageIO.createImageOutputStream(byteArrayOutputStream);
+ ImageIO.write(image, "jpg", imageOutputStream);
+ inputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return inputStream;
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/activity/NullValueAttributes.java b/src/main/java/com/codesdream/ase/component/activity/NullValueAttributes.java
new file mode 100644
index 0000000..dcd93ea
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/activity/NullValueAttributes.java
@@ -0,0 +1,12 @@
+package com.codesdream.ase.component.activity;
+
+import lombok.Data;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+@Data
+@Component
+public class NullValueAttributes {
+ private List nullValueAttributes;
+}
diff --git a/src/main/java/com/codesdream/ase/component/activity/QrCodeUtils.java b/src/main/java/com/codesdream/ase/component/activity/QrCodeUtils.java
new file mode 100644
index 0000000..e18de65
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/activity/QrCodeUtils.java
@@ -0,0 +1,225 @@
+package com.codesdream.ase.component.activity;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.EncodeHintType;
+import com.google.zxing.MultiFormatWriter;
+import com.google.zxing.common.BitMatrix;
+import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
+import org.springframework.stereotype.Component;
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.geom.RoundRectangle2D;
+import java.awt.image.BufferedImage;
+import java.io.*;
+import java.util.Hashtable;
+import java.util.Random;
+
+/**
+ * 二维码生成工具类
+ */
+@Component
+public class QrCodeUtils {
+ private static final String CHARSET = "utf-8";
+ public static final String FORMAT = "JPG";
+ // 二维码尺寸
+ private static final int QRCODE_SIZE = 300;
+ // LOGO宽度
+ private static final int LOGO_WIDTH = 60;
+ // LOGO高度
+ private static final int LOGO_HEIGHT = 60;
+
+ /**
+ * 生成二维码
+ *
+ * @param content 二维码内容
+ * @param logoPath logo地址
+ * @param needCompress 是否压缩logo
+ * @return 图片
+ * @throws Exception
+ */
+ public static BufferedImage createImage(String content, String logoPath, boolean needCompress) throws Exception {
+ Hashtable hints = new Hashtable<>();
+ hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
+ hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
+ hints.put(EncodeHintType.MARGIN, 1);
+ BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE,
+ hints);
+ int width = bitMatrix.getWidth();
+ int height = bitMatrix.getHeight();
+ BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+ for (int x = 0; x < width; x++) {
+ for (int y = 0; y < height; y++) {
+ image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
+ }
+ }
+ if (logoPath == null || "".equals(logoPath)) {
+ return image;
+ }
+ // 插入图片
+ QrCodeUtils.insertImage(image, logoPath, needCompress);
+ return image;
+ }
+
+ /**
+ * 插入LOGO
+ *
+ * @param source 二维码图片
+ * @param logoPath LOGO图片地址
+ * @param needCompress 是否压缩
+ * @throws IOException
+ */
+ private static void insertImage(BufferedImage source, String logoPath, boolean needCompress) throws IOException {
+ InputStream inputStream = null;
+ try {
+ inputStream = FileUtils.getResourceAsStream(logoPath);
+ Image src = ImageIO.read(inputStream);
+ int width = src.getWidth(null);
+ int height = src.getHeight(null);
+ if (needCompress) { // 压缩LOGO
+ if (width > LOGO_WIDTH) {
+ width = LOGO_WIDTH;
+ }
+ if (height > LOGO_HEIGHT) {
+ height = LOGO_HEIGHT;
+ }
+ Image image = src.getScaledInstance(width, height, Image.SCALE_SMOOTH);
+ BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+ Graphics g = tag.getGraphics();
+ g.drawImage(image, 0, 0, null); // 绘制缩小后的图
+ g.dispose();
+ src = image;
+ }
+ // 插入LOGO
+ Graphics2D graph = source.createGraphics();
+ int x = (QRCODE_SIZE - width) / 2;
+ int y = (QRCODE_SIZE - height) / 2;
+ graph.drawImage(src, x, y, width, height, null);
+ Shape shape = new RoundRectangle2D.Float(x, y, width, width, 6, 6);
+ graph.setStroke(new BasicStroke(3f));
+ graph.draw(shape);
+ graph.dispose();
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new RuntimeException(e);
+ } finally {
+ if (inputStream != null) {
+ inputStream.close();
+ }
+ }
+ }
+
+ /**
+ * 生成二维码(内嵌LOGO)
+ * 二维码文件名随机,文件名可能会有重复
+ *
+ * @param content 内容
+ * @param logoPath LOGO地址
+ * @param destPath 存放目录
+ * @param needCompress 是否压缩LOGO
+ * @throws Exception
+ */
+ public static String encode(String content, String logoPath, String destPath, boolean needCompress) throws Exception {
+ BufferedImage image = QrCodeUtils.createImage(content, logoPath, needCompress);
+ mkdirs(destPath);
+ String fileName = new Random().nextInt(99999999) + "." + FORMAT.toLowerCase();
+ ImageIO.write(image, FORMAT, new File(destPath + "/" + fileName));
+ return fileName;
+ }
+
+ /**
+ * 生成二维码(内嵌LOGO)
+ * 调用者指定二维码文件名
+ *
+ * @param content 内容
+ * @param logoPath LOGO地址
+ * @param destPath 存放目录
+ * @param fileName 二维码文件名
+ * @param needCompress 是否压缩LOGO
+ * @throws Exception
+ */
+ public static String encode(String content, String logoPath, String destPath, String fileName, boolean needCompress) throws Exception {
+ BufferedImage image = QrCodeUtils.createImage(content, logoPath, needCompress);
+ mkdirs(destPath);
+ fileName = fileName.substring(0, fileName.indexOf(".") > 0 ? fileName.indexOf(".") : fileName.length())
+ + "." + FORMAT.toLowerCase();
+ ImageIO.write(image, FORMAT, new File(destPath + "/" + fileName));
+ return fileName;
+ }
+
+ /**
+ * 当文件夹不存在时,mkdirs会自动创建多层目录,区别于mkdir.
+ * (mkdir如果父目录不存在则会抛出异常)
+ *
+ * @param destPath 存放目录
+ */
+ public static void mkdirs(String destPath) {
+ File file = new File(destPath);
+ if (!file.exists() && !file.isDirectory()) {
+ file.mkdirs();
+ }
+ }
+
+ /**
+ * 生成二维码(内嵌LOGO)
+ *
+ * @param content 内容
+ * @param logoPath LOGO地址
+ * @param destPath 存储地址
+ * @throws Exception
+ */
+ public static String encode(String content, String logoPath, String destPath) throws Exception {
+ return QrCodeUtils.encode(content, logoPath, destPath, false);
+ }
+
+ /**
+ * 生成二维码
+ *
+ * @param content 内容
+ * @param destPath 存储地址
+ * @param needCompress 是否压缩LOGO
+ * @throws Exception
+ */
+ public static String encode(String content, String destPath, boolean needCompress) throws Exception {
+ return QrCodeUtils.encode(content, null, destPath, needCompress);
+ }
+
+ /**
+ * 生成二维码
+ *
+ * @param content 内容
+ * @param destPath 存储地址
+ * @throws Exception
+ */
+ public static String encode(String content, String destPath) throws Exception {
+ return QrCodeUtils.encode(content, null, destPath, false);
+ }
+
+ /**
+ * 生成二维码(内嵌LOGO)
+ *
+ * @param content 内容
+ * @param logoPath LOGO地址
+ * @param output 输出流
+ * @param needCompress 是否压缩LOGO
+ * @throws Exception
+ */
+ public static void encode(String content, String logoPath, OutputStream output, boolean needCompress)
+ throws Exception {
+ BufferedImage image = QrCodeUtils.createImage(content, logoPath, needCompress);
+ ImageIO.write(image, FORMAT, output);
+ }
+
+ /**
+ * 生成二维码
+ *
+ * @param content 内容
+ * @param output 输出流
+ * @throws Exception
+ */
+ public static void encode(String content, OutputStream output) throws Exception {
+ QrCodeUtils.encode(content, null, output, false);
+ }
+
+
+}
diff --git a/src/main/java/com/codesdream/ase/component/api/QuickJSONRespond.java b/src/main/java/com/codesdream/ase/component/api/QuickJSONRespond.java
new file mode 100644
index 0000000..69d2db0
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/api/QuickJSONRespond.java
@@ -0,0 +1,128 @@
+package com.codesdream.ase.component.api;
+
+import com.codesdream.ase.component.datamanager.JSONParameter;
+import com.codesdream.ase.component.json.respond.JSONBaseRespondObject;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+
+
+@Component
+public class QuickJSONRespond {
+ @Resource
+ private JSONParameter jsonParameter;
+
+ // 根据对象构造获得标准的JSON响应字符串返回
+ public String getJSONStandardRespond(Integer status, String msg, String info, Object dataObject){
+ JSONBaseRespondObject respondObject = new JSONBaseRespondObject(status, msg);
+ if(info != null) respondObject.setInfo(info);
+ else respondObject.setInfo(null);
+
+ respondObject.setData(dataObject);
+ return jsonParameter.getJSONString(respondObject);
+ }
+
+ // 根据对象构造获得标准的JSON响应字符串返回
+ public String getJSONStandardRespond(HttpStatus status, Object dataObject){
+ JSONBaseRespondObject respondObject = new JSONBaseRespondObject(status.value(), status.getReasonPhrase());
+
+ respondObject.setData(dataObject);
+ return jsonParameter.getJSONString(respondObject);
+ }
+
+ // 根据对象构造获得标准的JSON响应字符串返回
+ public String getJSONStandardRespond(HttpStatus status, String info, Object dataObject){
+ JSONBaseRespondObject respondObject = new JSONBaseRespondObject(status.value(), status.getReasonPhrase());
+ if(info != null) respondObject.setInfo(info);
+ else respondObject.setInfo(null);
+
+ respondObject.setData(dataObject);
+ return jsonParameter.getJSONString(respondObject);
+ }
+
+ // 根据对象构造获得标准的JSON响应字符串返回
+ public String getJSONStandardRespond(HttpStatus status, String info){
+ JSONBaseRespondObject respondObject = new JSONBaseRespondObject(status.value(), status.getReasonPhrase());
+ if(info != null) respondObject.setInfo(info);
+ else respondObject.setInfo(null);
+
+ return jsonParameter.getJSONString(respondObject);
+ }
+
+ // 获得标准的JSON响应字符串返回特定状态码的和解释息
+ public String getJSONStandardRespond(Integer code, String msg, String info){
+ JSONBaseRespondObject respondObject = new JSONBaseRespondObject(code, msg);
+ if(info != null) respondObject.setInfo(info);
+ else respondObject.setInfo(null);
+ respondObject.setData(null);
+ return jsonParameter.getJSONString(respondObject);
+ }
+
+ // 获得标准的JSON响应字符串返回(404状态)
+ public String getRespond404(String info){
+ return getJSONStandardRespond(HttpStatus.NOT_FOUND, info);
+ }
+
+ // 获得标准的JSON响应字符串返回(404状态)
+ public String getRespond404(String info, Object object){
+ return getJSONStandardRespond(HttpStatus.NOT_FOUND, info, object);
+ }
+
+ // 获得标准的JSON响应字符串返回(500状态)
+ public String getRespond500(String info){
+ return getJSONStandardRespond(HttpStatus.INTERNAL_SERVER_ERROR, info);
+ }
+
+ // 获得标准的JSON响应字符串返回(200状态)
+ public String getRespond200(String info){
+ return getJSONStandardRespond(HttpStatus.OK, info);
+ }
+
+ // 获得标准的JSON响应字符串返回(200状态)
+ public String getRespond200(String info, Object object){
+ return getJSONStandardRespond(HttpStatus.OK, info, object);
+ }
+
+ // 获得标准的JSON响应字符串返回(403状态)
+ public String getRespond403(String info){
+ return getJSONStandardRespond(HttpStatus.FORBIDDEN, info);
+ }
+
+ // 获得标准的JSON响应字符串返回(406状态)
+ public String getRespond406(String info){
+ return getJSONStandardRespond(HttpStatus.NOT_ACCEPTABLE, info);
+ }
+
+ // 获得标准的JSON响应字符串返回(406状态)
+ public String getRespond406(String info, Object object){
+ return getJSONStandardRespond(HttpStatus.NOT_ACCEPTABLE, info, object);
+ }
+
+ // 获得标准的JSON响应字符串返回(501态)
+ public String getRespond501(String info){
+ return getJSONStandardRespond(501, "Not Implemented", info) ;
+ }
+
+ // 获得标准的JSON响应字符串返回(401状态)
+ public String getRespond401(String info){
+ return getJSONStandardRespond(401, "Unauthorized", info);
+ }
+
+ // 获得标准的JSON响应字符串返回(400状态)
+ public String getRespond400(String info){
+ return getJSONStandardRespond(400, "Bad Request", info);
+ }
+
+ // 获得标准的JSON响应字符串返回(404状态)
+ public String getRespond400(String info, Object object){
+ return getJSONStandardRespond(400, "Bad Request", info, object);
+ }
+
+ // 获得标准的JSON响应字符串返回(400状态)
+ public String getRespond409(String info){
+ return getJSONStandardRespond(409, "Conflict", info);
+ }
+
+
+}
diff --git a/src/main/java/com/codesdream/ase/component/auth/AJAXRequestChecker.java b/src/main/java/com/codesdream/ase/component/auth/AJAXRequestChecker.java
new file mode 100644
index 0000000..77c9bed
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/auth/AJAXRequestChecker.java
@@ -0,0 +1,14 @@
+package com.codesdream.ase.component.auth;
+
+import org.springframework.security.authentication.AuthenticationServiceException;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Optional;
+
+@Component
+public class AJAXRequestChecker {
+ public boolean checkAjaxPOSTRequest(HttpServletRequest request){
+ return Optional.ofNullable(request.getHeader("X-Requested-With")).isPresent();
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/auth/ASEAccessDecisionManager.java b/src/main/java/com/codesdream/ase/component/auth/ASEAccessDecisionManager.java
new file mode 100644
index 0000000..ae22d9e
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/auth/ASEAccessDecisionManager.java
@@ -0,0 +1,42 @@
+package com.codesdream.ase.component.auth;
+
+import org.springframework.security.access.AccessDecisionManager;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.access.ConfigAttribute;
+import org.springframework.security.authentication.InsufficientAuthenticationException;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.stereotype.Component;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+@Component
+public class ASEAccessDecisionManager implements AccessDecisionManager {
+ @Override
+ public void decide(Authentication authentication, Object object, Collection configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
+ if(null == configAttributes || configAttributes.size() <= 0) {
+ return;
+ }
+
+ for (ConfigAttribute c : configAttributes) {
+ String needRole = c.getAttribute();
+ for (GrantedAuthority ga : authentication.getAuthorities()) {
+ if (needRole.trim().equals(ga.getAuthority())) {
+ return;
+ }
+ }
+ }
+ throw new AccessDeniedException("Access Denied");
+ }
+
+ @Override
+ public boolean supports(ConfigAttribute attribute) {
+ return true;
+ }
+
+ @Override
+ public boolean supports(Class> clazz) {
+ return true;
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/auth/ASEAccessDeniedHandler.java b/src/main/java/com/codesdream/ase/component/auth/ASEAccessDeniedHandler.java
new file mode 100644
index 0000000..0c3350e
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/auth/ASEAccessDeniedHandler.java
@@ -0,0 +1,33 @@
+package com.codesdream.ase.component.auth;
+
+import com.codesdream.ase.component.api.QuickJSONRespond;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.web.access.AccessDeniedHandler;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+// 认证用户访问无权限资源
+@Slf4j
+@Component
+public class ASEAccessDeniedHandler implements AccessDeniedHandler {
+
+ @Resource
+ private QuickJSONRespond quickJSONRespond;
+
+ @Override
+ public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException)
+ throws IOException, ServletException {
+ log.info("ASEAccessDeniedHandler Found!");
+
+ // 对无权限操作返回403
+ response.getWriter().print(quickJSONRespond.getRespond403(null));
+
+
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/auth/ASEAuthenticationEntryPoint.java b/src/main/java/com/codesdream/ase/component/auth/ASEAuthenticationEntryPoint.java
new file mode 100644
index 0000000..296cc2b
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/auth/ASEAuthenticationEntryPoint.java
@@ -0,0 +1,29 @@
+package com.codesdream.ase.component.auth;
+
+import com.codesdream.ase.component.api.QuickJSONRespond;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.AuthenticationEntryPoint;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+// 匿名用户访问无权限资源
+@Slf4j
+@Component
+public class ASEAuthenticationEntryPoint implements AuthenticationEntryPoint {
+ @Resource
+ private QuickJSONRespond quickJSONRespond;
+
+ @Override
+ public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
+ throws IOException {
+
+ // 对匿名用户返回401
+ response.getWriter().print(quickJSONRespond.getRespond401(null));
+
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/auth/ASEAuthenticationFailureHandler.java b/src/main/java/com/codesdream/ase/component/auth/ASEAuthenticationFailureHandler.java
new file mode 100644
index 0000000..d90a784
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/auth/ASEAuthenticationFailureHandler.java
@@ -0,0 +1,43 @@
+package com.codesdream.ase.component.auth;
+
+import com.codesdream.ase.component.api.QuickJSONRespond;
+import com.codesdream.ase.component.json.respond.ErrorInfoJSONRespond;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.Date;
+
+// 认证失败返回
+@Slf4j
+@Component
+public class ASEAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
+
+ @Resource
+ private QuickJSONRespond quickJSONRespond;
+
+ @Override
+ public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception)
+ throws IOException
+ {
+ log.info("ASEAuthenticationFailureHandler Login Fail!");
+
+ // 填写异常信息存储对象
+ ErrorInfoJSONRespond errorInfoJSONRespond = new ErrorInfoJSONRespond();
+ errorInfoJSONRespond.setDate(new Date());
+ errorInfoJSONRespond.setExceptionMessage(exception.getMessage());
+ errorInfoJSONRespond.setException(exception.getClass().getSimpleName());
+
+ // 认证失败返回406
+ response.getWriter().write(quickJSONRespond.getJSONStandardRespond(
+ 406,
+ "Not Acceptable",
+ "Authentication Failure",
+ errorInfoJSONRespond));
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/auth/ASEAuthenticationSuccessHandler.java b/src/main/java/com/codesdream/ase/component/auth/ASEAuthenticationSuccessHandler.java
new file mode 100644
index 0000000..f73d445
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/auth/ASEAuthenticationSuccessHandler.java
@@ -0,0 +1,60 @@
+package com.codesdream.ase.component.auth;
+
+import com.codesdream.ase.component.api.QuickJSONRespond;
+import com.codesdream.ase.component.json.respond.UserLoginCheckerJSONRespond;
+import com.codesdream.ase.model.permission.User;
+
+import com.codesdream.ase.service.IAuthService;
+import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.security.core.Authentication;
+
+import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
+
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.Optional;
+
+// 认证成功返回
+@Slf4j
+@Component
+public class ASEAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
+ @Resource
+ private QuickJSONRespond quickJSONRespond;
+
+ @Resource
+ private IAuthService authService;
+
+ @Override
+ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
+ throws IOException, ServletException
+ {
+
+ UserLoginCheckerJSONRespond respond = new UserLoginCheckerJSONRespond();
+ respond.setUserExist(authentication.isAuthenticated());
+ respond.setLoginStatus(authentication.isAuthenticated());
+ respond.setPvc(authService.preValidationCodeGetter());
+
+ // 获得 JSONTokenAuthenticationToken
+ JSONTokenAuthenticationToken authenticationToken = (JSONTokenAuthenticationToken) authentication;
+
+ User user = (User) authenticationToken.getPrincipal();
+
+ Optional tokenOptional = authService.userNewTokenGetter(
+ user.getUsername(), authenticationToken.getClientCode());
+
+ if(tokenOptional.isPresent()){
+ respond.setToken(tokenOptional.get());
+ }
+ else respond.setToken("");
+
+ // 认证成功返回200
+ response.getWriter().write(quickJSONRespond.getRespond200("Authentication Success", respond));
+
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/auth/ASEInvocationSecurityMetadataSource.java b/src/main/java/com/codesdream/ase/component/auth/ASEInvocationSecurityMetadataSource.java
new file mode 100644
index 0000000..3c1f359
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/auth/ASEInvocationSecurityMetadataSource.java
@@ -0,0 +1,65 @@
+package com.codesdream.ase.component.auth;
+
+import com.codesdream.ase.model.permission.Function;
+import com.codesdream.ase.repository.permission.FunctionRepository;
+import org.springframework.security.access.ConfigAttribute;
+import org.springframework.security.access.SecurityConfig;
+import org.springframework.security.web.FilterInvocation;
+import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
+import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+
+@Component
+public class ASEInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
+
+ private HashMap> map = null;
+
+ @Resource
+ private FunctionRepository functionRepository;
+
+ private void loadFunctionDefine(){
+ map = new HashMap<>();
+ Iterable functions = functionRepository.findAll();
+ for(Function function : functions) {
+ Collection array = new ArrayList<>();
+ ConfigAttribute cfg = new SecurityConfig(function.getName());
+
+ array.add(cfg);
+
+ map.put(function.getUrl(), array);
+ }
+ }
+
+ @Override
+ public Collection getAttributes(Object object) throws IllegalArgumentException {
+
+ if(map == null) loadFunctionDefine();
+
+ HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
+
+ for (String url : map.keySet()) {
+ AntPathRequestMatcher matcher = new AntPathRequestMatcher(url);
+ if (matcher.matches(request)) {
+ return map.get(url);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public Collection getAllConfigAttributes() {
+ return null;
+ }
+
+ @Override
+ public boolean supports(Class> clazz) {
+ return true;
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/auth/ASEJSONTokenAuthenticationFilter.java b/src/main/java/com/codesdream/ase/component/auth/ASEJSONTokenAuthenticationFilter.java
new file mode 100644
index 0000000..f7f54f2
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/auth/ASEJSONTokenAuthenticationFilter.java
@@ -0,0 +1,114 @@
+package com.codesdream.ase.component.auth;
+
+import com.codesdream.ase.model.auth.JSONToken;
+import com.codesdream.ase.service.AuthService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import javax.annotation.Resource;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Optional;
+
+
+// API请求验证过滤
+@Slf4j
+public class ASEJSONTokenAuthenticationFilter extends OncePerRequestFilter {
+
+ @Resource
+ private JSONRandomCodeGenerator randomCodeGenerator;
+
+ @Resource
+ private AuthService authService;
+
+ @Resource
+ private JSONSignedGenerator signedGenerator;
+
+ @Resource
+ private UserDetailsService userDetailsService;
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
+ throws ServletException, IOException {
+
+ // 用户名
+ String username = request.getHeader( "username");
+ // 客户端签名
+ String signed = request.getHeader("signed");
+ // 时间戳
+ String timestamp = request.getHeader("timestamp");
+
+ // 服务端API测试豁免签名
+ if(signed != null && signed.equals("6d4923fca4dcb51f67b85e54a23a8d763d9e02af")){
+ //执行授权
+ doAuthentication("u_id_88883b9e023c8824310760d8bb8b6542e5a3f16a0d67253214e01ee7ab0e96a1", request);
+ }
+ // 正常认证
+ else if (signed != null && username != null && timestamp != null) {
+ // 获得具体时间
+ Date date = new Date(Long.parseLong(timestamp));
+
+ Date now = new Date();
+
+ // 限制时间戳有效区间为60s
+ long dtTime = 60*1000;
+ Date maxDate = new Date(now.getTime() + dtTime);
+
+ // 检查时间戳是否合理
+ if(maxDate.after(date)) {
+ // 从服务器中查找token
+ Optional optionalJSONToken = authService.findTokenByUserName(username);
+ if (optionalJSONToken.isPresent()) {
+ JSONToken token = optionalJSONToken.get();
+
+ // 检查token是否过期
+ if (!authService.checkTokenIfExpired(token)) {
+ // 生成特征随机代码
+ String randomCode = randomCodeGenerator.generateRandomCode(username, date, token.getClientCode());
+
+ log.info(String.format("Determined Signed: %s",
+ signedGenerator.generateSigned(username, randomCode, token.getToken())));
+
+ // 检查签名是否正确
+ if (signed.equals(signedGenerator.generateSigned(username, randomCode, token.getToken()))) {
+ // 执行授权操作
+ doAuthentication(username, request);
+ }
+ }
+ }
+ }
+ }
+
+ filterChain.doFilter(request, response);
+
+ }
+
+ // 执行授权
+ private void doAuthentication(String username, HttpServletRequest request){
+ // 查询用户的相关信息
+ UserDetails user = userDetailsService.loadUserByUsername(username);
+
+ // 生成用户权限列表
+ Collection extends GrantedAuthority> authorities = user.getAuthorities();
+
+ // 生成授权柄 (储存上下文信息)
+ JSONTokenAuthenticationToken authentication =
+ new JSONTokenAuthenticationToken(user, null, authorities);
+
+ authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
+
+ // 执行授权
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+ }
+
+}
diff --git a/src/main/java/com/codesdream/ase/component/auth/ASEPasswordEncoder.java b/src/main/java/com/codesdream/ase/component/auth/ASEPasswordEncoder.java
new file mode 100644
index 0000000..95c66c4
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/auth/ASEPasswordEncoder.java
@@ -0,0 +1,18 @@
+package com.codesdream.ase.component.auth;
+
+import org.apache.commons.codec.digest.DigestUtils;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ASEPasswordEncoder implements PasswordEncoder {
+ @Override
+ public String encode(CharSequence charSequence) {
+ return DigestUtils.sha256Hex(charSequence.toString());
+ }
+
+ @Override
+ public boolean matches(CharSequence charSequence, String s) {
+ return s.equals(DigestUtils.sha256Hex(charSequence.toString()));
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/auth/ASESecurityAuthenticationProvider.java b/src/main/java/com/codesdream/ase/component/auth/ASESecurityAuthenticationProvider.java
new file mode 100644
index 0000000..fc70128
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/auth/ASESecurityAuthenticationProvider.java
@@ -0,0 +1,77 @@
+package com.codesdream.ase.component.auth;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.authentication.*;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.Collection;
+
+// 普通用户名密码验证, 用户获得Token
+@Slf4j
+@Component
+public class ASESecurityAuthenticationProvider implements AuthenticationProvider {
+ @Resource
+ UserDetailsService userDetailsService;
+
+ @Resource
+ ASEUsernameEncoder usernameEncoder;
+
+ @Resource
+ PasswordEncoder passwordEncoder;
+
+ @Override
+ public Authentication authenticate(Authentication authentication) throws AuthenticationException {
+ JSONTokenUsernamePasswordAuthenticationToken authenticationToken =
+ (JSONTokenUsernamePasswordAuthenticationToken) authentication;
+
+ // 获得JSON中的学号
+ String username = usernameEncoder.encode((CharSequence) authenticationToken.getPrincipal());
+ // 获得JSON中的加密密码
+ String encrypted_password = (String) authenticationToken.getCredentials();
+ // 获得客户端代码
+ String clientCode = authenticationToken.getClientCode();
+ // 判断用户是否存在
+ UserDetails userInfo = userDetailsService.loadUserByUsername(username);
+
+ if (userInfo == null) {
+ throw new UsernameNotFoundException("User Not Exist");
+ }
+
+ String sha256_password = userInfo.getPassword();
+
+ // 判断密码是否正确
+ if(!passwordEncoder.encode(String.format(
+ "PASS_ENCODE [%s][%s]", sha256_password, clientCode)).equals(encrypted_password)){
+ throw new BadCredentialsException("Password IS INCORRECT");
+ }
+
+ // 判断账号是否停用/删除
+ if (!userInfo.isEnabled()) {
+ throw new DisabledException("User IS Disabled");
+ }
+ else if(!userInfo.isAccountNonLocked()) {
+ throw new LockedException("User IS Locked");
+ }
+ else if(!userInfo.isAccountNonExpired()) {
+ throw new AccountExpiredException("User IS Expired");
+ }
+
+ // 生成权限列表
+ Collection extends GrantedAuthority> authorities = userInfo.getAuthorities();
+
+ return new JSONTokenAuthenticationToken(userInfo, clientCode, authorities);
+ }
+
+ @Override
+ public boolean supports(Class> aClass) {
+ return aClass.equals(JSONTokenUsernamePasswordAuthenticationToken.class);
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/auth/ASESecurityInterceptor.java b/src/main/java/com/codesdream/ase/component/auth/ASESecurityInterceptor.java
new file mode 100644
index 0000000..f8ea470
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/auth/ASESecurityInterceptor.java
@@ -0,0 +1,62 @@
+package com.codesdream.ase.component.auth;
+
+import javax.annotation.Resource;
+import javax.servlet.*;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.SecurityMetadataSource;
+import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
+import org.springframework.security.access.intercept.InterceptorStatusToken;
+import org.springframework.security.web.FilterInvocation;
+import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+
+@Component
+public class ASESecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
+
+ @Resource
+ private FilterInvocationSecurityMetadataSource securityMetadataSource;
+
+ @Resource
+ public void setASEAccessDecisionManager(ASEAccessDecisionManager accessDecisionManager) {
+ super.setAccessDecisionManager(accessDecisionManager);
+ }
+
+ @Override
+ public Class> getSecureObjectClass() {
+ return FilterInvocation.class;
+ }
+
+ @Override
+ public SecurityMetadataSource obtainSecurityMetadataSource() {
+ return this.securityMetadataSource;
+ }
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+ FilterInvocation fi = new FilterInvocation(request, response, chain);
+ invoke(fi);
+ }
+
+ public void invoke(FilterInvocation fi) throws IOException, ServletException {
+ InterceptorStatusToken token = super.beforeInvocation(fi);
+ try {
+ //执行下一个拦截器
+ fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
+ } finally {
+ super.afterInvocation(token, null);
+ }
+ }
+
+ @Override
+ public void destroy() {
+
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/auth/ASEUsernameEncoder.java b/src/main/java/com/codesdream/ase/component/auth/ASEUsernameEncoder.java
new file mode 100644
index 0000000..c4dc157
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/auth/ASEUsernameEncoder.java
@@ -0,0 +1,15 @@
+package com.codesdream.ase.component.auth;
+
+import org.apache.commons.codec.digest.DigestUtils;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ASEUsernameEncoder {
+ public String encode(CharSequence charSequence){
+ return "u_id_" + DigestUtils.sha256Hex(charSequence.toString());
+ }
+
+ public boolean matches(CharSequence charSequence, String s){
+ return s.equals(encode(charSequence.toString()));
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/auth/ASEUsernamePasswordAuthenticationFilter.java b/src/main/java/com/codesdream/ase/component/auth/ASEUsernamePasswordAuthenticationFilter.java
new file mode 100644
index 0000000..3297622
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/auth/ASEUsernamePasswordAuthenticationFilter.java
@@ -0,0 +1,85 @@
+package com.codesdream.ase.component.auth;
+
+import com.codesdream.ase.component.auth.AJAXRequestChecker;
+import com.codesdream.ase.component.auth.JSONTokenUsernamePasswordAuthenticationToken;
+import com.codesdream.ase.component.auth.TimestampExpiredChecker;
+import com.codesdream.ase.component.datamanager.JSONParameter;
+import com.codesdream.ase.component.json.request.UserLoginChecker;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.authentication.AuthenticationServiceException;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Optional;
+
+// 普通登录验证过滤器
+@Slf4j
+public class ASEUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
+
+ @Resource
+ private JSONParameter jsonParameter;
+
+ @Resource
+ private AJAXRequestChecker ajaxRequestChecker;
+
+ @Resource
+ private TimestampExpiredChecker timestampExpiredChecker;
+
+ @Override
+ public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
+ throws AuthenticationException {
+
+ String timestamp = request.getHeader("timestamp");
+
+ // 检查时间戳是否合理(60秒内)
+ if(timestamp == null || !timestampExpiredChecker.checkTimestampBeforeMaxTime(timestamp, 60)){
+ throw new AuthenticationServiceException("Timestamp Expired.");
+ }
+
+ // 判断是否为AJAX请求格式的数据
+ if(!ajaxRequestChecker.checkAjaxPOSTRequest(request)) {
+ throw new AuthenticationServiceException("Authentication method not supported: NOT Ajax Method.");
+ }
+
+ Optional checkerOptional = jsonParameter.getJavaObjectByRequest(request, UserLoginChecker.class);
+ if(!checkerOptional.isPresent()) throw new BadCredentialsException("Invalid AJAX JSON Request");
+
+ UserLoginChecker checker = checkerOptional.get();
+
+ if(checker.getUsername() == null
+ || checker.getPassword() == null
+ || checker.getClientCode() == null
+ || checker.getCheckType() == null)
+ throw new AuthenticationServiceException("Request Data IS Incomplete");
+
+ if (!checker.getCheckType().equals("UsernamePasswordChecker"))
+ throw new AuthenticationServiceException("Authentication not supported: NOT Username Password Type.");
+
+ // 获得相应的用户名密码
+ String username = checker.getUsername();
+ // 得到加密密码
+ String password = checker.getPassword();
+ String clientCode = checker.getClientCode();
+
+ if (username == null) username = "";
+ if (password == null) password = "";
+
+ // 去除首尾两端的空白字符
+ username = username.trim();
+ password = password.trim();
+
+
+ JSONTokenUsernamePasswordAuthenticationToken authRequest =
+ new JSONTokenUsernamePasswordAuthenticationToken(username, password, clientCode);
+
+ authRequest.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
+
+ return this.getAuthenticationManager().authenticate(authRequest);
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/auth/AuthTokenGenerator.java b/src/main/java/com/codesdream/ase/component/auth/AuthTokenGenerator.java
new file mode 100644
index 0000000..8977670
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/auth/AuthTokenGenerator.java
@@ -0,0 +1,19 @@
+package com.codesdream.ase.component.auth;
+
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.Date;
+import java.util.UUID;
+
+@Component
+public class AuthTokenGenerator {
+ @Resource
+ private SHA1Encoder encoder;
+
+ public String generateAuthToken(String username){
+ Date dateNow = new Date();
+ UUID uuid = UUID.randomUUID();
+ return encoder.encode(String.format("Token [%s][%d][%s]",username,dateNow.getTime(), uuid.toString()));
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/auth/JSONRandomCodeGenerator.java b/src/main/java/com/codesdream/ase/component/auth/JSONRandomCodeGenerator.java
new file mode 100644
index 0000000..fe2ce6c
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/auth/JSONRandomCodeGenerator.java
@@ -0,0 +1,19 @@
+package com.codesdream.ase.component.auth;
+
+import com.alibaba.fastjson.JSONObject;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.Date;
+
+// 随机特征码生成器
+@Component
+public class JSONRandomCodeGenerator {
+ @Resource
+ private SHA1Encoder encoder;
+
+ public String generateRandomCode(String username, Date date, String clientCode){
+ return encoder.encode(String.format("RandomCode [%s][%s][%s]",
+ username, Long.toString(date.getTime()), clientCode));
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/auth/JSONSignedGenerator.java b/src/main/java/com/codesdream/ase/component/auth/JSONSignedGenerator.java
new file mode 100644
index 0000000..b67a76c
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/auth/JSONSignedGenerator.java
@@ -0,0 +1,16 @@
+package com.codesdream.ase.component.auth;
+
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+
+// 用来给JSON生成签名
+@Component
+public class JSONSignedGenerator {
+ @Resource
+ SHA1Encoder encoder;
+
+ public String generateSigned(String username, String randomCode, String token){
+ return encoder.encode(String.format("SIGN [%s][%s][%s]",username, randomCode, token));
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/auth/JSONTokenAuthenticationToken.java b/src/main/java/com/codesdream/ase/component/auth/JSONTokenAuthenticationToken.java
new file mode 100644
index 0000000..92bf5f9
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/auth/JSONTokenAuthenticationToken.java
@@ -0,0 +1,62 @@
+package com.codesdream.ase.component.auth;
+
+
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+
+import java.util.Collection;
+
+// 关联Token与其他用户的相关数据的授权柄
+public class JSONTokenAuthenticationToken extends AbstractAuthenticationToken {
+ // 客户端签名
+ private String signed = null;
+ // 用户名
+ private Object principal = null;
+ // 客户端代码
+ private String clientCode = null;
+
+
+ /**
+ * Creates a token with the supplied array of authorities.
+ *
+ * @param authorities the collection of GrantedAuthoritys for the principal
+ * represented by this authentication object.
+ */
+ public JSONTokenAuthenticationToken(UserDetails principal,
+ String clientCode,
+ Collection extends GrantedAuthority> authorities)
+ {
+ super(authorities);
+ this.principal = principal;
+ this.clientCode = clientCode;
+ this.signed = null;
+ setAuthenticated(true);
+ }
+
+ public JSONTokenAuthenticationToken(String principal, String clientCode, String signed) {
+ super(null);
+ this.principal = principal;
+ this.clientCode = clientCode;
+ this.signed = signed;
+ setAuthenticated(false);
+ }
+
+ @Override
+ public String getCredentials() {
+ return signed;
+ }
+
+ @Override
+ public Object getPrincipal() {
+ return principal;
+ }
+
+ public String getClientCode() {
+ return clientCode;
+ }
+
+ public void setClientCode(String clientCode) {
+ this.clientCode = clientCode;
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/auth/JSONTokenUsernamePasswordAuthenticationToken.java b/src/main/java/com/codesdream/ase/component/auth/JSONTokenUsernamePasswordAuthenticationToken.java
new file mode 100644
index 0000000..14cc5fd
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/auth/JSONTokenUsernamePasswordAuthenticationToken.java
@@ -0,0 +1,38 @@
+package com.codesdream.ase.component.auth;
+
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.core.GrantedAuthority;
+
+import java.util.Collection;
+
+// 明文用户名密码验证授权柄
+public class JSONTokenUsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {
+ // 用户名
+ private String username = null;
+ // 明文密码
+ private String password = null;
+ // 授权柄
+ private String clientCode = null;
+
+ public JSONTokenUsernamePasswordAuthenticationToken(String username, String password, String clientCode) {
+ super(null);
+ this.username = username;
+ this.password = password;
+ this.clientCode = clientCode;
+ setAuthenticated(false);
+ }
+
+ @Override
+ public Object getCredentials() {
+ return password;
+ }
+
+ @Override
+ public Object getPrincipal() {
+ return username;
+ }
+ // 扩展接口 获得客户端代码
+ public String getClientCode() {
+ return clientCode;
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/auth/SHA1Encoder.java b/src/main/java/com/codesdream/ase/component/auth/SHA1Encoder.java
new file mode 100644
index 0000000..9216ab9
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/auth/SHA1Encoder.java
@@ -0,0 +1,17 @@
+package com.codesdream.ase.component.auth;
+
+import org.apache.commons.codec.cli.Digest;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.springframework.stereotype.Component;
+
+// SHA1算法不可逆加密 主要用于JSON签名
+@Component
+public class SHA1Encoder {
+ public String encode(CharSequence charSequence){
+ return DigestUtils.sha1Hex(charSequence.toString());
+ }
+
+ public boolean match (CharSequence charSequence, String s){
+ return s.equals(encode(charSequence));
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/auth/TimestampExpiredChecker.java b/src/main/java/com/codesdream/ase/component/auth/TimestampExpiredChecker.java
new file mode 100644
index 0000000..667d801
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/auth/TimestampExpiredChecker.java
@@ -0,0 +1,24 @@
+package com.codesdream.ase.component.auth;
+
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+
+// 验证时间戳是否有效
+@Component
+public class TimestampExpiredChecker {
+
+ public boolean checkTimestampBeforeMaxTime(String timestamp, int seconds){
+ Date timestampDate = new Date(Long.parseLong(timestamp));
+ long currentTime = System.currentTimeMillis();
+ Date maxDate = new Date(currentTime + seconds * 1000);
+ return timestampDate.before(maxDate);
+ }
+
+ public boolean checkDateBeforeMaxTime(Date date, int seconds){
+ long currentTime = System.currentTimeMillis();
+ Date maxDate = new Date(currentTime + seconds * 1000);
+ return date.before(maxDate);
+ }
+
+}
diff --git a/src/main/java/com/codesdream/ase/component/datamanager/DataExcelGenerator.java b/src/main/java/com/codesdream/ase/component/datamanager/DataExcelGenerator.java
new file mode 100644
index 0000000..c11d01b
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/datamanager/DataExcelGenerator.java
@@ -0,0 +1,84 @@
+package com.codesdream.ase.component.datamanager;
+
+import com.codesdream.ase.exception.notfound.DataFileNotFoundException;
+import com.codesdream.ase.exception.innerservererror.DataIOException;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * 利用数据生成Excel文件
+ */
+public class DataExcelGenerator implements DataGenerator {
+ Workbook workbook = new XSSFWorkbook();
+
+ Sheet sheet;
+ Integer colNumber;
+ String path;
+
+ public DataExcelGenerator(String path) {
+ sheet = workbook.createSheet("Data");
+ this.path = path;
+ }
+
+ public void setTableTitle(Collection titles){
+ Row sheetRow = sheet.createRow(0);
+ int idx = 0;
+ for(String title : titles){
+ sheetRow.createCell(idx).setCellValue(title);
+ idx++;
+ }
+ colNumber = titles.size();
+ }
+
+ public void insertRow(int rowIndex, Collection dataCollection){
+ Row row = sheet.createRow(rowIndex);
+ int idx = 0;
+ for(String data : dataCollection){
+ // 限制表头与表体的数据数目
+ if(idx >= colNumber) break;
+ row.createCell(idx).setCellValue(data);
+ idx++;
+ }
+ }
+
+ public void insertRow(Collection dataCollection){
+ insertRow(sheet.getLastRowNum() + 1, dataCollection);
+
+ }
+
+ public void insertRowDataALL(Collection dataCollections){
+ int cellIdx = 0;
+ Collection dataCollection = new ArrayList<>();
+ for(String dataCollectionItem :dataCollections){
+ dataCollection.add(dataCollectionItem);
+ cellIdx++;
+ if(cellIdx % colNumber == 0) {
+ insertRow(dataCollection);
+ dataCollection.clear();
+ cellIdx = 0;
+ }
+
+ }
+ }
+
+
+ public void save() {
+ try {
+ FileOutputStream stream = new FileOutputStream(path, false);
+ workbook.write(stream);
+ stream.close();
+ } catch (FileNotFoundException e) {
+ throw new DataFileNotFoundException(path);
+ } catch (IOException e) {
+ throw new DataIOException();
+ }
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/datamanager/DataExcelReader.java b/src/main/java/com/codesdream/ase/component/datamanager/DataExcelReader.java
new file mode 100644
index 0000000..794f8c3
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/datamanager/DataExcelReader.java
@@ -0,0 +1,123 @@
+package com.codesdream.ase.component.datamanager;
+
+
+import com.codesdream.ase.exception.*;
+import com.codesdream.ase.exception.innerservererror.DataIOException;
+import com.codesdream.ase.exception.innerservererror.DataIllegalTableFormatException;
+import com.codesdream.ase.exception.innerservererror.DataInvalidFormatException;
+import com.codesdream.ase.exception.notfound.DataFileNotFoundException;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.ss.usermodel.*;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * 读取简单表格式的Excel文件
+ */
+public class DataExcelReader implements DataReader {
+ Workbook workbook = null;
+ Sheet sheet = null;
+ short colNumber = 0;
+ int rowNumberNow = 0;
+ String path;
+ boolean hasReadFile = false;
+
+ public DataExcelReader(String path){
+ this.path = path;
+ readFile();
+ }
+
+ public void readFile(){
+ if(hasReadFile) return;
+ try{
+ FileInputStream fileInputStream = new FileInputStream(path);
+ workbook = WorkbookFactory.create(fileInputStream);
+ sheet = workbook.getSheetAt(0);
+ fileInputStream.close();
+ } catch (FileNotFoundException e) {
+ throw new DataFileNotFoundException(path);
+ } catch (InvalidFormatException e) {
+ throw new DataInvalidFormatException(e);
+ } catch (IOException e) {
+ throw new DataIOException();
+ }
+ }
+
+ public Collection readColsTitle(){
+ Row titleRow = sheet.getRow(0);
+ colNumber = titleRow.getLastCellNum();
+ // 表头项目个数不可为0
+ if(colNumber == 0) throw new DataIllegalTableFormatException();
+ Collection title = new ArrayList<>();
+ for(int cellIdx = 0; cellIdx < colNumber; cellIdx++){
+ title.add(readCell(titleRow.getCell(cellIdx)));
+ }
+ return title;
+ }
+
+ public Collection readRow(int idx){
+ // 检查是否获取表头数据
+ if(this.colNumber == 0) readColsTitle();
+ if(idx > getRowsSize()) throw new DataReaderRowIndexOutOfRangeException();
+ this.rowNumberNow = idx;
+ Collection data = new ArrayList<>();
+ Row dataRow = sheet.getRow(rowNumberNow);
+ // 检查列数是否合适
+ if(dataRow.getLastCellNum() > colNumber) throw new DataIllegalTableFormatException();
+ for(int cellIdx = 0; cellIdx < colNumber; cellIdx++){
+ data.add(readCell(dataRow.getCell(cellIdx)));
+ }
+ return data;
+ }
+
+ @Override
+ public int getRowsSize() {
+ return lastDataRowIndex() - firstDataRowIndex();
+ }
+
+ @Override
+ public int firstDataRowIndex() {
+ return 1;
+ }
+
+ @Override
+ public int lastDataRowIndex() {
+ return sheet.getLastRowNum();
+ }
+
+ public Collection readRow(){
+ if(rowNumberNow >= getRowsSize()) return null;
+ return readRow(rowNumberNow + 1);
+ }
+
+ private String readCell(Cell cell){
+ String cellValue = "";
+ switch (cell.getCellType()) {
+ case Cell.CELL_TYPE_STRING:
+ cellValue = cell.getRichStringCellValue().getString();
+ break;
+ case Cell.CELL_TYPE_NUMERIC:
+ if (DateUtil.isCellDateFormatted(cell)) {
+ cellValue = cell.getDateCellValue().toString();
+ } else {
+ cellValue = String.valueOf(cell.getNumericCellValue());
+ }
+ break;
+ case Cell.CELL_TYPE_BOOLEAN:
+ cellValue = String.valueOf(cell.getBooleanCellValue());
+ break;
+ case Cell.CELL_TYPE_FORMULA:
+ cellValue = String.valueOf(cell.getCellFormula());
+ break;
+ case Cell.CELL_TYPE_BLANK:
+ break;
+ default:
+ }
+ return cellValue;
+ }
+
+}
diff --git a/src/main/java/com/codesdream/ase/component/datamanager/DataGenerator.java b/src/main/java/com/codesdream/ase/component/datamanager/DataGenerator.java
new file mode 100644
index 0000000..4a9f061
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/datamanager/DataGenerator.java
@@ -0,0 +1,17 @@
+package com.codesdream.ase.component.datamanager;
+
+import java.util.Collection;
+
+/**
+ * 表给狗数据文件生成器接口
+ */
+public interface DataGenerator {
+ // 读取表头信息
+ void setTableTitle(Collection titles);
+
+ // 向表中写入一行数据
+ void insertRow(int rowIndex, Collection dataCollection);
+
+ // 将修改保存表到文件中
+ void save();
+}
diff --git a/src/main/java/com/codesdream/ase/component/datamanager/DataGetObjectFromRequest.java b/src/main/java/com/codesdream/ase/component/datamanager/DataGetObjectFromRequest.java
new file mode 100644
index 0000000..3d59371
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/datamanager/DataGetObjectFromRequest.java
@@ -0,0 +1,8 @@
+package com.codesdream.ase.component.datamanager;
+
+import org.springframework.stereotype.Component;
+
+// 解析request请求,获得对应的Java对象
+@Component
+public class DataGetObjectFromRequest {
+}
diff --git a/src/main/java/com/codesdream/ase/component/datamanager/DataModelRepositorySearcher.java b/src/main/java/com/codesdream/ase/component/datamanager/DataModelRepositorySearcher.java
new file mode 100644
index 0000000..3844039
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/datamanager/DataModelRepositorySearcher.java
@@ -0,0 +1,57 @@
+package com.codesdream.ase.component.datamanager;
+
+import com.codesdream.ase.component.ASESpringUtil;
+import lombok.Data;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+
+/**
+ * Model对应的Repository层查询器
+ */
+@Data
+@Component
+@Scope("prototype")
+public class DataModelRepositorySearcher {
+ @Resource
+ ASESpringUtil springUtil;
+
+ public final String repositoryPath = "com.codesdream.ase.repository.";
+
+ private Class> repositoryClass = null;
+
+ private boolean present;
+
+ public void getDataModelRepositoryClass(String subSystem, String dataModel){
+ dataModel = doCheckName(dataModel);
+ try {
+ this.repositoryClass = Class.forName(dataModelFullNameGenerator(subSystem, dataModel));
+ this.setPresent(true);
+ } catch (ClassNotFoundException e) {
+ this.setPresent(false);
+ }
+ }
+
+ public T getDataModelRepositoryInstance() {
+ // 确保可以引用
+ if(isPresent()) {
+ @SuppressWarnings("unchecked")
+ T repository = (T) springUtil.getBean(repositoryClass);
+ return repository;
+ }
+ return null;
+ }
+
+ public static String doCheckName(String string) {
+ char[] charArray = string.toCharArray();
+ if(Character.isLowerCase(charArray[0])) charArray[0] -= 32;
+ else return string;
+ return String.valueOf(charArray);
+ }
+
+ private String dataModelFullNameGenerator(String subSystem, String dataModel){
+ return new String(repositoryPath + subSystem + "." + dataModel + "Repository");
+ }
+
+}
diff --git a/src/main/java/com/codesdream/ase/component/datamanager/DataModelSearcher.java b/src/main/java/com/codesdream/ase/component/datamanager/DataModelSearcher.java
new file mode 100644
index 0000000..0493d1c
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/datamanager/DataModelSearcher.java
@@ -0,0 +1,70 @@
+package com.codesdream.ase.component.datamanager;
+
+import lombok.Data;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+
+import javax.swing.text.html.parser.Entity;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * 根据子系统及名称查找特定的MModel
+ */
+@Data
+@Component
+@Scope("prototype")
+public class DataModelSearcher {
+ public final String modelPath = "com.codesdream.ase.model.";
+ private Class> dataModelClass = null;
+
+ private boolean present;
+
+ public void getDataModelClass(String subSystem, String dataModel) {
+ dataModel = doCheckName(dataModel);
+ try {
+ dataModelClass = Class.forName(dataModelFullNameGenerator(subSystem, dataModel));
+ this.setPresent(true);
+ }catch (ClassNotFoundException e){
+ this.setPresent(false);
+ }
+ }
+
+ public Collection getDataModelParamArgs() {
+ if(isPresent()) {
+ Collection paramArgs = new ArrayList<>();
+ for (Field field : dataModelClass.getDeclaredFields()) {
+ paramArgs.add(field.getName());
+ }
+ return paramArgs;
+ }
+ return null;
+ }
+
+ public T getDataModelInstance(Class dataModelClass) {
+ if(isPresent()){
+ try {
+ return dataModelClass.newInstance();
+ } catch (IllegalAccessException e) {
+ this.setPresent(false);
+ return null;
+ } catch (InstantiationException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+ return null;
+ }
+
+ public static String doCheckName(String string) {
+ char[] charArray = string.toCharArray();
+ if(Character.isLowerCase(charArray[0])) charArray[0] -= 32;
+ else return string;
+ return String.valueOf(charArray);
+ }
+
+ private String dataModelFullNameGenerator(String subSystem, String dataModel){
+ return new String(modelPath + subSystem + "." + dataModel);
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/datamanager/DataReader.java b/src/main/java/com/codesdream/ase/component/datamanager/DataReader.java
new file mode 100644
index 0000000..4b9dd21
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/datamanager/DataReader.java
@@ -0,0 +1,27 @@
+package com.codesdream.ase.component.datamanager;
+
+import java.util.Collection;
+
+/**
+ * 表结构信息读取器接口
+ */
+public interface DataReader {
+ // 从文件中读取数据(在使用上要求这个调用可有可无)
+ void readFile();
+
+ // 获得表头列的数目
+ Collection readColsTitle();
+
+ // 读取特定序号的行的数据
+ Collection readRow(int row);
+
+ // 得到数据的总行数
+ int getRowsSize();
+
+ // 得到第一数据行的序号
+ int firstDataRowIndex();
+
+ // 得到最后一行数据行的序号
+ int lastDataRowIndex();
+
+}
diff --git a/src/main/java/com/codesdream/ase/component/datamanager/DataTable.java b/src/main/java/com/codesdream/ase/component/datamanager/DataTable.java
new file mode 100644
index 0000000..97d04f5
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/datamanager/DataTable.java
@@ -0,0 +1,89 @@
+package com.codesdream.ase.component.datamanager;
+
+import com.codesdream.ase.exception.innerservererror.DataIllegalTableFormatException;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+
+// 描述一张数据表
+@Component
+@Scope("prototype")
+public class DataTable {
+ Collection titleCollection = new ArrayList<>();
+ Vector dataRows = new Vector<>();
+
+ // 为表添加列及列名
+ public DataTable addColTitle(String title){
+ titleCollection.add(title);
+ return this;
+ }
+
+ // 获得特定行的数据
+ public Collection getRow(int index){
+ if(index >= getRowsSize()) throw new IndexOutOfBoundsException();
+ return dataRows.elementAt(index).getRow();
+ }
+
+ // 获得特定行的数据
+ public Vector getRowVector(int index){
+ if(index >= getRowsSize()) throw new IndexOutOfBoundsException();
+ return new Vector<>(dataRows.elementAt(index).getRow());
+ }
+
+ // 从DataReader导入特定表
+ public void importTable(DataReader reader){
+ // 从文件中读取数据
+ reader.readFile();
+ // 读取列信息
+ titleCollection = reader.readColsTitle();
+
+ int rowsSize = reader.getRowsSize();
+ int index = 0;
+ for(int i = reader.firstDataRowIndex(); i < reader.lastDataRowIndex(); i++){
+ dataRows.add(new DataTableRow(index++, reader.readRow(i)));
+ // 检查是否列数一致
+ if(dataRows.lastElement().getColsSize() != this.getColsSize()) {
+ // 清空表数据
+ this.dataRows.clear();
+ throw new DataIllegalTableFormatException();
+ }
+ }
+ }
+
+ // 查找有无相关表头项
+ public Optional getTitleIndex(String title){
+ int index = 0;
+ for(String dataTitle :titleCollection){
+ if(dataTitle.equals(title))
+ return Optional.of(index);
+ index++;
+ }
+ return Optional.empty();
+ }
+
+ // 导出表数据
+ public void exportTable(DataGenerator dataGenerator){
+ dataGenerator.setTableTitle(titleCollection);
+ for(DataTableRow row : dataRows){
+ dataGenerator.insertRow(row.getIndex(), row.getRow());
+ }
+ dataGenerator.save();
+ }
+
+ // 为表添加行
+ public void addRow(Collection row){
+ Collection dataRow = new ArrayList<>(row);
+ dataRows.add(new DataTableRow(dataRows.size() + 1, dataRow));
+ }
+
+ // 获得表的列数
+ public int getColsSize(){
+ return titleCollection.size();
+ }
+
+ // 获得表的行数
+ public int getRowsSize(){
+ return dataRows.size();
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/datamanager/DataTableRow.java b/src/main/java/com/codesdream/ase/component/datamanager/DataTableRow.java
new file mode 100644
index 0000000..ca07c1b
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/datamanager/DataTableRow.java
@@ -0,0 +1,21 @@
+package com.codesdream.ase.component.datamanager;
+
+import lombok.Data;
+
+import java.util.Collection;
+
+// 描述数据表的行
+@Data
+public class DataTableRow {
+ private int index;
+ private Collection row;
+
+ public DataTableRow(int index, Collection row){
+ this.index = index;
+ this.row = row;
+ }
+
+ public int getColsSize(){
+ return row.size();
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/datamanager/JSONParameter.java b/src/main/java/com/codesdream/ase/component/datamanager/JSONParameter.java
new file mode 100644
index 0000000..c87dea7
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/datamanager/JSONParameter.java
@@ -0,0 +1,89 @@
+package com.codesdream.ase.component.datamanager;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.codesdream.ase.component.json.respond.JSONBaseRespondObject;
+import com.codesdream.ase.exception.innerservererror.HandlingErrorsException;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.github.fge.jsonpatch.JsonPatch;
+import com.github.fge.jsonpatch.JsonPatchException;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.Optional;
+
+/**
+ * 处理JSON相关数据
+ */
+@Component
+public class JSONParameter {
+
+ // 处理Request Body
+ public String getRequestBody(HttpServletRequest request){
+ try {
+ StringBuilder stringBuilder = new StringBuilder();
+ BufferedReader reader = request.getReader();
+ reader.reset();
+ String line;
+ while ((line = reader.readLine()) != null)
+ stringBuilder.append(line);
+ return stringBuilder.toString();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ // 提取Request中的JSON数据
+ public Optional getJSONByRequest(HttpServletRequest request){
+ try {
+ JSONObject jsonParam = null;
+ String content = getRequestBody(request);
+ jsonParam = JSONObject.parseObject(content);
+ return Optional.ofNullable(jsonParam);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return Optional.empty();
+ }
+
+ }
+
+ // 根据JSON对象构造JSON字符串用于返回
+ public String getJSONString(JSONObject json){
+ return json.toJSONString();
+ }
+
+ // 根据Java对象构造JSON字符串用于返回
+ public String getJSONString(Object object){
+ return JSON.toJSONString(object);
+ }
+
+ // 由JSON对象获得对应的Java对象
+ public T getJavaObject(JSONObject json, Class type){
+ return json.toJavaObject(type);
+ }
+
+ // 由Request获得对应的Java对象(常用于Post请求中)
+ public Optional getJavaObjectByRequest(HttpServletRequest request, Class type){
+ Optional json = getJSONByRequest(request);
+ return json.map(jsonObject -> getJavaObject(jsonObject, type));
+ }
+
+ public T parsePathToObject(JsonPatch patch, T object){
+ try {
+ ObjectMapper mapper = new ObjectMapper();
+ JsonNode patched = patch.apply(mapper.convertValue(object, JsonNode.class));
+ return (T) mapper.treeToValue(patched, object.getClass());
+ } catch (JsonPatchException | JsonProcessingException e) {
+ throw new HandlingErrorsException(e.getMessage());
+ }
+
+ }
+
+}
diff --git a/src/main/java/com/codesdream/ase/component/datamanager/StringFile.java b/src/main/java/com/codesdream/ase/component/datamanager/StringFile.java
new file mode 100644
index 0000000..7ef0f5e
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/datamanager/StringFile.java
@@ -0,0 +1,12 @@
+package com.codesdream.ase.component.datamanager;
+
+import lombok.Data;
+
+// 储存字符串标识的文件,并可以转换为json进行传输
+@Data
+public class StringFile {
+ private String strData = null;
+ private String sha1Checker = null;
+ private Integer size = null;
+ private String type = "none";
+}
diff --git a/src/main/java/com/codesdream/ase/component/datamanager/StringFileGenerator.java b/src/main/java/com/codesdream/ase/component/datamanager/StringFileGenerator.java
new file mode 100644
index 0000000..a64fd32
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/datamanager/StringFileGenerator.java
@@ -0,0 +1,92 @@
+package com.codesdream.ase.component.datamanager;
+
+import com.codesdream.ase.component.auth.SHA1Encoder;
+import com.codesdream.ase.exception.StringFileConvertException;
+import com.sun.xml.internal.messaging.saaj.util.ByteInputStream;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.io.*;
+import java.util.Base64;
+import java.util.Optional;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+// 将文件处理成可发送的字符串文件对象
+@Component
+public class StringFileGenerator {
+
+ @Resource
+ private SHA1Encoder encoder;
+
+ // 用过读入流创建一个字符串文件
+ public Optional generateStringFile(InputStream stream){
+ StringFile file = new StringFile();
+ // 字符串内容计算
+ file.setStrData(generateFile2String(stream));
+ if(file.getStrData() == null) return Optional.empty();
+ // 相关校验值计算
+ file.setSha1Checker(generateSHA1Checker(file.getStrData()));
+ file.setSize(file.getStrData().length());
+ return Optional.of(file);
+ }
+
+ private byte[] readSteamAll(InputStream stream) {
+ try {
+ byte[] bytes = new byte[stream.available()];
+
+ //检查文件书否完全读取
+ if (stream.read(bytes) != bytes.length) return null;
+ else return bytes;
+ } catch (IOException e){
+ return null;
+ }
+ }
+
+ private String generateFile2String(InputStream stream){
+ ByteArrayOutputStream zipDataStream = new ByteArrayOutputStream();
+ try {
+ GZIPOutputStream gzipOutputStream = new GZIPOutputStream(zipDataStream);
+ byte[] bytes = readSteamAll(stream);
+ if(bytes == null) return null;
+ gzipOutputStream.write(bytes);
+ gzipOutputStream.close();
+ return Base64.getEncoder().encodeToString(zipDataStream.toByteArray());
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ // 生成字符串文件的校验码
+ private String generateSHA1Checker(String str){
+ return encoder.encode(str);
+ }
+
+ // 检查文件内容是否正确,包括大小与校验码
+ public boolean checkStringFile(StringFile file){
+ return file.getStrData().length() == file.getSize()
+ && encoder.match(file.getStrData(), file.getSha1Checker());
+ }
+
+ // 从字符串文件中读取真实的文件数据
+ public InputStream readFileString(StringFile file){
+ try {
+ // 字符串转换为二进制数据
+ byte[] bytes = Base64.getDecoder().decode(file.getStrData());
+ GZIPInputStream stream = new GZIPInputStream(new ByteArrayInputStream(bytes), bytes.length);
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+
+ // 数据解压缩
+ int readBits = 0;
+ byte[] rawBytes = new byte[1024];
+ while ((readBits = stream.read(rawBytes)) != -1) {
+ outputStream.write(rawBytes, 0, readBits);
+ }
+
+ stream.close();
+ return new ByteArrayInputStream(outputStream.toByteArray());
+ } catch (IOException e) {
+ throw new StringFileConvertException("Read FileString Failed");
+ }
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/error/ErrorResponse.java b/src/main/java/com/codesdream/ase/component/error/ErrorResponse.java
new file mode 100644
index 0000000..0aee4ca
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/error/ErrorResponse.java
@@ -0,0 +1,24 @@
+package com.codesdream.ase.component.error;
+
+import lombok.Data;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import java.util.List;
+
+@XmlRootElement(name = "error")
+public class ErrorResponse
+{
+ public ErrorResponse(String message, List details) {
+ super();
+ this.message = message;
+ this.details = details;
+ }
+
+ //General error message about nature of error
+ private String message;
+
+ //Specific errors in API request processing
+ private List details;
+
+ //Getter and setters
+}
diff --git a/src/main/java/com/codesdream/ase/component/json/JSONBaseObject.java b/src/main/java/com/codesdream/ase/component/json/JSONBaseObject.java
new file mode 100644
index 0000000..86504b4
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/json/JSONBaseObject.java
@@ -0,0 +1,12 @@
+package com.codesdream.ase.component.json;
+
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Date;
+
+// 所有有效的JSON对象模板
+@Slf4j
+public class JSONBaseObject {
+ Date time = new Date();
+
+}
diff --git a/src/main/java/com/codesdream/ase/component/json/form/RegisterForm.java b/src/main/java/com/codesdream/ase/component/json/form/RegisterForm.java
new file mode 100644
index 0000000..ccd73f0
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/json/form/RegisterForm.java
@@ -0,0 +1,5 @@
+package com.codesdream.ase.component.json.form;
+
+public class RegisterForm {
+
+}
diff --git a/src/main/java/com/codesdream/ase/component/json/model/JsonableFPC.java b/src/main/java/com/codesdream/ase/component/json/model/JsonableFPC.java
new file mode 100644
index 0000000..af57a91
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/json/model/JsonableFPC.java
@@ -0,0 +1,38 @@
+package com.codesdream.ase.component.json.model;
+
+import com.codesdream.ase.model.permission.FunctionalPermissionContainer;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import springfox.documentation.spring.web.json.Json;
+
+@Data
+@NoArgsConstructor
+@ApiModel("功能性权限容器")
+public class JsonableFPC {
+ private Integer id;
+
+ private String name;
+
+ private String description;
+
+ private boolean enabled;
+
+ private boolean deleted;
+
+ public JsonableFPC(FunctionalPermissionContainer fpc){
+ this.id = fpc.getId();
+ this.name = fpc.getName();
+ this.description = fpc.getDescription();
+ this.enabled = fpc.isEnabled();
+ this.deleted = fpc.isDeleted();
+ }
+
+ public FunctionalPermissionContainer parseObject(FunctionalPermissionContainer fpc){
+ fpc.setName(this.name);
+ fpc.setDescription(this.description);
+ fpc.setEnabled(this.enabled);
+ fpc.setDeleted(this.deleted);
+ return fpc;
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/json/model/JsonableFSR.java b/src/main/java/com/codesdream/ase/component/json/model/JsonableFSR.java
new file mode 100644
index 0000000..c72f333
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/json/model/JsonableFSR.java
@@ -0,0 +1,62 @@
+package com.codesdream.ase.component.json.model;
+
+import com.codesdream.ase.exception.notfound.NotFoundException;
+import com.codesdream.ase.model.permission.FunctionalPermissionContainer;
+import com.codesdream.ase.model.permission.FunctionalScopeRelation;
+import com.codesdream.ase.model.permission.ScopePermissionContainer;
+import com.codesdream.ase.service.IPermissionService;
+import com.codesdream.ase.service.PermissionService;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import javax.annotation.Resource;
+import java.util.Optional;
+
+@Data
+@NoArgsConstructor
+@ApiModel("单项权力")
+public class JsonableFSR {
+ private Integer id;
+
+ private String name;
+
+ private String description;
+
+ private Integer fpcId;
+
+ private Integer spcId;
+
+ @Resource
+ @ApiModelProperty(hidden = true)
+ private IPermissionService permissionService;
+
+ public JsonableFSR(FunctionalScopeRelation fsr){
+ this.id = fsr.getId();
+ this.name = fsr.getName();
+ this.description = fsr.getDescription();
+ this.fpcId = fsr.getFunctionalPermissionContainer().getId();
+ this.spcId = fsr.getScopePermissionContainer().getId();
+ }
+
+ public FunctionalScopeRelation parseObject(FunctionalScopeRelation fsr){
+ fsr.setName(this.name);
+ fsr.setDescription(this.description);
+ if(this.fpcId != null){
+ Optional fpc = permissionService.findFPC(this.fpcId);
+ if(!fpc.isPresent()) throw new NotFoundException(this.fpcId.toString());
+ fsr.setFunctionalPermissionContainer(fpc.get());
+ }
+
+ if(this.spcId != null){
+ Optional spc = permissionService.findSPC(this.spcId);
+ if(!spc.isPresent()) throw new NotFoundException(this.spcId.toString());
+ fsr.setScopePermissionContainer(spc.get());
+ }
+
+ return fsr;
+
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/json/model/JsonableFunction.java b/src/main/java/com/codesdream/ase/component/json/model/JsonableFunction.java
new file mode 100644
index 0000000..afdd13b
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/json/model/JsonableFunction.java
@@ -0,0 +1,60 @@
+package com.codesdream.ase.component.json.model;
+
+import com.codesdream.ase.exception.notfound.NotFoundException;
+import com.codesdream.ase.model.permission.Function;
+import com.codesdream.ase.service.IPermissionService;
+import com.codesdream.ase.service.PermissionService;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiParam;
+import io.swagger.models.auth.In;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import springfox.documentation.annotations.ApiIgnore;
+
+import javax.annotation.Resource;
+import java.util.Optional;
+
+@Data
+@NoArgsConstructor
+@ApiModel("功能")
+public class JsonableFunction {
+
+ private Integer id;
+
+ private String name;
+
+ private String description;
+
+ private Integer fatherId;
+
+ private String url;
+
+ @Resource
+ @ApiModelProperty(hidden = true)
+ private IPermissionService permissionService;
+
+ public JsonableFunction(Function function){
+ this.id = function.getId();
+ this.name = function.getName();
+ this.description = function.getDescription();
+ if(function.getFather() != null) {
+ this.fatherId = function.getFather().getId();
+ }
+ else this.fatherId = null;
+ this.url = function.getUrl();
+ }
+
+ public Function parseObject(Function function){
+ function.setName(this.name);
+ function.setDescription(this.description);
+ if(this.fatherId != null) {
+ Optional fatherFunction = permissionService.findFunction(this.fatherId);
+ if (!fatherFunction.isPresent()) throw new NotFoundException(fatherId.toString());
+ function.setFather(fatherFunction.get());
+ }
+ function.setUrl(this.url);
+
+ return function;
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/json/model/JsonablePCCList.java b/src/main/java/com/codesdream/ase/component/json/model/JsonablePCCList.java
new file mode 100644
index 0000000..870743e
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/json/model/JsonablePCCList.java
@@ -0,0 +1,22 @@
+package com.codesdream.ase.component.json.model;
+
+import com.codesdream.ase.model.permission.PermissionContainersCollection;
+import com.codesdream.ase.model.permission.Tag;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+@Data
+@NoArgsConstructor
+@ApiModel("权限容器集合列表")
+public class JsonablePCCList {
+ List pccIdList;
+
+ public JsonablePCCList(Tag tag){
+ for(PermissionContainersCollection pcc : tag.getPermissionContainersCollections()){
+ pccIdList.add(pcc.getId());
+ }
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/json/model/JsonableRoleList.java b/src/main/java/com/codesdream/ase/component/json/model/JsonableRoleList.java
new file mode 100644
index 0000000..efacf0f
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/json/model/JsonableRoleList.java
@@ -0,0 +1,26 @@
+package com.codesdream.ase.component.json.model;
+
+import com.codesdream.ase.model.permission.Function;
+import com.codesdream.ase.model.permission.FunctionalPermissionContainer;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.HashSet;
+import java.util.Set;
+
+@Data
+@NoArgsConstructor
+@ApiModel("功能性权限容器所属角色集合")
+public class JsonableRoleList {
+ private Integer id;
+ private Set functions = new HashSet<>();
+
+ public JsonableRoleList(FunctionalPermissionContainer fpc){
+ this.id = fpc.getId();
+ if(fpc.getFunctions() != null) {
+ for(Function function : fpc.getFunctions())
+ this.functions.add(function.getId());
+ }
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/json/model/JsonableSPC.java b/src/main/java/com/codesdream/ase/component/json/model/JsonableSPC.java
new file mode 100644
index 0000000..14fffd1
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/json/model/JsonableSPC.java
@@ -0,0 +1,39 @@
+package com.codesdream.ase.component.json.model;
+
+import com.codesdream.ase.model.permission.ScopePermissionContainer;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+@ApiModel("范围性权限容器")
+public class JsonableSPC {
+ private Integer id;
+
+ private String name;
+
+ private String description;
+
+ private boolean enabled;
+
+ private boolean deleted;
+
+ public JsonableSPC(ScopePermissionContainer spc){
+ this.id = spc.getId();
+ this.name = spc.getName();
+ this.description = spc.getDescription();
+ this.enabled = spc.isEnabled();
+ this.deleted = spc.isDeleted();
+ }
+
+ public ScopePermissionContainer parseObject(ScopePermissionContainer spc){
+ spc.setName(this.name);
+ spc.setDescription(this.description);
+ spc.setEnabled(this.enabled);
+ spc.setDeleted(this.deleted);
+
+ return spc;
+ }
+
+}
diff --git a/src/main/java/com/codesdream/ase/component/json/model/JsonableTag.java b/src/main/java/com/codesdream/ase/component/json/model/JsonableTag.java
new file mode 100644
index 0000000..8b518ec
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/json/model/JsonableTag.java
@@ -0,0 +1,48 @@
+package com.codesdream.ase.component.json.model;
+
+import com.codesdream.ase.model.permission.Tag;
+import com.codesdream.ase.model.permission.User;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import net.bytebuddy.implementation.bind.annotation.DefaultMethod;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@Data
+@NoArgsConstructor
+@ApiModel("标签")
+public class JsonableTag {
+ @ApiModelProperty(value = "标签id")
+ private Integer id = null;
+ @ApiModelProperty(value = "标签名", example = "系统管理员")
+ private String name;
+ @ApiModelProperty(value = "标签说明", example = "该系统的管理员")
+ private String description;
+
+ private boolean enabled;
+
+ private boolean deleted;
+
+
+ public JsonableTag(Tag tag){
+ this.id = tag.getId();
+ this.name = tag.getName();
+ this.description = tag.getDescription();
+ this.enabled = tag.isEnabled();
+ this.deleted = tag.isDeleted();
+ }
+
+ public Tag parseObject(Tag tag){
+ tag.setName(this.name);
+ tag.setDescription(this.description);
+ tag.setDeleted(this.deleted);
+ tag.setEnabled(this.enabled);
+ return tag;
+ }
+
+}
diff --git a/src/main/java/com/codesdream/ase/component/json/model/JsonableUidGetter.java b/src/main/java/com/codesdream/ase/component/json/model/JsonableUidGetter.java
new file mode 100644
index 0000000..9eee677
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/json/model/JsonableUidGetter.java
@@ -0,0 +1,6 @@
+package com.codesdream.ase.component.json.model;
+
+public class JsonableUidGetter {
+ private String checkType;
+ private String username;
+}
diff --git a/src/main/java/com/codesdream/ase/component/json/model/JsonableUser.java b/src/main/java/com/codesdream/ase/component/json/model/JsonableUser.java
new file mode 100644
index 0000000..ad72dec
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/json/model/JsonableUser.java
@@ -0,0 +1,33 @@
+package com.codesdream.ase.component.json.model;
+
+import com.codesdream.ase.model.permission.User;
+import com.codesdream.ase.model.permission.UserAuth;
+import com.codesdream.ase.model.permission.UserDetail;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@ApiModel("用户")
+@NoArgsConstructor
+public class JsonableUser {
+ private Integer id;
+ private String username;
+ private boolean enabled;
+ private boolean deleted;
+
+
+ public JsonableUser(User user){
+ this.id = user.getId();
+ this.username = user.getUsername();
+
+ this.enabled = user.isEnabled();
+ this.deleted= user.isDeleted();
+ }
+
+ public User parseObject(User user){
+ user.setEnabled(this.enabled);
+ user.setDeleted(this.deleted);
+ return user;
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/json/request/JSONBaseRequestObject.java b/src/main/java/com/codesdream/ase/component/json/request/JSONBaseRequestObject.java
new file mode 100644
index 0000000..910ee53
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/json/request/JSONBaseRequestObject.java
@@ -0,0 +1,13 @@
+package com.codesdream.ase.component.json.request;
+
+import com.codesdream.ase.component.json.JSONBaseObject;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Date;
+
+// 客户端请求的JSON模板
+@Slf4j
+public class JSONBaseRequestObject extends JSONBaseObject {
+ String signed;
+
+}
diff --git a/src/main/java/com/codesdream/ase/component/json/request/UserLeaveAuth.java b/src/main/java/com/codesdream/ase/component/json/request/UserLeaveAuth.java
new file mode 100644
index 0000000..92452c6
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/json/request/UserLeaveAuth.java
@@ -0,0 +1,16 @@
+package com.codesdream.ase.component.json.request;
+
+import lombok.Data;
+
+@Data
+public class UserLeaveAuth {
+ /*
+ 备注
+ */
+ private int id;
+ private String Comment;
+ /*
+ 审核结果
+ */
+ private String newStat;
+}
diff --git a/src/main/java/com/codesdream/ase/component/json/request/UserLeaveRequest.java b/src/main/java/com/codesdream/ase/component/json/request/UserLeaveRequest.java
new file mode 100644
index 0000000..df05547
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/json/request/UserLeaveRequest.java
@@ -0,0 +1,15 @@
+package com.codesdream.ase.component.json.request;
+
+import lombok.Data;
+
+import java.util.Date;
+@Data
+public class UserLeaveRequest {
+ private String UserId;//用户名
+ private String Type;//请假类型
+ private String Reason;//请假原因
+ private String Addon;//附件
+ private Date Starttime;//开始时间
+ private Date EndTime;//结束时间
+
+}
diff --git a/src/main/java/com/codesdream/ase/component/json/request/UserLoginChecker.java b/src/main/java/com/codesdream/ase/component/json/request/UserLoginChecker.java
new file mode 100644
index 0000000..0f706cf
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/json/request/UserLoginChecker.java
@@ -0,0 +1,13 @@
+package com.codesdream.ase.component.json.request;
+
+import lombok.Data;
+
+@Data
+public class UserLoginChecker {
+ // 请求类型
+ private String checkType;
+ private String username;
+ private String password;
+ private String clientCode;
+
+}
diff --git a/src/main/java/com/codesdream/ase/component/json/request/UserRegisterChecker.java b/src/main/java/com/codesdream/ase/component/json/request/UserRegisterChecker.java
new file mode 100644
index 0000000..491cda6
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/json/request/UserRegisterChecker.java
@@ -0,0 +1,19 @@
+package com.codesdream.ase.component.json.request;
+
+import lombok.Data;
+
+@Data
+public class UserRegisterChecker {
+
+ // 学号
+ private String studentId;
+
+ // 密码
+ private String password;
+
+ // 密保问题
+ private String userQuestion;
+
+ // 密保答案
+ private String userAnswer;
+}
diff --git a/src/main/java/com/codesdream/ase/component/json/request/UserSGettudentLeaveListRequest.java b/src/main/java/com/codesdream/ase/component/json/request/UserSGettudentLeaveListRequest.java
new file mode 100644
index 0000000..b97c771
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/json/request/UserSGettudentLeaveListRequest.java
@@ -0,0 +1,8 @@
+package com.codesdream.ase.component.json.request;
+
+import lombok.Data;
+
+@Data
+public class UserSGettudentLeaveListRequest {
+ private int studentId;
+}
diff --git a/src/main/java/com/codesdream/ase/component/json/respond/EmptyDataObjectRespond.java b/src/main/java/com/codesdream/ase/component/json/respond/EmptyDataObjectRespond.java
new file mode 100644
index 0000000..e3ae905
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/json/respond/EmptyDataObjectRespond.java
@@ -0,0 +1,5 @@
+package com.codesdream.ase.component.json.respond;
+
+public class EmptyDataObjectRespond {
+
+}
diff --git a/src/main/java/com/codesdream/ase/component/json/respond/ErrorInfoJSONRespond.java b/src/main/java/com/codesdream/ase/component/json/respond/ErrorInfoJSONRespond.java
new file mode 100644
index 0000000..4495e20
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/json/respond/ErrorInfoJSONRespond.java
@@ -0,0 +1,12 @@
+package com.codesdream.ase.component.json.respond;
+
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+public class ErrorInfoJSONRespond {
+ String exception = null;
+ String exceptionMessage = null;
+ Date date = null;
+}
diff --git a/src/main/java/com/codesdream/ase/component/json/respond/JSONBaseRespondObject.java b/src/main/java/com/codesdream/ase/component/json/respond/JSONBaseRespondObject.java
new file mode 100644
index 0000000..d1a2d2f
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/json/respond/JSONBaseRespondObject.java
@@ -0,0 +1,61 @@
+package com.codesdream.ase.component.json.respond;
+
+import com.codesdream.ase.component.json.JSONBaseObject;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.Date;
+
+// 服务端返回的JSON对象模板
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class JSONBaseRespondObject extends JSONBaseObject {
+
+ // 存放返回内容
+ private Object data = new EmptyDataObjectRespond();
+
+ // 存放响应信息提示
+ private String msg = "";
+
+ // 额外信息
+ private String info = null;
+
+ // 状态
+ private Integer status = 200;
+
+ public JSONBaseRespondObject(String msg){
+ super();
+ this.status = 200;
+ this.msg = msg;
+ }
+
+ public JSONBaseRespondObject(Integer status, String msg){
+ super();
+ this.status = status;
+ this.msg = msg;
+ }
+
+ public void setStatusNotFound(){
+ this.status = 404;
+ }
+
+ public void setStatusBadRequest(){
+ this.status = 400;
+ }
+
+ public void setStatusUnauthorized(){
+ this.status = 401;
+ }
+
+ public void setStatusForbidden(){
+ this.status = 403;
+ }
+
+ public void setStatusInternalServerError(){
+ this.status = 500;
+ }
+
+ public void setStatusOK(){
+ this.status = 200;
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/json/respond/JSONStandardFailedRespond.java b/src/main/java/com/codesdream/ase/component/json/respond/JSONStandardFailedRespond.java
new file mode 100644
index 0000000..ba1626f
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/json/respond/JSONStandardFailedRespond.java
@@ -0,0 +1,8 @@
+package com.codesdream.ase.component.json.respond;
+
+// 请求失败返回JSON
+public class JSONStandardFailedRespond extends JSONBaseRespondObject {
+ public JSONStandardFailedRespond(){
+ super(500, "failed");
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/json/respond/PermissionJSONRespond.java b/src/main/java/com/codesdream/ase/component/json/respond/PermissionJSONRespond.java
new file mode 100644
index 0000000..915804a
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/json/respond/PermissionJSONRespond.java
@@ -0,0 +1,16 @@
+package com.codesdream.ase.component.json.respond;
+
+import com.sun.org.apache.xpath.internal.operations.Bool;
+import lombok.Data;
+
+import java.util.List;
+import java.util.Set;
+
+@Data
+public class PermissionJSONRespond {
+ private Boolean tagExist = null;
+ private Boolean actionSuccess = null;
+ private Integer tagId = null;
+ private String tagName = null;
+ private Set users = null;
+}
diff --git a/src/main/java/com/codesdream/ase/component/json/respond/UserLoginCheckerJSONRespond.java b/src/main/java/com/codesdream/ase/component/json/respond/UserLoginCheckerJSONRespond.java
new file mode 100644
index 0000000..e12c077
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/json/respond/UserLoginCheckerJSONRespond.java
@@ -0,0 +1,17 @@
+package com.codesdream.ase.component.json.respond;
+
+import com.sun.org.apache.xpath.internal.operations.Bool;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+public class UserLoginCheckerJSONRespond {
+ Boolean userExist = null;
+ Boolean userBanned = null;
+ Boolean loginStatus = null;
+ String respondInformation = null;
+ String token = null;
+ String uid = null;
+ String pvc = null;
+
+}
diff --git a/src/main/java/com/codesdream/ase/component/json/respond/UserLoginTokenJSONRespond.java b/src/main/java/com/codesdream/ase/component/json/respond/UserLoginTokenJSONRespond.java
new file mode 100644
index 0000000..43272ab
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/json/respond/UserLoginTokenJSONRespond.java
@@ -0,0 +1,6 @@
+package com.codesdream.ase.component.json.respond;
+
+// 返回登录后的授权码(用于客户端生成签名)
+public class UserLoginTokenJSONRespond {
+ String token;
+}
diff --git a/src/main/java/com/codesdream/ase/component/permission/UserAuthoritiesGenerator.java b/src/main/java/com/codesdream/ase/component/permission/UserAuthoritiesGenerator.java
new file mode 100644
index 0000000..ded5ff7
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/permission/UserAuthoritiesGenerator.java
@@ -0,0 +1,19 @@
+package com.codesdream.ase.component.permission;
+
+import com.codesdream.ase.model.permission.User;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.Collection;
+
+@Component
+public class UserAuthoritiesGenerator {
+ @Resource
+ UserFunctionsListGenerator userFunctionsListGenerator;
+
+ public Collection extends GrantedAuthority> grantedAuthorities(User user){
+ return userFunctionsListGenerator.generateRoles(user);
+ }
+
+}
diff --git a/src/main/java/com/codesdream/ase/component/permission/UserFPCListGenerator.java b/src/main/java/com/codesdream/ase/component/permission/UserFPCListGenerator.java
new file mode 100644
index 0000000..964973d
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/permission/UserFPCListGenerator.java
@@ -0,0 +1,41 @@
+package com.codesdream.ase.component.permission;
+
+import com.codesdream.ase.model.permission.*;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * 生成功能性权限容器列表
+ */
+@Component
+public class UserFPCListGenerator {
+ @Resource
+ private UserFSRGenerator fsrListGenerator;
+
+ public Collection generateFPC(
+ Collection functionalScopeRelations){
+ Collection functionalPermissionContainers
+ = new ArrayList<>();
+
+ for (FunctionalScopeRelation functionalScopeRelation : functionalScopeRelations){
+ functionalPermissionContainers.add(functionalScopeRelation.getFunctionalPermissionContainer());
+ }
+
+ return functionalPermissionContainers;
+ }
+
+ public Collection generateFPCs(User user){
+ return generateFPC(
+ fsrListGenerator.generateFSRs(user)
+ );
+ }
+
+ public Collection generateFPCs(String username){
+ return generateFPC(
+ fsrListGenerator.generateFSRs(username)
+ );
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/permission/UserFSRGenerator.java b/src/main/java/com/codesdream/ase/component/permission/UserFSRGenerator.java
new file mode 100644
index 0000000..077dddd
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/permission/UserFSRGenerator.java
@@ -0,0 +1,45 @@
+package com.codesdream.ase.component.permission;
+
+import com.codesdream.ase.model.permission.FunctionalScopeRelation;
+import com.codesdream.ase.model.permission.PermissionContainersCollection;
+import com.codesdream.ase.model.permission.User;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * 生成功能性权限容器与范围性权限容器关联对列表
+ */
+@Component
+public class UserFSRGenerator {
+
+ @Resource
+ private UserPCCListGenerator userPCCListGenerator;
+
+
+ public Collection generateFSRs(
+ Collection pccs){
+ Collection fsrs =
+ new ArrayList<>();
+
+ for(PermissionContainersCollection pcc : pccs){
+ fsrs.addAll(pcc.getFunctionalScopeRelations());
+ }
+
+ return fsrs;
+ }
+
+ public Collection generateFSRs(
+ User user){
+ return generateFSRs(
+ userPCCListGenerator.generatePCCs(user));
+ }
+
+ public Collection generateFSRs(
+ String username){
+ return generateFSRs(
+ userPCCListGenerator.generatePCCs(username));
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/permission/UserFunctionsListGenerator.java b/src/main/java/com/codesdream/ase/component/permission/UserFunctionsListGenerator.java
new file mode 100644
index 0000000..337ddad
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/permission/UserFunctionsListGenerator.java
@@ -0,0 +1,45 @@
+package com.codesdream.ase.component.permission;
+
+import com.codesdream.ase.model.permission.Function;
+import com.codesdream.ase.model.permission.FunctionalPermissionContainer;
+import com.codesdream.ase.model.permission.User;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * 生成用户访问权限角色列表
+ */
+@Component
+public class UserFunctionsListGenerator {
+ @Resource
+ private UserFPCListGenerator fpcListGenerator;
+
+ public Collection generateRoles(
+ Collection fpcs){
+ Collection authorities = new ArrayList<>();
+ for(FunctionalPermissionContainer fpc :fpcs){
+ for(Function function :fpc.getFunctions()){
+ authorities.add(new SimpleGrantedAuthority(function.getName()));
+ }
+ }
+ return authorities;
+ }
+
+ public Collection generateRoles(String username){
+ return generateRoles(
+ fpcListGenerator.generateFPCs(username)
+ );
+ }
+
+ public Collection generateRoles(User user){
+
+ return generateRoles(
+ fpcListGenerator.generateFPCs(user)
+ );
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/permission/UserPCCListGenerator.java b/src/main/java/com/codesdream/ase/component/permission/UserPCCListGenerator.java
new file mode 100644
index 0000000..ffb22f9
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/permission/UserPCCListGenerator.java
@@ -0,0 +1,39 @@
+package com.codesdream.ase.component.permission;
+
+import com.codesdream.ase.model.permission.PermissionContainersCollection;
+import com.codesdream.ase.model.permission.Tag;
+import com.codesdream.ase.model.permission.User;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * 生成权限容器集合列表
+ */
+@Component
+public class UserPCCListGenerator {
+ @Resource
+ private UserTagsListGenerator userTagsListGenerator;
+
+ public Collection generatePCCs(
+ Collection tags){
+ Collection pccs =
+ new ArrayList<>();
+ for(Tag tag : tags){
+ pccs.addAll(tag.getPermissionContainersCollections());
+ }
+ return pccs;
+ }
+
+ public Collection generatePCCs(
+ User user) {
+ return generatePCCs(userTagsListGenerator.generateTags(user));
+ }
+
+ public Collection generatePCCs(
+ String username){
+ return generatePCCs(userTagsListGenerator.generateTags(username));
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/permission/UserTagsListGenerator.java b/src/main/java/com/codesdream/ase/component/permission/UserTagsListGenerator.java
new file mode 100644
index 0000000..d53bfe4
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/permission/UserTagsListGenerator.java
@@ -0,0 +1,54 @@
+package com.codesdream.ase.component.permission;
+
+import com.codesdream.ase.model.permission.ScopePermissionContainer;
+import com.codesdream.ase.model.permission.Tag;
+import com.codesdream.ase.model.permission.User;
+import com.codesdream.ase.repository.permission.UserRepository;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Optional;
+
+/**
+ * 生成用户标签列表
+ */
+@Component
+public class UserTagsListGenerator {
+ @Resource
+ UserRepository userRepository;
+
+ public Collection generateTags(ScopePermissionContainer spc){
+ return new ArrayList<>(spc.getTags());
+ }
+
+ public Collection generateTags(User user){
+ return new ArrayList(user.getTags());
+ }
+
+ public Collection generateTags(String username){
+ Optional user = userRepository.findByUsername(username);
+ // 检查用户是否存在
+ if(!user.isPresent()) throw new RuntimeException("User Not Found");
+
+ return generateTags(user.get());
+ }
+
+ public Collection generateTagsName(User user){
+ Collection tagsName = new ArrayList<>();
+ Collection tags = generateTags(user);
+ for(Tag tag : tags){
+ tagsName.add(tag.getName());
+ }
+ return tagsName;
+ }
+
+ public Collection generateTagsName(String username){
+ Optional user = userRepository.findByUsername(username);
+
+ if(!user.isPresent()) throw new RuntimeException("User Not Found");
+
+ return generateTagsName(user.get());
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/permission/UsersListGenerator.java b/src/main/java/com/codesdream/ase/component/permission/UsersListGenerator.java
new file mode 100644
index 0000000..db0abf1
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/permission/UsersListGenerator.java
@@ -0,0 +1,22 @@
+package com.codesdream.ase.component.permission;
+
+import com.codesdream.ase.model.permission.Tag;
+import com.codesdream.ase.model.permission.User;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * 生成用户列表
+ */
+@Component
+public class UsersListGenerator {
+ public Collection generateUsers(Collection tags){
+ Collection users = new ArrayList<>();
+ for(Tag tag : tags){
+ users.addAll(tag.getUsers());
+ }
+ return users;
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/configure/ASESwaggerConfigure.java b/src/main/java/com/codesdream/ase/configure/ASESwaggerConfigure.java
new file mode 100644
index 0000000..1e2061e
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/configure/ASESwaggerConfigure.java
@@ -0,0 +1,57 @@
+package com.codesdream.ase.configure;
+
+import com.google.common.collect.Sets;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.core.parameters.P;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.ParameterBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.schema.ModelRef;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.Parameter;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+@Configuration
+@EnableSwagger2
+public class ASESwaggerConfigure {
+ @Bean
+ public Docket createRestApi() {
+
+ List pars = new ArrayList();
+
+ pars.add(new ParameterBuilder().name("username").description("真实用户名").hidden(true).order(1)
+ .modelRef(new ModelRef("string")).parameterType("header")
+ .required(false).defaultValue("u_id_88883b9e023c8824310760d8bb8b6542e5a3f16a0d67253214e01ee7ab0e96a1").build());
+ pars.add(new ParameterBuilder().name("signed").description("客户端签名").hidden(true).order(2)
+ .modelRef(new ModelRef("string")).parameterType("header")
+ .required(false).defaultValue("6d4923fca4dcb51f67b85e54a23a8d763d9e02af").build());
+ pars.add(new ParameterBuilder().name("timestamp").description("时间戳").hidden(true).order(3)
+ .modelRef(new ModelRef("string")).parameterType("header")
+ .required(false).defaultValue(Long.toString(new Date().getTime())).build());
+
+ return new Docket(DocumentationType.SWAGGER_2)
+ .protocols(Sets.newHashSet("http"))
+ .apiInfo(apiInfo())
+ .select()
+ .apis(RequestHandlerSelectors.basePackage("com.codesdream.ase.controller"))
+ .paths(PathSelectors.any())
+ .build()
+ .globalOperationParameters(pars);
+ }
+
+ private ApiInfo apiInfo() {
+ return new ApiInfoBuilder()
+ .title("全员育人管理系统后端接口定义")
+ .version("0.0.1")
+ .description("用于对后端接口进行说明")
+ .build();
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/configure/ActivityFormConfigure.java b/src/main/java/com/codesdream/ase/configure/ActivityFormConfigure.java
new file mode 100644
index 0000000..48e34b9
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/configure/ActivityFormConfigure.java
@@ -0,0 +1,37 @@
+package com.codesdream.ase.configure;
+
+import lombok.Data;
+import org.springframework.stereotype.Component;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+@Component
+public class ActivityFormConfigure {
+
+ private final HashSet stdActivityForm = new HashSet(){{
+ add("title");
+ add("creator");
+ add("type");
+ add("description");
+ add("cycle");
+ add("volunteers");
+ add("participate");
+ add("sign");
+ add("visible");
+ add("inform");
+ add("start-time");
+ add("remind");
+ add("enclosure");
+ add("chief-manager");
+ add("assist-manager");
+ add("attendance");
+ }};
+
+
+ public HashSet getStdActivityForm() {
+ return stdActivityForm;
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/configure/AppConfigure.java b/src/main/java/com/codesdream/ase/configure/AppConfigure.java
new file mode 100644
index 0000000..2d606c6
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/configure/AppConfigure.java
@@ -0,0 +1,43 @@
+package com.codesdream.ase.configure;
+
+import org.springframework.stereotype.Component;
+
+/**
+ * 应用程序常用配置信息
+ * 用于常见的应用程序本身的相关信息的引用
+ */
+@Component
+public class AppConfigure {
+ /**
+ * 获得应用程序的中文名
+ * @return 返回包含完整内容的字符串
+ */
+ public String getName() {
+ return "全员育人";
+ }
+
+ /**
+ * 获得应用程序的版本号
+ * @return 返回版本号内容的字符串
+ */
+ public String getVersion() {
+ return "0.0.1_200204";
+ }
+
+ /**
+ * 获得应用程序的英文名
+ * @return 返回包含完整内容的字符串
+ */
+ public String getEnglishName() {
+ return "All Staff Education";
+ }
+
+ /**
+ * 获得开发小组的名称
+ * @return 包含完整内容的字符串
+ */
+ public String getOrganization() {
+ return "全员育人WEB端开发组";
+ }
+
+}
diff --git a/src/main/java/com/codesdream/ase/configure/ComponentsConfigure.java b/src/main/java/com/codesdream/ase/configure/ComponentsConfigure.java
new file mode 100644
index 0000000..85708af
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/configure/ComponentsConfigure.java
@@ -0,0 +1,11 @@
+package com.codesdream.ase.configure;
+
+import com.codesdream.ase.component.*;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class ComponentsConfigure {
+
+
+}
diff --git a/src/main/java/com/codesdream/ase/configure/CustomWebSecurityConfig.java b/src/main/java/com/codesdream/ase/configure/CustomWebSecurityConfig.java
new file mode 100644
index 0000000..0e3d814
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/configure/CustomWebSecurityConfig.java
@@ -0,0 +1,138 @@
+package com.codesdream.ase.configure;
+
+import com.codesdream.ase.component.auth.*;
+import com.codesdream.ase.service.ASEUserDetailsService;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.builders.WebSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.core.session.SessionRegistry;
+import org.springframework.security.core.session.SessionRegistryImpl;
+import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy;
+import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
+import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+
+import javax.annotation.Resource;
+
+/**
+ * Spring Security 配置类
+ * 用于Spring Security相关参数的配置
+ */
+@Configuration
+@EnableWebSecurity
+public class CustomWebSecurityConfig extends WebSecurityConfigurerAdapter {
+
+ @Resource
+ ASEUserDetailsService aseUserDetailService;
+
+ @Resource
+ ASEPasswordEncoder asePasswordEncoder;
+
+ @Resource
+ ASESecurityAuthenticationProvider aseSecurityAuthenticationProvider;
+
+ @Resource
+ ASEAuthenticationSuccessHandler successHandler;
+
+ @Resource
+ ASEAuthenticationFailureHandler failureHandler;
+
+ @Resource
+ ASEAuthenticationEntryPoint authenticationEntryPoint;
+
+ @Resource
+ ASEAccessDeniedHandler accessDeniedHandler;
+
+ @Resource
+ ASESecurityInterceptor securityInterceptor;
+
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http
+ .authorizeRequests()
+ .anyRequest().authenticated()
+ .and()
+ .csrf().disable()
+ .logout().permitAll();
+
+ http.exceptionHandling()
+ .authenticationEntryPoint(authenticationEntryPoint)
+ .accessDeniedHandler(accessDeniedHandler);
+
+ // 替换掉原有的UsernamePasswordAuthenticationFilter
+ http.addFilterAt(aseUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
+ .addFilterBefore(asejsonTokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
+ .addFilterAt(securityInterceptor, FilterSecurityInterceptor.class);
+
+ http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
+
+ }
+
+ @Override
+ protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+ auth.authenticationProvider(aseSecurityAuthenticationProvider)
+ .userDetailsService(aseUserDetailService)
+ .passwordEncoder(asePasswordEncoder);
+ }
+
+ @Override
+ public void configure(WebSecurity web) throws Exception {
+ web
+ .ignoring()
+ .antMatchers(
+ "/assets/**",
+ "/register/**",
+ "/forget/**",
+ "/not_found/**",
+ "/error/**",
+ "/login/**",
+ "/swagger-ui.html",
+ "/webjars/**",
+ "/swagger-resources/**",
+ "/v2/api-docs",
+ "/configuration/ui",
+ "/configuration/security");
+ }
+
+ //注册自定义的UsernamePasswordAuthenticationFilter
+ @Bean
+ ASEJSONTokenAuthenticationFilter asejsonTokenAuthenticationFilter() throws Exception {
+ return new ASEJSONTokenAuthenticationFilter();
+ }
+
+ //注册自定义的UsernamePasswordAuthenticationFilter
+ @Bean
+ ASEUsernamePasswordAuthenticationFilter aseUsernamePasswordAuthenticationFilter() throws Exception {
+ ASEUsernamePasswordAuthenticationFilter filter = new ASEUsernamePasswordAuthenticationFilter();
+ filter.setAuthenticationSuccessHandler(successHandler);
+ filter.setAuthenticationFailureHandler(failureHandler);
+ filter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy(sessionRegistry()));
+ filter.setAllowSessionCreation(true);
+ filter.setRequiresAuthenticationRequestMatcher(
+ new AntPathRequestMatcher("/login/token", "POST"));
+
+ filter.setAuthenticationManager(authenticationManagerBean());
+ return filter;
+ }
+
+ @Bean
+ public SessionRegistry sessionRegistry() {
+ return new SessionRegistryImpl();
+ }
+
+
+ @Bean
+ public SessionAuthenticationStrategy sessionAuthenticationStrategy(SessionRegistry sessionRegistry){
+ return new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry){{
+ setMaximumSessions(1);
+ }};
+ }
+
+}
diff --git a/src/main/java/com/codesdream/ase/configure/GlobalConfigure.java b/src/main/java/com/codesdream/ase/configure/GlobalConfigure.java
new file mode 100644
index 0000000..a6efbb8
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/configure/GlobalConfigure.java
@@ -0,0 +1,15 @@
+package com.codesdream.ase.configure;
+
+
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Spring 框架全局配置类
+ * 主要用于注册或者管理Bean
+ */
+@Configuration
+public class GlobalConfigure {
+
+
+
+}
diff --git a/src/main/java/com/codesdream/ase/controller/APIController.java b/src/main/java/com/codesdream/ase/controller/APIController.java
new file mode 100644
index 0000000..f2a574d
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/controller/APIController.java
@@ -0,0 +1,14 @@
+package com.codesdream.ase.controller;
+
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/api")
+public class APIController {
+
+ @RequestMapping("hello")
+ String hello(){
+ return "hello";
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/controller/ASEController.java b/src/main/java/com/codesdream/ase/controller/ASEController.java
new file mode 100644
index 0000000..ee66e6c
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/controller/ASEController.java
@@ -0,0 +1,26 @@
+package com.codesdream.ase.controller;
+
+
+import com.codesdream.ase.configure.GlobalConfigure;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import javax.annotation.Resource;
+import java.security.Principal;
+
+/**
+ * 管理界面主页控制器类
+ * 现阶段主要用于管理界面主页
+ */
+@Controller
+public class ASEController {
+ @Resource
+ GlobalConfigure globalConfigure;
+
+ @RequestMapping(value = "/")
+ public String printIndex(Model model, Principal principal){
+ model.addAttribute("username",principal.getName());
+ return "index";
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/controller/ASEControllerAdvice.java b/src/main/java/com/codesdream/ase/controller/ASEControllerAdvice.java
new file mode 100644
index 0000000..281ced4
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/controller/ASEControllerAdvice.java
@@ -0,0 +1,77 @@
+package com.codesdream.ase.controller;
+
+import com.codesdream.ase.component.api.QuickJSONRespond;
+import com.codesdream.ase.component.json.respond.ErrorInfoJSONRespond;
+import com.codesdream.ase.exception.badrequest.AlreadyExistException;
+import com.codesdream.ase.exception.badrequest.IllegalException;
+import com.codesdream.ase.exception.conflict.RelatedObjectsExistException;
+import com.codesdream.ase.exception.innerservererror.FormatException;
+import com.codesdream.ase.exception.innerservererror.HandlingErrorsException;
+import com.codesdream.ase.exception.innerservererror.RuntimeIOException;
+import com.codesdream.ase.exception.notfound.NotFoundException;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+import javax.annotation.Resource;
+import java.util.Date;
+
+@RestControllerAdvice
+public class ASEControllerAdvice {
+
+ @Resource
+ private QuickJSONRespond quickJSONRespond;
+
+ @ExceptionHandler(value = {
+ NullPointerException.class,
+ AlreadyExistException.class,
+ IllegalException.class
+ })
+ public ResponseEntity