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..74f4de4 --- /dev/null +++ b/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,118 @@ +/* + * Copyright 2012-2019 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.5"; + /** + * 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..0d5e649 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..7d59a01 --- /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.2/apache-maven-3.6.2-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar diff --git a/mvnw b/mvnw new file mode 100644 index 0000000..822f699 --- /dev/null +++ b/mvnw @@ -0,0 +1,322 @@ +#!/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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ]; then + + if [ -f /etc/mavenrc ]; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ]; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false +darwin=false +mingw=false +case "$(uname)" in +CYGWIN*) cygwin=true ;; +MINGW*) mingw=true ;; +Darwin*) + darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="$(/usr/libexec/java_home)" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ]; then + if [ -r /etc/gentoo-release ]; then + JAVA_HOME=$(java-config --jre-home) + fi +fi + +if [ -z "$M2_HOME" ]; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ]; do + ls=$(ls -ld "$PRG") + link=$(expr "$ls" : '.*-> \(.*\)$') + if expr "$link" : '/.*' >/dev/null; then + PRG="$link" + else + PRG="$(dirname "$PRG")/$link" + fi + done + + saveddir=$(pwd) + + M2_HOME=$(dirname "$PRG")/.. + + # make it fully qualified + M2_HOME=$(cd "$M2_HOME" && pwd) + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=$(cygpath --unix "$M2_HOME") + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --unix "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --unix "$CLASSPATH") +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw; then + [ -n "$M2_HOME" ] && + M2_HOME="$( ( + cd "$M2_HOME" + pwd + ))" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="$( ( + cd "$JAVA_HOME" + pwd + ))" +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.5/maven-wrapper-0.5.5.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.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..84d60ab --- /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 Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing 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.5/maven-wrapper-0.5.5.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.5/maven-wrapper-0.5.5.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..ce2d8f4 --- /dev/null +++ b/pom.xml @@ -0,0 +1,159 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.2.1.RELEASE + + + com.rymcu + vertical + 0.0.1 + war + vertical + 一个用 Java 实现的现代化社区(论坛 / BBS / 社交网络 / 博客)平台,“下一代的社区系统,为未来而构建”。 + + + 1.8 + + + + + org.springframework.boot + spring-boot-starter-data-redis + + + org.springframework.boot + spring-boot-starter-mail + + + org.springframework.boot + spring-boot-starter-web + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 2.1.1 + + + + mysql + mysql-connector-java + runtime + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-tomcat + provided + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + org.springframework.boot + spring-boot-starter-aop + + + + tk.mybatis + mapper + 4.1.5 + + + net.sourceforge.jtds + jtds + 1.3.1 + + + + com.github.pagehelper + pagehelper + 5.1.10 + + + + com.alibaba + fastjson + 1.2.60 + + + + org.apache.shiro + shiro-spring + 1.4.1 + + + + org.crazycake + shiro-redis + 3.2.3 + + + + commons-lang + commons-lang + 2.6 + + + commons-io + commons-io + 2.6 + + + org.apache.commons + commons-text + 1.8 + + + io.jsonwebtoken + jjwt + 0.9.1 + + + + com.alibaba + druid-spring-boot-starter + 1.1.20 + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + + + + + src/main/java + + **/*.xml + **/**/*.xml + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/src/main/java/com/rymcu/vertical/ServletInitializer.java b/src/main/java/com/rymcu/vertical/ServletInitializer.java new file mode 100644 index 0000000..9db7b2d --- /dev/null +++ b/src/main/java/com/rymcu/vertical/ServletInitializer.java @@ -0,0 +1,13 @@ +package com.rymcu.vertical; + +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +public class ServletInitializer extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(VerticalApplication.class); + } + +} diff --git a/src/main/java/com/rymcu/vertical/VerticalApplication.java b/src/main/java/com/rymcu/vertical/VerticalApplication.java new file mode 100644 index 0000000..207d93f --- /dev/null +++ b/src/main/java/com/rymcu/vertical/VerticalApplication.java @@ -0,0 +1,13 @@ +package com.rymcu.vertical; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class VerticalApplication { + + public static void main(String[] args) { + SpringApplication.run(VerticalApplication.class, args); + } + +} diff --git a/src/main/java/com/rymcu/vertical/config/HpeisExceptionHandler.java b/src/main/java/com/rymcu/vertical/config/HpeisExceptionHandler.java new file mode 100644 index 0000000..2400b10 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/config/HpeisExceptionHandler.java @@ -0,0 +1,125 @@ +package com.rymcu.vertical.config; + +import com.alibaba.fastjson.support.spring.FastJsonJsonView; +import com.rymcu.vertical.core.exception.ServiceException; +import com.rymcu.vertical.core.result.GlobalResult; +import com.rymcu.vertical.core.result.ResultCode; +import org.apache.shiro.authz.UnauthenticatedException; +import org.apache.shiro.authz.UnauthorizedException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.NoHandlerFoundException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.HashMap; +import java.util.Map; + +/** + * 全局异常处理器 + * */ +@RestControllerAdvice +public class HpeisExceptionHandler { + + private final Logger logger = LoggerFactory.getLogger(HpeisExceptionHandler.class); + + @SuppressWarnings("Duplicates") + @ExceptionHandler(Exception.class) + public Object errorHandler(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex){ + if(isAjax(request)){ + GlobalResult result = new GlobalResult(); + if (ex instanceof UnauthenticatedException) { + result.setCode(1000001); + result.setMessage("token错误"); + logger.info("token错误"); + } else if (ex instanceof UnauthorizedException) { + result.setCode(1000002); + result.setMessage("用户无权限"); + logger.info("用户无权限"); + }else if (ex instanceof ServiceException) {//业务失败的异常,如“账号或密码错误” + result.setCode(((ServiceException) ex).getCode()); + result.setMessage(ex.getMessage()); + logger.info(ex.getMessage()); + } else if (ex instanceof NoHandlerFoundException) { + result.setCode(ResultCode.NOT_FOUND.getCode()); + result.setMessage(ResultCode.NOT_FOUND.getMessage()); + } else if (ex instanceof ServletException) { + result.setCode(ResultCode.FAIL.getCode()); + result.setMessage(ex.getMessage()); + }else { + //系统内部异常,不返回给客户端,内部记录错误日志 + result.setCode(ResultCode.INTERNAL_SERVER_ERROR.getCode()); + String message; + if (handler instanceof HandlerMethod) { + HandlerMethod handlerMethod = (HandlerMethod) handler; + message = String.format("接口 [%s] 出现异常,方法:%s.%s,异常摘要:%s", + request.getRequestURI(), + handlerMethod.getBean().getClass().getName(), + handlerMethod.getMethod().getName(), + ex.getMessage()); + } else { + message = ex.getMessage(); + } + result.setMessage("操作失败"); + logger.error(message, ex); + } + result.setSuccess(false); + return result; + }else { + ModelAndView mv = new ModelAndView(); + FastJsonJsonView view = new FastJsonJsonView(); + Map attributes = new HashMap(); + if (ex instanceof UnauthenticatedException) { + attributes.put("code", "1000001"); + attributes.put("message", "token错误"); + } else if (ex instanceof UnauthorizedException) { + attributes.put("code", "1000002"); + attributes.put("message", "用户无权限"); + } else if (ex instanceof ServiceException) {//业务失败的异常,如“账号或密码错误” + attributes.put("code",((ServiceException) ex).getCode()); + attributes.put("message",ex.getMessage()); + logger.info(ex.getMessage()); + } else if (ex instanceof NoHandlerFoundException) { + attributes.put("code",ResultCode.NOT_FOUND.getCode()); + attributes.put("message",ResultCode.NOT_FOUND.getMessage()); + } else if (ex instanceof ServletException) { + attributes.put("code",ResultCode.FAIL.getCode()); + attributes.put("message",ex.getMessage()); + }else { + //系统内部异常,不返回给客户端,内部记录错误日志 + attributes.put("code",ResultCode.INTERNAL_SERVER_ERROR.getCode()); + String message; + if (handler instanceof HandlerMethod) { + HandlerMethod handlerMethod = (HandlerMethod) handler; + message = String.format("接口 [%s] 出现异常,方法:%s.%s,异常摘要:%s", + request.getRequestURI(), + handlerMethod.getBean().getClass().getName(), + handlerMethod.getMethod().getName(), + ex.getMessage()); + } else { + message = ex.getMessage(); + } + logger.error(message, ex); + attributes.put("message","操作失败"); + } + attributes.put("success",false); + view.setAttributesMap(attributes); + mv.setView(view); + return mv; + } + } + + private boolean isAjax(HttpServletRequest request) { + String requestedWith = request.getHeader("x-requested-with"); + if (requestedWith != null && requestedWith.equalsIgnoreCase("XMLHttpRequest")) { + return true; + } else { + return false; + } + } +} diff --git a/src/main/java/com/rymcu/vertical/config/HpeisSessionManager.java b/src/main/java/com/rymcu/vertical/config/HpeisSessionManager.java new file mode 100644 index 0000000..93fbb4a --- /dev/null +++ b/src/main/java/com/rymcu/vertical/config/HpeisSessionManager.java @@ -0,0 +1,36 @@ +package com.rymcu.vertical.config; + +import org.apache.shiro.web.servlet.ShiroHttpServletRequest; +import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; +import org.apache.shiro.web.util.WebUtils; +import org.springframework.util.StringUtils; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import java.io.Serializable; + +public class HpeisSessionManager extends DefaultWebSessionManager { + private static final String AUTHORIZATION = "Authorization"; + + private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request"; + + public HpeisSessionManager() { + super(); + } + + @Override + protected Serializable getSessionId(ServletRequest request, ServletResponse response) { + String id = WebUtils.toHttp(request).getHeader(AUTHORIZATION); + // 如果请求头中有 Authorization 则其值为 sessionId + if (!StringUtils.isEmpty(id)) { + request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE); + request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id); + request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE); + + return id; + } else { + // 否则按默认规则从 cookie 取 sessionId + return super.getSessionId(request, response); + } + } +} diff --git a/src/main/java/com/rymcu/vertical/config/HpeisShiroFilterFactoryBean.java b/src/main/java/com/rymcu/vertical/config/HpeisShiroFilterFactoryBean.java new file mode 100644 index 0000000..16c3afd --- /dev/null +++ b/src/main/java/com/rymcu/vertical/config/HpeisShiroFilterFactoryBean.java @@ -0,0 +1,98 @@ +package com.rymcu.vertical.config; + +import org.apache.shiro.mgt.SecurityManager; +import org.apache.shiro.spring.web.ShiroFilterFactoryBean; +import org.apache.shiro.web.filter.mgt.FilterChainManager; +import org.apache.shiro.web.filter.mgt.FilterChainResolver; +import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver; +import org.apache.shiro.web.mgt.WebSecurityManager; +import org.apache.shiro.web.servlet.AbstractShiroFilter; +import org.springframework.beans.factory.BeanInitializationException; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +/** + * Shiro静态资源配置 + * */ +public class HpeisShiroFilterFactoryBean extends ShiroFilterFactoryBean { + // 对ShiroFilter来说,需要直接忽略的请求 + private Set ignoreExt; + + public HpeisShiroFilterFactoryBean() { + super(); + ignoreExt = new HashSet<>(); + ignoreExt.add(".svg"); + ignoreExt.add(".jpg"); + ignoreExt.add(".png"); + ignoreExt.add(".gif"); + ignoreExt.add(".bmp"); + ignoreExt.add(".js"); + ignoreExt.add(".css"); + } + + @Override + protected AbstractShiroFilter createInstance() throws Exception { + + SecurityManager securityManager = getSecurityManager(); + if (securityManager == null) { + String msg = "SecurityManager property must be set."; + throw new BeanInitializationException(msg); + } + + if (!(securityManager instanceof WebSecurityManager)) { + String msg = "The security manager does not implement the WebSecurityManager interface."; + throw new BeanInitializationException(msg); + } + + FilterChainManager manager = createFilterChainManager(); + + PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver(); + chainResolver.setFilterChainManager(manager); + + return new HpeisSpringShiroFilter((WebSecurityManager) securityManager, chainResolver); + } + + private final class HpeisSpringShiroFilter extends AbstractShiroFilter { + + protected HpeisSpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver) { + super(); + if (webSecurityManager == null) { + throw new IllegalArgumentException("WebSecurityManager property cannot be null."); + } + setSecurityManager(webSecurityManager); + if (resolver != null) { + setFilterChainResolver(resolver); + } + } + + @Override + protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, + FilterChain chain) throws ServletException, IOException { + HttpServletRequest request = (HttpServletRequest)servletRequest; + String str = request.getRequestURI().toLowerCase(); + // 因为ShiroFilter 拦截所有请求(在上面我们配置了urlPattern 为 * ,当然你也可以在那里精确的添加要处理的路径,这样就不需要这个类了),而在每次请求里面都做了session的读取和更新访问时间等操作,这样在集群部署session共享的情况下,数量级的加大了处理量负载。 + // 所以我们这里将一些能忽略的请求忽略掉。 + // 当然如果你的集群系统使用了动静分离处理,静态资料的请求不会到Filter这个层面,便可以忽略。 + boolean flag = true; + int idx = 0; + if(( idx = str.indexOf(".")) > 0){ + str = str.substring(idx); + if(ignoreExt.contains(str.toLowerCase())) + flag = false; + } + if(flag){ + super.doFilterInternal(servletRequest, servletResponse, chain); + }else{ + chain.doFilter(servletRequest, servletResponse); + } + } + + } +} diff --git a/src/main/java/com/rymcu/vertical/config/HpeisShiroRealm.java b/src/main/java/com/rymcu/vertical/config/HpeisShiroRealm.java new file mode 100644 index 0000000..4da8858 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/config/HpeisShiroRealm.java @@ -0,0 +1,157 @@ +package com.rymcu.vertical.config; + +import com.rymcu.vertical.core.constant.ShiroConstants; +import com.rymcu.vertical.core.exception.CaptchaException; +import com.rymcu.vertical.core.exception.ServiceException; +import com.rymcu.vertical.entity.Menu; +import com.rymcu.vertical.entity.Role; +import com.rymcu.vertical.entity.User; +import com.rymcu.vertical.service.MenuService; +import com.rymcu.vertical.service.RoleService; +import com.rymcu.vertical.service.UserService; +import com.rymcu.vertical.util.Encodes; +import com.rymcu.vertical.util.Utils; +import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.authc.*; +import org.apache.shiro.authz.AuthorizationInfo; +import org.apache.shiro.authz.SimpleAuthorizationInfo; +import org.apache.shiro.realm.AuthorizingRealm; +import org.apache.shiro.subject.PrincipalCollection; +import org.apache.shiro.util.ByteSource; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.annotation.Resource; +import java.io.Serializable; +import java.util.List; + +/** + * @author ronger + * @since 2018/05/28 11:00 + * 自定义权限匹配和账号密码匹配 + * */ +public class HpeisShiroRealm extends AuthorizingRealm { + @Resource + private RoleService roleService; + + @Resource + private MenuService menuService; + + @Resource + private UserService userService; + + @Override + protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { + //Principal principal = (Principal) getAvailablePrincipal(principals); +// System.out.println("权限配置-->MyShiroRealm.doGetAuthorizationInfo()"); + SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); + Principal principal = (Principal)principals.getPrimaryPrincipal(); + User user = new User(); + user.setIdUser(principal.getId()); + try { + List roles = roleService.selectRoleByUser(user); + for (Role role : roles) { + if(StringUtils.isNotBlank(role.getInputCode())){ + authorizationInfo.addRole(role.getInputCode()); + } + } + List permissions = menuService.selectMenuByUser(user); + for (Menu perm : permissions) { + if (perm.getPermission() != null) { + authorizationInfo.addStringPermission(perm.getPermission()); + } + } + // 添加用户权限 + authorizationInfo.addStringPermission("user"); + + } catch (Exception e) { + e.printStackTrace(); + } + return authorizationInfo; + } + + /** + * 认证回调函数, 登录时调用,主要是用来进行身份认证的,也就是说验证用户输入的账号和密码是否正确。 + * */ + @Override + protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException { + UsernamePasswordToken token = (UsernamePasswordToken) authcToken; + //获取用户的输入的账号. + String username = token.getUsername(); + + User user = null; + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + if (!org.springframework.util.StringUtils.isEmpty(attributes.getRequest().getAttribute(ShiroConstants.CURRENT_CAPTCHA))) { + throw new CaptchaException(); + } + try { + user = userService.findByLoginName(username); + } catch (ServiceException e) { + e.printStackTrace(); + } + if (user == null) { + return null; + } + if (!"0".equals(user.getStatus())) { //账户冻结(是否允许登陆) + throw new LockedAccountException(); + } + byte[] salt = Encodes.decodeHex(user.getPassword().substring(0,16)); + return new SimpleAuthenticationInfo(new Principal(user, token.isMobileLogin()), + user.getPassword().substring(16), ByteSource.Util.bytes(salt), getName()); + } + + /** + * 授权用户信息 + */ + public static class Principal implements Serializable { + + private static final long serialVersionUID = 1L; + + private Integer id; // 编号 + private String account; // 登录名 + private String name; // 姓名 + private boolean mobileLogin; // 是否手机登录 + +// private Map cacheMap; + + public Principal(User user, boolean mobileLogin) { + this.id = user.getIdUser(); + this.account = user.getAccount(); + this.name = user.getNickName(); + this.mobileLogin = mobileLogin; + } + + public Integer getId() { + return id; + } + + public String getAccount() { + return account; + } + + public String getName() { + return name; + } + + public boolean isMobileLogin() { + return mobileLogin; + } + + /** + * 获取SESSIONID + */ + public String getSessionid() { + try{ + return (String) Utils.getSession().getId(); + }catch (Exception e) { + return ""; + } + } + + @Override + public String toString() { + return id.toString(); + } + + } +} diff --git a/src/main/java/com/rymcu/vertical/config/MybatisConfigurer.java b/src/main/java/com/rymcu/vertical/config/MybatisConfigurer.java new file mode 100644 index 0000000..1d83e0a --- /dev/null +++ b/src/main/java/com/rymcu/vertical/config/MybatisConfigurer.java @@ -0,0 +1,69 @@ +package com.rymcu.vertical.config; + +import com.github.pagehelper.PageInterceptor; +import org.apache.ibatis.plugin.Interceptor; +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.SqlSessionFactoryBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; +import tk.mybatis.spring.mapper.MapperScannerConfigurer; + +import javax.sql.DataSource; +import java.util.Properties; + +import static com.rymcu.vertical.core.constant.ProjectConstant.*; + + +/** + * Mybatis & Mapper & PageHelper 配置 + */ +@Configuration +public class MybatisConfigurer { + + @Bean + public SqlSessionFactory sqlSessionFactoryBean(DataSource dataSource) throws Exception { + SqlSessionFactoryBean factory = new SqlSessionFactoryBean(); + factory.setDataSource(dataSource); + factory.setTypeAliasesPackage(MODEL_PACKAGE); + + //配置分页插件,详情请查阅官方文档 + PageInterceptor pageHelper = new PageInterceptor(); + Properties properties = new Properties(); + properties.setProperty("helperDialect", "mysql"); + properties.setProperty("pageSizeZero", "true");//分页尺寸为0时查询所有纪录不再执行分页 + properties.setProperty("reasonable", "true");//页码<=0 查询第一页,页码>=总页数查询最后一页 + properties.setProperty("supportMethodsArguments", "true");//支持通过 Mapper 接口参数来传递分页参数 + properties.setProperty("rowBoundsWithCount", "true");// + pageHelper.setProperties(properties); + + //添加插件 + factory.setPlugins(new Interceptor[]{pageHelper}); + + //添加XML目录 + ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + factory.setMapperLocations(resolver.getResources("classpath:mapper/**/*.xml")); + factory.setTypeHandlersPackage("com.rymcu.vertical.util.handlers"); + return factory.getObject(); + } + + @Bean + public MapperScannerConfigurer mapperScannerConfigurer() { + MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer(); + mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactoryBean"); + mapperScannerConfigurer.setBasePackage(MAPPER_PACKAGE); + + //配置通用Mapper,详情请查阅官方文档 + Properties properties = new Properties(); + properties.setProperty("mappers", MAPPER_INTERFACE_REFERENCE); + properties.setProperty("notEmpty", "false");//insert、update是否判断字符串类型!='' 即 test="str != null"表达式内是否追加 and str != '' + properties.setProperty("IDENTITY", "JDBC"); + mapperScannerConfigurer.setProperties(properties); + + + return mapperScannerConfigurer; + } + +} + diff --git a/src/main/java/com/rymcu/vertical/config/ShiroConfig.java b/src/main/java/com/rymcu/vertical/config/ShiroConfig.java new file mode 100644 index 0000000..8632a12 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/config/ShiroConfig.java @@ -0,0 +1,238 @@ +package com.rymcu.vertical.config; + +import org.apache.shiro.authc.credential.HashedCredentialsMatcher; +import org.apache.shiro.mgt.SecurityManager; +import org.apache.shiro.session.mgt.SessionManager; +import org.apache.shiro.spring.LifecycleBeanPostProcessor; +import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; +import org.apache.shiro.spring.web.ShiroFilterFactoryBean; +import org.apache.shiro.web.filter.authc.FormAuthenticationFilter; +import org.apache.shiro.web.mgt.DefaultWebSecurityManager; +import org.crazycake.shiro.RedisCacheManager; +import org.crazycake.shiro.RedisManager; +import org.crazycake.shiro.RedisSessionDAO; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.EnvironmentAware; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +import javax.servlet.Filter; +import java.util.LinkedHashMap; +import java.util.Map; + +@Configuration +@ConfigurationProperties(prefix = "redis.shiro") +public class ShiroConfig implements EnvironmentAware { + + private Environment env; + + @Override + public void setEnvironment(Environment environment) { + this.env=environment; + } + + @Bean + public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) { + ShiroFilterFactoryBean shiroFilterFactoryBean = new HpeisShiroFilterFactoryBean(); + shiroFilterFactoryBean.setSecurityManager(securityManager); + + Map filterChainDefinitionMap = new LinkedHashMap(); + //注意过滤器配置顺序 不能颠倒 + //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了,登出后跳转配置的loginUrl + filterChainDefinitionMap.put("/logout", "logout"); + // 配置不会被拦截的链接 顺序判断 + filterChainDefinitionMap.put("/druid/**", "anon"); + filterChainDefinitionMap.put("/css/**", "anon"); + filterChainDefinitionMap.put("/fonts/**", "anon"); + filterChainDefinitionMap.put("/js/**", "anon"); + filterChainDefinitionMap.put("/uploadFile/**", "anon"); + filterChainDefinitionMap.put("/login", "anon"); + filterChainDefinitionMap.put("/api/**", "anon"); + // filterChainDefinitionMap.put("/**", "authc"); + filterChainDefinitionMap.put("/**", "authc"); + //配置shiro默认登录界面地址,前后端分离中登录界面跳转应由前端路由控制,后台仅返回json数据 + shiroFilterFactoryBean.setLoginUrl("/login"); + // 登录成功后要跳转的链接 + shiroFilterFactoryBean.setSuccessUrl("/index"); + //未授权界面; + shiroFilterFactoryBean.setUnauthorizedUrl("/403"); + shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); + + Map filtersMap = new LinkedHashMap<>(); + filtersMap.put("authc",hpeisFormAuthenticationFilter()); + shiroFilterFactoryBean.setFilters(filtersMap); + + filterChainDefinitionMap.put("/**", "authc"); + shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); + + return shiroFilterFactoryBean; + } + + /** + * 凭证匹配器 + * (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了 + * ) + * + * @return + */ + @Bean + public HashedCredentialsMatcher hashedCredentialsMatcher() { + HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); + hashedCredentialsMatcher.setHashAlgorithmName("SHA-1");//散列算法:这里使用MD5算法; + hashedCredentialsMatcher.setHashIterations(1024);//散列的次数,比如散列两次,相当于 md5(md5("")); + return hashedCredentialsMatcher; + } + + @Bean + public HpeisShiroRealm hpeisShiroRealm() { + HpeisShiroRealm shiroRealm = new HpeisShiroRealm(); + shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher()); + return shiroRealm; + } + + + @Bean + public SecurityManager securityManager() { + DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); + securityManager.setRealm(hpeisShiroRealm()); + // 自定义session管理 使用redis + securityManager.setSessionManager(sessionManager()); + // 自定义缓存实现 使用redis + //securityManager.setCacheManager(cacheManager()); + return securityManager; + } + + //自定义sessionManager + @Bean + public SessionManager sessionManager() { + HpeisSessionManager sessionManager = new HpeisSessionManager(); + sessionManager.setSessionDAO(redisSessionDAO()); + sessionManager.setSessionIdUrlRewritingEnabled(false); + sessionManager.setGlobalSessionTimeout(21600000L); + return sessionManager; + } + + /** + * 配置shiro redisManager + *

+ * 使用的是shiro-redis开源插件 + * + * @return + */ + + + public RedisManager redisManager() { + // 设置redis配置信息 + RedisManager redisManager = new RedisManager(); + redisManager.setHost(env.getProperty("spring.redis.host")); + redisManager.setPassword(env.getProperty("spring.redis.password")); + return redisManager; + } + + /** + * cacheManager 缓存 redis实现 + *

+ * 使用的是shiro-redis开源插件 + * + * @return + */ + + + @Bean + public RedisCacheManager cacheManager() { + RedisCacheManager redisCacheManager = new RedisCacheManager(); + redisCacheManager.setRedisManager(redisManager()); + return redisCacheManager; + } + + /** + * RedisSessionDAO shiro sessionDao层的实现 通过redis + *

+ * 使用的是shiro-redis开源插件 + */ + + + @Bean + public RedisSessionDAO redisSessionDAO() { + RedisSessionDAO redisSessionDAO = new RedisSessionDAO(); + redisSessionDAO.setRedisManager(redisManager()); + redisSessionDAO.setExpire(21600); +// Custom your redis key prefix for session management, if you doesn't define this parameter, +// shiro-redis will use 'shiro_redis_session:' as default prefix +// redisSessionDAO.setKeyPrefix(""); + return redisSessionDAO; + } + + /** + * 开启shiro aop注解支持. + * 使用代理方式;所以需要开启代码支持; + * + * @param securityManager + * @return + */ + @Bean + public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { + AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); + authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); + return authorizationAttributeSourceAdvisor; + } + + /** + * Shiro生命周期处理器 + * @return + */ + @Bean + public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){ + return new LifecycleBeanPostProcessor(); + } +// +// /** +// * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证 +// * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能 +// * +// * @return +// */ +// @Bean +// @DependsOn({"lifecycleBeanPostProcessor"}) +// public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() { +// DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); +// advisorAutoProxyCreator.setProxyTargetClass(true); +// return advisorAutoProxyCreator; +// } + + @Bean + public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() { + AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); + authorizationAttributeSourceAdvisor.setSecurityManager(securityManager()); + return authorizationAttributeSourceAdvisor; + } + + public FormAuthenticationFilter hpeisFormAuthenticationFilter(){ + FormAuthenticationFilter formAuthenticationFilter = new ShiroLoginFilter(); + return formAuthenticationFilter; + } + + @Bean + public FilterRegistrationBean someFilterRegistration() { + FilterRegistrationBean registration = new FilterRegistrationBean(); + FormAuthenticationFilter hpeisFormAuthenticationFilter = new ShiroLoginFilter(); + registration.setFilter(hpeisFormAuthenticationFilter); + registration.setEnabled(false); + return registration; + } + + + + + /** + * 注册全局异常处理 + * + * @return + */ + /*@Bean(name = "exceptionHandler") + public HandlerExceptionResolver handlerExceptionResolver() { + return new HpeisExceptionHandler(); + }*/ +} diff --git a/src/main/java/com/rymcu/vertical/config/ShiroLoginFilter.java b/src/main/java/com/rymcu/vertical/config/ShiroLoginFilter.java new file mode 100644 index 0000000..1f54a53 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/config/ShiroLoginFilter.java @@ -0,0 +1,72 @@ +package com.rymcu.vertical.config; + + +import com.alibaba.fastjson.JSONObject; +import com.rymcu.vertical.core.result.GlobalResultGenerator; +import org.apache.shiro.web.filter.authc.FormAuthenticationFilter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * Created by wanwh on 2019/1/24 0024. + */ +public class ShiroLoginFilter extends FormAuthenticationFilter { + + private static final Logger log = LoggerFactory.getLogger(FormAuthenticationFilter.class); + + /** + * 在访问controller前判断是否登录,返回json,不进行重定向。 + * @param request + * @param response + * @return true-继续往下执行,false-该filter过滤器已经处理,不继续执行其他过滤器 + * @throws Exception + */ + + @Override + protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { + HttpServletResponse httpServletResponse = (HttpServletResponse) response; + if (this.isLoginRequest(request, response)) { + if (this.isLoginSubmission(request, response)) { + if (log.isTraceEnabled()) { + log.trace("Login submission detected. Attempting to execute login."); + } + + return this.executeLogin(request, response); + } else { + if (log.isTraceEnabled()) { + log.trace("Login page view."); + } + + return true; + } + }else if(isAjax((HttpServletRequest) request)){ + httpServletResponse.setContentType("application/json"); + httpServletResponse.setCharacterEncoding("UTF-8"); + httpServletResponse.setHeader("sessionstatus", "timeOut"); + httpServletResponse.addHeader("loginPath", this.getLoginUrl()); + httpServletResponse.getWriter().write(JSONObject.toJSONString(GlobalResultGenerator.genErrorResult("未登录或已登录超时,请重新登录"),true)); + return false; + }else { + if (log.isTraceEnabled()) { + log.trace("Attempting to access a path which requires authentication. Forwarding to the Authentication url [" + this.getLoginUrl() + "]"); + } + + this.saveRequestAndRedirectToLogin(request, response); + return false; + } + } + + private boolean isAjax(HttpServletRequest request) { + String requestedWith = request.getHeader("x-requested-with"); + if (requestedWith != null && requestedWith.equalsIgnoreCase("XMLHttpRequest")) { + return true; + } else { + return false; + } + } +} diff --git a/src/main/java/com/rymcu/vertical/config/UsernamePasswordToken.java b/src/main/java/com/rymcu/vertical/config/UsernamePasswordToken.java new file mode 100644 index 0000000..863d4d1 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/config/UsernamePasswordToken.java @@ -0,0 +1,36 @@ +package com.rymcu.vertical.config; + +/** + * 用户和密码(包含验证码)令牌类 + */ +public class UsernamePasswordToken extends org.apache.shiro.authc.UsernamePasswordToken { + + private static final long serialVersionUID = 1L; + + private String captcha; + private boolean mobileLogin; + + public UsernamePasswordToken() { + super(); + } + + public UsernamePasswordToken(String username, char[] password, + boolean rememberMe, String host, String captcha, boolean mobileLogin) { + super(username, password, rememberMe, host); + this.captcha = captcha; + this.mobileLogin = mobileLogin; + } + + public String getCaptcha() { + return captcha; + } + + public void setCaptcha(String captcha) { + this.captcha = captcha; + } + + public boolean isMobileLogin() { + return mobileLogin; + } + +} \ No newline at end of file diff --git a/src/main/java/com/rymcu/vertical/config/WebMvcConfigurer.java b/src/main/java/com/rymcu/vertical/config/WebMvcConfigurer.java new file mode 100644 index 0000000..11c0972 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/config/WebMvcConfigurer.java @@ -0,0 +1,118 @@ +package com.rymcu.vertical.config; + + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.alibaba.fastjson.support.config.FastJsonConfig; +import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; +import com.rymcu.vertical.core.result.GlobalResult; +import com.rymcu.vertical.jwt.aop.RestAuthTokenInterceptor; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * Spring MVC 配置 + */ +@Configuration +public class WebMvcConfigurer extends WebMvcConfigurationSupport { + + private final Logger logger = LoggerFactory.getLogger(WebMvcConfigurer.class); +// @Value("${env}") +// private String env;//当前激活的配置文件 + + @Override + public void extendMessageConverters(List> converters) { + FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter(); + FastJsonConfig config = new FastJsonConfig(); + config.setSerializerFeatures(SerializerFeature.WriteMapNullValue,//保留空的字段 + SerializerFeature.WriteNullStringAsEmpty);//String null -> "" + //SerializerFeature.WriteNullNumberAsZero);//Number null -> 0 + config.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect); //关闭循环引用 + converter.setFastJsonConfig(config); + converter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON)); + converter.setDefaultCharset(Charset.forName("UTF-8")); + converters.add(0, converter); + } + + //解决跨域问题 + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOrigins("*") + .allowCredentials(true) + .allowedMethods("GET", "POST", "DELETE", "PUT", "PATCH"); + } + + @Bean + public RestAuthTokenInterceptor restAuthTokenInterceptor() { + return new RestAuthTokenInterceptor(); + } + + //添加拦截器 + @Override + public void addInterceptors(InterceptorRegistry registry) { + // TODO 先不拦截接口,进行测试 + registry.addInterceptor(restAuthTokenInterceptor()).addPathPatterns("/api/**") + .excludePathPatterns("/api/login/**", "/api/logout","/api/member/*", + "/api/home/*","/api/item/**","/api/searchHistory/**", "/api/pay/payNotify", + "/api/mall/oauth/**","/api/express/**","/api/person/**"); + + } + + private void responseResult(HttpServletResponse response, GlobalResult result) { + response.setCharacterEncoding("UTF-8"); + response.setHeader("Content-type", "application/json;charset=UTF-8"); + response.setStatus(200); + try { + response.getWriter().write(JSON.toJSONString(result)); + } catch (IOException ex) { + logger.error(ex.getMessage()); + } + } + + /** + * 一个简单的签名认证,规则: + * 1. 将请求参数按ascii码排序 + * 2. 拼接为a=value&b=value...这样的字符串(不包含sign) + * 3. 混合密钥(secret)进行md5获得签名,与请求的签名进行比较 + */ + private boolean validateSign(HttpServletRequest request) { + String requestSign = request.getParameter("sign");//获得请求签名,如sign=19e907700db7ad91318424a97c54ed57 + if (StringUtils.isEmpty(requestSign)) { + return false; + } + List keys = new ArrayList(request.getParameterMap().keySet()); + keys.remove("sign");//排除sign参数 + Collections.sort(keys);//排序 + + StringBuilder sb = new StringBuilder(); + for (String key : keys) { + sb.append(key).append("=").append(request.getParameter(key)).append("&");//拼接字符串 + } + String linkString = sb.toString(); + linkString = StringUtils.substring(linkString, 0, linkString.length() - 1);//去除最后一个'&' + + String secret = "Potato";//密钥,自己修改 + String sign = DigestUtils.md5Hex(linkString + secret);//混合密钥md5 + + return StringUtils.equals(sign, requestSign);//比较 + } +} diff --git a/src/main/java/com/rymcu/vertical/core/constant/ProjectConstant.java b/src/main/java/com/rymcu/vertical/core/constant/ProjectConstant.java new file mode 100644 index 0000000..96eb072 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/core/constant/ProjectConstant.java @@ -0,0 +1,17 @@ +package com.rymcu.vertical.core.constant; + +/** + * 项目常量 + */ +public final class ProjectConstant { + public static final String BASE_PACKAGE = "com.rymcu.vertical";//项目基础包名称,根据自己公司的项目修改 + + public static final String DTO_PACKAGE = BASE_PACKAGE + ".dto";//DTO所在包 + public static final String MODEL_PACKAGE = BASE_PACKAGE + ".entity";//Model所在包 + public static final String MAPPER_PACKAGE = BASE_PACKAGE + ".mapper";//Mapper所在包 + public static final String SERVICE_PACKAGE = BASE_PACKAGE + ".service";//Service所在包 + public static final String SERVICE_IMPL_PACKAGE = SERVICE_PACKAGE + ".impl";//ServiceImpl所在包 + public static final String CONTROLLER_PACKAGE = BASE_PACKAGE + ".web";//Controller所在包 + + public static final String MAPPER_INTERFACE_REFERENCE = BASE_PACKAGE + ".core.mapper.Mapper";//Mapper插件基础接口的完全限定名 +} diff --git a/src/main/java/com/rymcu/vertical/core/constant/ShiroConstants.java b/src/main/java/com/rymcu/vertical/core/constant/ShiroConstants.java new file mode 100644 index 0000000..22ff54d --- /dev/null +++ b/src/main/java/com/rymcu/vertical/core/constant/ShiroConstants.java @@ -0,0 +1,63 @@ +package com.rymcu.vertical.core.constant; + +/** + * Shiro通用常量 + * + */ +public interface ShiroConstants +{ + /** + * 当前登录的用户 + */ + public static final String CURRENT_USER = "currentUser"; + + /** + * 用户名 + */ + public static final String CURRENT_USERNAME = "username"; + + /** + * 消息key + */ + public static String MESSAGE = "message"; + + /** + * 错误key + */ + public static String ERROR = "errorMsg"; + + /** + * 编码格式 + */ + public static String ENCODING = "UTF-8"; + + /** + * 当前在线会话 + */ + public String ONLINE_SESSION = "online_session"; + + /** + * 验证码key + */ + public static final String CURRENT_CAPTCHA = "captcha"; + + /** + * 验证码开关 + */ + public static final String CURRENT_ENABLED = "captchaEnabled"; + + /** + * 验证码开关 + */ + public static final String CURRENT_TYPE = "captchaType"; + + /** + * 验证码 + */ + public static final String CURRENT_VALIDATECODE = "validateCode"; + + /** + * 验证码错误 + */ + public static final String CAPTCHA_ERROR = "captchaError"; +} diff --git a/src/main/java/com/rymcu/vertical/core/exception/CaptchaException.java b/src/main/java/com/rymcu/vertical/core/exception/CaptchaException.java new file mode 100644 index 0000000..463ca5c --- /dev/null +++ b/src/main/java/com/rymcu/vertical/core/exception/CaptchaException.java @@ -0,0 +1,17 @@ +package com.rymcu.vertical.core.exception; + +import org.apache.shiro.authc.AuthenticationException; + +/** + * 验证码错误异常类 + * + */ +public class CaptchaException extends AuthenticationException +{ + private static final long serialVersionUID = 1L; + + public CaptchaException() + { + super("验证码不正确"); + } +} diff --git a/src/main/java/com/rymcu/vertical/core/exception/ServiceException.java b/src/main/java/com/rymcu/vertical/core/exception/ServiceException.java new file mode 100644 index 0000000..fc95680 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/core/exception/ServiceException.java @@ -0,0 +1,47 @@ +package com.rymcu.vertical.core.exception; + + +import com.rymcu.vertical.core.result.ResultCode; + +/** + * 服务(业务)异常如“ 账号或密码错误 ”,该异常只做INFO级别的日志记录 @see WebMvcConfigurer + */ +public class ServiceException extends Exception { + private int code; + private String extraMessage; + + public ServiceException(ResultCode resultCode) { + super(resultCode.getMessage()); + this.code=resultCode.getCode(); + } + + + public ServiceException(String message, Throwable cause) { + + } + public ServiceException(int code, String message, String extraMessage, Throwable cause){ + super(message,cause); + this.code=code; + this.extraMessage=extraMessage; + } + + + + public ServiceException(ResultCode resultCode, String extraMessage){ + this(resultCode.getCode(),resultCode.getMessage(),extraMessage,null); + } + + public ServiceException(String extraMessage){ + this(ResultCode.INVALID_PARAM,extraMessage); + } + + + public int getCode() { + return code; + } + + public String getExtraMessage() { + return extraMessage; + } + +} diff --git a/src/main/java/com/rymcu/vertical/core/mapper/Mapper.java b/src/main/java/com/rymcu/vertical/core/mapper/Mapper.java new file mode 100644 index 0000000..dc9744a --- /dev/null +++ b/src/main/java/com/rymcu/vertical/core/mapper/Mapper.java @@ -0,0 +1,17 @@ +package com.rymcu.vertical.core.mapper; + +import tk.mybatis.mapper.common.BaseMapper; +import tk.mybatis.mapper.common.ConditionMapper; +import tk.mybatis.mapper.common.IdsMapper; +import tk.mybatis.mapper.common.special.InsertListMapper; + +/** + * 定制版MyBatis Mapper插件接口,如需其他接口参考官方文档自行添加。 + */ +public interface Mapper + extends + BaseMapper, + ConditionMapper, + IdsMapper, + InsertListMapper { +} diff --git a/src/main/java/com/rymcu/vertical/core/mapper/TreeMapper.java b/src/main/java/com/rymcu/vertical/core/mapper/TreeMapper.java new file mode 100644 index 0000000..e339783 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/core/mapper/TreeMapper.java @@ -0,0 +1,21 @@ +package com.rymcu.vertical.core.mapper; + +import java.util.List; + +public interface TreeMapper extends Mapper { + + /** + * 找到所有子节点 + * @param entity + * @return + */ + public List findByParentIdsLike(T entity); + + /** + * 更新所有父节点字段 + * @param entity + * @return + */ + public int updateParentIds(T entity); + +} diff --git a/src/main/java/com/rymcu/vertical/core/result/GlobalResult.java b/src/main/java/com/rymcu/vertical/core/result/GlobalResult.java new file mode 100644 index 0000000..c1cb6d4 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/core/result/GlobalResult.java @@ -0,0 +1,19 @@ +package com.rymcu.vertical.core.result; + +import lombok.Data; + +@Data +public class GlobalResult { + private boolean success = false; + private T data; + private int code; + private String message; + + public GlobalResult() { + } + + public static GlobalResult newInstance() { + return new GlobalResult(); + } + +} diff --git a/src/main/java/com/rymcu/vertical/core/result/GlobalResultGenerator.java b/src/main/java/com/rymcu/vertical/core/result/GlobalResultGenerator.java new file mode 100644 index 0000000..1e16b17 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/core/result/GlobalResultGenerator.java @@ -0,0 +1,80 @@ +package com.rymcu.vertical.core.result; + +import com.rymcu.vertical.util.ErrorCode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class GlobalResultGenerator { + private static final Logger LOGGER = LoggerFactory.getLogger(GlobalResultGenerator.class); + + /** + * normal + * @param success + * @param data + * @param message + * @param + * @return + */ + public static GlobalResult genResult(boolean success, T data, String message) { + GlobalResult result = GlobalResult.newInstance(); + result.setSuccess(success); + result.setData(data); + result.setMessage(message); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("generate rest result:{}", result); + } + return result; + } + + /** + * success + * @param data + * @param + * @return + */ + public static GlobalResult genSuccessResult(T data) { + + return genResult(true, data, null); + } + + /** + * error message + * @param message error message + * @param + * @return + */ + public static GlobalResult genErrorResult(String message) { + + return genResult(false, null, message); + } + + /** + * error + * @param error error enum + * @param + * @return + */ + public static GlobalResult genErrorResult(ErrorCode error) { + + return genErrorResult(error.getMessage()); + } + + /** + * success no message + * @return + */ + public static GlobalResult genSuccessResult() { + return genSuccessResult(null); + } + + /** + * success + * @param + * @return + */ + public static GlobalResult genSuccessResult(String message) { + + return genResult(true, null, message); + } + +} diff --git a/src/main/java/com/rymcu/vertical/core/result/ResultCode.java b/src/main/java/com/rymcu/vertical/core/result/ResultCode.java new file mode 100644 index 0000000..0994c2a --- /dev/null +++ b/src/main/java/com/rymcu/vertical/core/result/ResultCode.java @@ -0,0 +1,33 @@ +package com.rymcu.vertical.core.result; + +/** + * 响应码枚举,参考HTTP状态码的语义 + */ +public enum ResultCode { + SUCCESS(1, "SUCCESS"),//成功 + FAIL(400, "访问失败"),//失败 + UNAUTHORIZED(401, "签名错误"),//未认证(签名错误) + NOT_FOUND(404, "此接口不存在"),//接口不存在 + INTERNAL_SERVER_ERROR(500, "系统繁忙,请稍后再试"),//服务器内部错误 + INVALID_PARAM(10000, "参数错误"), + + + + ; + private int code; + private String message; + + ResultCode(int code, String message) { + this.code = code; + this.message = message; + } + + + public int getCode() { + return code; + } + + public String getMessage() { + return message; + } +} diff --git a/src/main/java/com/rymcu/vertical/core/service/AbstractService.java b/src/main/java/com/rymcu/vertical/core/service/AbstractService.java new file mode 100644 index 0000000..ebcab81 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/core/service/AbstractService.java @@ -0,0 +1,77 @@ +package com.rymcu.vertical.core.service; + + +import com.rymcu.vertical.core.exception.ServiceException; +import com.rymcu.vertical.core.mapper.Mapper; +import org.apache.ibatis.exceptions.TooManyResultsException; +import org.springframework.beans.factory.annotation.Autowired; +import tk.mybatis.mapper.entity.Condition; + +import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.util.List; + +/** + * 基于通用MyBatis Mapper插件的Service接口的实现 + */ +public abstract class AbstractService implements Service { + + @Autowired + protected Mapper mapper; + + private Class modelClass; // 当前泛型真实类型的Class + + public AbstractService() { + ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass(); + modelClass = (Class) pt.getActualTypeArguments()[0]; + } + + public void save(T model) { + mapper.insertSelective(model); + } + + public void save(List models) { + mapper.insertList(models); + } + + public void deleteById(String id) { + mapper.deleteByPrimaryKey(id); + } + + public void deleteByIds(String ids) { + mapper.deleteByIds(ids); + } + + public void update(T model) { + mapper.updateByPrimaryKeySelective(model); + } + + public T findById(String id) { + return mapper.selectByPrimaryKey(id); + } + + @Override + public T findBy(String fieldName, Object value) throws TooManyResultsException, ServiceException { + try { + T model = modelClass.newInstance(); + Field field = modelClass.getDeclaredField(fieldName); + field.setAccessible(true); + field.set(model, value); + return mapper.selectOne(model); + } catch (ReflectiveOperationException e) { + throw new ServiceException(e.getMessage(), e); + } + } + + public List findByIds(String ids) { + return mapper.selectByIds(ids); + } + + public List findByCondition(Condition condition) { + return mapper.selectByCondition(condition); + } + + public List findAll() { + return mapper.selectAll(); + } +} diff --git a/src/main/java/com/rymcu/vertical/core/service/LogService.java b/src/main/java/com/rymcu/vertical/core/service/LogService.java new file mode 100644 index 0000000..cb15af0 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/core/service/LogService.java @@ -0,0 +1,12 @@ +package com.rymcu.vertical.core.service; + + + +/** + * 体检者日志接口 + */ +public interface LogService { + + //void log(LogInfo logInfo); + +} diff --git a/src/main/java/com/rymcu/vertical/core/service/Service.java b/src/main/java/com/rymcu/vertical/core/service/Service.java new file mode 100644 index 0000000..c09523f --- /dev/null +++ b/src/main/java/com/rymcu/vertical/core/service/Service.java @@ -0,0 +1,23 @@ +package com.rymcu.vertical.core.service; + +import com.rymcu.vertical.core.exception.ServiceException; +import org.apache.ibatis.exceptions.TooManyResultsException; +import tk.mybatis.mapper.entity.Condition; + +import java.util.List; + +/** + * Service 层 基础接口,其他Service 接口 请继承该接口 + */ +public interface Service { + void save(T model);//持久化 + void save(List models);//批量持久化 + void deleteById(String id);//通过主鍵刪除 + void deleteByIds(String ids);//批量刪除 eg:ids -> “1,2,3,4” + void update(T model);//更新 + T findById(String id);//通过ID查找 + T findBy(String fieldName, Object value) throws TooManyResultsException, ServiceException; //通过Model中某个成员变量名称(非数据表中column的名称)查找,value需符合unique约束 + List findByIds(String ids);//通过多个ID查找//eg:ids -> “1,2,3,4” + List findByCondition(Condition condition);//根据条件查找 + List findAll();//获取所有 +} diff --git a/src/main/java/com/rymcu/vertical/core/service/dynProps4Files/DynProps4FilesService.java b/src/main/java/com/rymcu/vertical/core/service/dynProps4Files/DynProps4FilesService.java new file mode 100644 index 0000000..21ace24 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/core/service/dynProps4Files/DynProps4FilesService.java @@ -0,0 +1,347 @@ +package com.rymcu.vertical.core.service.dynProps4Files; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.io.*; +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * 动态配置文件,可以设置更新周期 + * 配置读取读取服务 + */ +@Component +public class DynProps4FilesService { + private Logger _log = LoggerFactory.getLogger(DynProps4FilesService.class); + /** + * 属性文件 + */ + private File[] fileArray; + /** + * 启动延时 + */ + private long delay; + /** + * 更新周期 + */ + private long period; + /** + * 属性对象 + */ + private Properties property = new Properties(); + /** + * 文件监控器 + */ + private List monitors; + + /** + * @param files 属性文件 + * @param delay 从DynProps被创建到第一次动态监视的时间间隔. 约束范围delay > 0 + * @param period 动态监视的时间间隔. 约束范围period >= 0;等于0表示不执行动态监视,退化为静态配置文件. + */ + public DynProps4FilesService(File[] files, long delay, long period) throws IOException { + this.fileArray = files; + this.delay = delay; + this.period = period; + init(); + } + + public DynProps4FilesService(List fileNames, long delay, long period) throws IOException { + this.delay = delay; + this.period = period; + fileArray = new File[fileNames.size()]; + int index = 0; + for (String oriFileName : fileNames) { + String fileName = oriFileName.trim(); + if (StringUtils.indexOfIgnoreCase(fileName, "classpath:") == 0) { + fileArray[index++] = new File( + this.getClass().getClassLoader().getResource("").getPath() + File.separator + + fileName.substring("classpath:".length())); + } else { + fileArray[index++] = new File(fileName); + } + } + + init(); + } + + + public DynProps4FilesService(String fileNames, long delay, long period) throws IOException { + this.delay = delay; + this.period = period; + boolean isClassPath = false; + if (fileNames.startsWith("classpath")) { + fileNames = fileNames.substring("classpath:".length()); + isClassPath = true; + } + String[] fileName = fileNames.split("[,|,|;|;]"); + fileArray = new File[fileName.length]; + if (isClassPath) { + for (int i = 0; i < fileName.length; i++) { + fileArray[i] = new File(this.getClass().getClassLoader().getResource("").getPath() + fileName[i]); + } + } else { + for (int i = 0; i < fileName.length; i++) { + fileArray[i] = new File(fileName[i]); + } + } + + init(); + } + + public DynProps4FilesService(File[] files, long period) throws IOException { + this(files, 0, period); + } + + public DynProps4FilesService(String fileNames, long period) throws IOException { + this.period = period; + this.delay = 0; + String[] fileName = fileNames.split("[,|,|;|;]"); + + File[] files = new File[fileName.length]; + for (int i = 0; i < fileName.length; i++) { + files[i] = new File(fileName[i]); + } + init(); + } + + public DynProps4FilesService() { + } + + /** + * 加载属性文件,启动监控 + * + * @throws IOException 加载文件时出现IO异常 + */ + protected void load() throws IOException { + update(); + if (monitors == null) { + monitors = new ArrayList(fileArray.length); + } else { + for (FileMonitor monitor : monitors) { + try { + monitor.timer.cancel(); + } catch (Exception e) { + _log.warn(String.format("Timer for file [%s] cancelling failed.", monitor.file.getAbsolutePath())); + } + } + } + + for (File file : fileArray) { + long lastModify = file.lastModified(); + FileMonitor monitor = new FileMonitor(file, lastModify); + this.monitors.add(monitor); + monitor.doTask(); + } + } + + /** + * 如果文件有更新调用此方法载入 + * + * @throws IOException 没有找到文件或读文件错误时抛出 + */ + protected void update() throws IOException { + for (File file : fileArray) { + InputStream in = null; + try { + in = new FileInputStream(file); + this.property.load(in); + } catch (Exception e) { + if (e instanceof IOException) { + throw (IOException) e; + } + + throw new IOException(e); + } finally { + IOUtils.closeQuietly(in); + } + } + } + + /** + * @param key 需要获取属性值的KEY + * @param def 默认值 + * + * @return 属性值 + */ + public String getProperty(String key, String def) { + String val = this.property.getProperty(key); + return val == null ? def : val.trim(); + } + + public String getProperty(String key) { + String val = this.property.getProperty(key); + return val == null ? null : val.trim(); + } + + /** + * 设置属性值 + * + * @param key + * @param value + */ + public void setProperty(String key, String value) { + this.property.setProperty(key, value); + } + + /** + * @param key 需要获取属性值的KEY + * @param def 默认值 + * + * @return 属性值 + * + * @throws NumberFormatException 如果属性值不是整数形式 + */ + public int getInt(String key, int def) throws NumberFormatException { + String val = this.getProperty(key); + return val == null ? def : Integer.parseInt(val); + } + + public int getInt(String key) throws NumberFormatException { + return getInt(key, 0); + } + + public float getFloat(String key, float def) throws NumberFormatException { + String val = this.getProperty(key); + return val == null ? def : Float.parseFloat(val); + } + + public float getFloat(String key) throws NumberFormatException { + return getFloat(key, 0.0f); + } + + public double getDouble(String key, double def) { + String val = this.getProperty(key); + return val == null ? def : Double.parseDouble(val); + } + public double getDouble(String key) throws NumberFormatException { + return getDouble(key,0.0); + } + + + + public long getLong(String key, long def) { + String val = this.getProperty(key); + return val == null ? def : Long.parseLong(val); + } + + public long getLong(String key) throws NumberFormatException { + return getLong(key, 0L); + } + + private void init() throws IOException { + for (File file : fileArray) { + if (!file.exists() || file.length() == 0) { + throw new IllegalArgumentException("动态配置文件 " + file.getAbsolutePath() + " 不存在,或是空文件!"); + } + if (delay <= 0) { + throw new IllegalArgumentException("定时器延时时间不能为负数!"); + } + if (period <= 0) { + throw new IllegalArgumentException("定时器更新周期不能为负数!"); + } + this.property = new Properties(); + this.load();// 初始构造时,执行第一次加载. + } + //当进程终止时,取消定时任务 + Runtime.getRuntime().addShutdownHook(new Thread(new ShutdownHook())); + } + + private class ShutdownHook implements Runnable { + private DynProps4FilesService dynProps4FilesService; + + @Override + public void run() { + System.out.println("Monitors cancelling start ..."); + if (monitors != null) { + for (FileMonitor monitor : monitors) { + try { + monitor.timer.cancel(); + } catch (Exception e) { + _log.warn(String.format("Timer for file [%s] cancelling failed.", + monitor.file.getAbsolutePath())); + } + } + } + } + } + + /** + * 描述:一个内部私有类,实时监控文件有没有更新,如果更新则自动载入 + */ + private class FileMonitor { + + private long lastModifiedTime; + private File file; + /** + * 定时器,以守护线程方式启动 + */ + private Timer timer = new Timer(true); + + /** + * @param lastMonitorTime 最后的更新时间 + */ + private FileMonitor(File file, long lastMonitorTime) { + this.file = file; + this.lastModifiedTime = lastMonitorTime; + } + + /** + * 对文件进行实时监控,有更新则自动载入 + */ + private void doTask() { + if (delay < 0) { + delay = 0L; + } + if (period <= 0) { + return;// 如果更新周期非正数,则退化成静态配置文件. + } + timer.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + long t = file.lastModified(); + // 文件被删除 + // 如果动态更新过程中,配置文件被强制删除了,本次不执行任何更新.或者对配置文件进行恢复 + if (t == 0) { + try { + if (file.createNewFile()) { + OutputStream fos = new FileOutputStream(file); + property.store(fos, "文件被删除,自动恢复."); + fos.close(); + } + } catch (IOException ioe2) { + // 这里基本上只有磁盘空间满才会发生,暂时不处理 + } + return; + } + // 文件被更新 + if (t > lastModifiedTime) { + lastModifiedTime = t; + // 2秒后还在改变,则本次更新不做处理 + try { + TimeUnit.SECONDS.sleep(2); + } catch (InterruptedException e) { + // do nothing + } + if (t != file.lastModified()) { + _log.info("文件可能未更新完成,本次不更新!"); + } else { + try { + property.clear(); + update(); + _log.info("UPDATED " + file.getAbsolutePath()); + } catch (IOException ioe) { + _log.error("UPDATING " + file.getAbsolutePath() + " failed", ioe); + } + } + _log.debug("-----------------------:" + property.keySet()); + } + }// end run() + }, delay, period); + } + } +} diff --git a/src/main/java/com/rymcu/vertical/core/service/redis/RedisResult.java b/src/main/java/com/rymcu/vertical/core/service/redis/RedisResult.java new file mode 100644 index 0000000..7e63652 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/core/service/redis/RedisResult.java @@ -0,0 +1,75 @@ +package com.rymcu.vertical.core.service.redis; + +import com.rymcu.vertical.entity.BaseDO; + +import java.util.List; + +/** + * redis 中取得的结果 + * Created by liwei on 2017/2/7. + */ +public class RedisResult extends BaseDO { + + /** + * redis中是否存在 + */ + private boolean exist = false; + + /** + * redis中取得的数据 + */ + private T result; + + /** + * redis中取得的List数据 + */ + private List listResult; + /** + * redis中的key是否存在。true:表示redis中存在Key,但对应的值为空值标记 + */ + private boolean keyExists = false; + /** + * redis中key 对应在对象值 + */ + private T resultObj; + + + public boolean isExist() { + return exist; + } + + public void setExist(boolean exist) { + this.exist = exist; + } + + public T getResult() { + return result; + } + + public void setResult(T result) { + this.result = result; + } + + public List getListResult() { + return listResult; + } + + public void setListResult(List listResult) { + this.listResult = listResult; + } + + public void setKeyExists(boolean keyExists) { + this.keyExists = keyExists; + } + public boolean isKeyExists() { + return keyExists; + } + + public T getResultObj() { + return resultObj; + } + + public void setResultObj(T resultObj) { + this.resultObj = resultObj; + } +} diff --git a/src/main/java/com/rymcu/vertical/core/service/redis/RedisService.java b/src/main/java/com/rymcu/vertical/core/service/redis/RedisService.java new file mode 100644 index 0000000..ebe522e --- /dev/null +++ b/src/main/java/com/rymcu/vertical/core/service/redis/RedisService.java @@ -0,0 +1,264 @@ +package com.rymcu.vertical.core.service.redis; + + +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.util.Set; + +/** + * Redis 服务接口 + * Jimersy Lee + * 2017-09-18 14:58:21 + */ +public interface RedisService { + /** + * NX: 当且仅当缓存中特定的key不存在时设定数据 + */ + String NXXX_SET_IF_NOT_EXISTS = "nx"; + /** + * XX: 当且仅当缓存中特定的key存在时设定数据 + */ + String NXXX_SET_IF_EXISTS = "xx"; + + /** + * EX:缓存失效的时间单位:秒 + */ + String EXPX_SECONDS = "ex"; + + /** + * EX:缓存失效的时间单位:毫秒 + */ + String EXPX_MILLISECOND = "px"; + + /** + * 默认过期时间 ,3600(秒) + */ + int DEFAULT_EXPIRE_TIME = 3600; + + ObjectMapper om = new ObjectMapper(); + + + /** + * 空白占位符 + */ + String BLANK_CONTENT = "__BLANK__"; + + /** + * 初始化操作 + */ + void init(); + + + void destroy(); + + // /** + // * 从连接池里取连接(用完连接后必须销毁) + // * + // * @return + // */ + // Jedis getResource(); + + // /** + // * 用完后,销毁连接(必须) + // * + // * @param jedis + // */ + // void destroyResource(Jedis jedis); + + /** + * 根据key取数据 + * + * @param key + * @return + */ + String get(String key); + + + /** + * 根据key取对象数据(不支持Collection数据类型) + * + * @param key + * @param clazz + * @return + */ + T get(String key, Class clazz); + + + /** + * 根据key取对象数据(不支持Collection数据类型) + * + * @param key + * @param clazz + * @param + * @return + */ + RedisResult getResult(String key, Class clazz); + + /** + * 根据key取 Collection 对象数据 + * + * @param key + * @param elementClazz 集合元素类型 + * @param + * @return + */ + RedisResult getListResult(String key, Class elementClazz); + + + /** + * 写入/修改 缓存内容 + * + * @param key + * @param obj + * @return + */ + String set(String key, Object obj); + + + /** + * 写入/修改 缓存内容 + * + * @param key + * @param value + * @return + */ + String set(String key, String value); + + + /** + * 写入/修改 缓存内容(无论key是否存在,均会更新key对应的值) + * + * @param key + * @param obj + * @param expireTime 缓存内容过期时间 (单位:秒) ,若expireTime小于0 则表示该内容不过期 + * @return + */ + String set(String key, Object obj, int expireTime); + + + /** + * 写入/修改 缓存内容(无论key是否存在,均会更新key对应的值) + * + * @param key + * @param value + * @param expireTime 缓存内容过期时间 (单位:秒) ,若expireTime小于0 则表示该内容不过期 + * @return + */ + String set(String key, String value, int expireTime); + + /** + * 写入/修改 缓存内容 + * + * @param key + * @param value + * @param nxxx 缓存写入值模式 详见 {@link RedisService#NXXX_SET_IF_EXISTS}, {@link RedisService#NXXX_SET_IF_NOT_EXISTS} + * @param expx 缓存超时时间单位 详见{@link RedisService#EXPX_SECONDS}, {@link RedisService#EXPX_MILLISECOND} + * @param expiredTime 缓存存活时长,必须 大于0 + * @return + */ + String set(String key, String value, String nxxx, String expx, long expiredTime); + + /** + * 仅当redis中不含对应的key时,设定缓存内容 + * + * @param key + * @param value + * @param expiredTime 缓存内容过期时间 (单位:秒) ,若expireTime小于0 则表示该内容不过期 + * @return + */ + String setnx(String key, String value, long expiredTime); + + /** + * 仅当redis中含有对应的key时,修改缓存内容 + * + * @param key + * @param value + * @param expiredTime 缓存内容过期时间 (单位:秒) ,若expireTime小于0 则表示该内容不过期 + * @return + */ + String setxx(String key, String value, long expiredTime); + + + /** + * 根据key删除缓存, + * + * @param keys + * @return + */ + Long delete(String... keys); + + /** + * 判断对应的key是否存在 + * + * @param key + * @return + */ + boolean exists(String key); + + + /** + * redis 加法运算 + * + * @param key + * @param value + * @return 运算结果 + */ + Long incrBy(String key, long value); + + /** + * 设定redis 对应的key的剩余存活时间 + * + * @param key + * @param seconds + */ + void setTTL(String key, int seconds); + + /** + * 根据通配符表达式查询key值的set,通配符仅支持* + * + * @param pattern 如 ke6*abc等 + * @return + */ + Set keys(String pattern); + + /** + * 将对象转为json字符串。若对象为null,则返回 {@link RedisService#BLANK_CONTENT} + * + * @param object + * @return + */ + String toJsonString(Object object); + + /** + * json序列化对象。 + * + * @param value + * @return 返回序列化后的字符串。若value为null,则返回 {@link RedisService#BLANK_CONTENT} + */ + String makeSerializedString(Object value); + + /** + * 写入/修改 缓存内容(无论key是否存在,均会更新key对应的值) + * + * @param cacheName + * @param key + * @param value + * @return + */ + String put(String cacheName, String key, Object value); + + /** + * 写入/修改 缓存内容(无论key是否存在,均会更新key对应的值) + * + * @param cacheName + * @param key + * @param value + * @param expireTime 缓存 内容过期时间 (单位:秒) ,若expireTime小于0 则表示该内容不过期 + * @return + */ + String put(String cacheName, String key, Object value, int expireTime); + + Object get(String cacheName, String key); + + +} diff --git a/src/main/java/com/rymcu/vertical/core/service/redis/impl/RedisKeyHelper.java b/src/main/java/com/rymcu/vertical/core/service/redis/impl/RedisKeyHelper.java new file mode 100644 index 0000000..d3fbb0b --- /dev/null +++ b/src/main/java/com/rymcu/vertical/core/service/redis/impl/RedisKeyHelper.java @@ -0,0 +1,213 @@ +package com.rymcu.vertical.core.service.redis.impl; + +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +/** + * Redis Key 辅助类 + */ +@Component +@Lazy(false) +public class RedisKeyHelper { + /** + * 应用级别前缀 + */ + private static String SYS_PREFIX = "PROJECT_"; + /** + * 用户信息前缀 + */ + private static String UIC_PREFIX = SYS_PREFIX + "UIC_"; + /** + * 商品中心信息前缀 + */ + private static String IC_PREFIX = SYS_PREFIX + "IC_"; + /** + * 交易中心信息前缀 + */ + private static String TC_PREFIX = SYS_PREFIX + "TC_"; + /** + * 优惠中心信息前缀 + */ + private static String PROM_PREFIX = SYS_PREFIX + "PROM_"; + /** + * 会员中心信息前缀 + */ + private static String MMB_PREFIX = SYS_PREFIX + "MMB_"; + /** + * 分布式互斥锁前缀 + */ + private static String LOCK_PREFIX = SYS_PREFIX + "LOCK_"; + + /** + * 单次登录的默认有效时长(单位:秒) + */ + public static final int DEFAULT_LOGIN_TIMEOUT = 3600 * 24 * 7; + + /** + * 签到记录的默认有效时长(单位:秒) + */ + public static final int MMB_SIGN_TIMEOUT = 3600 * 12; + /** + * redis中加息券信息的保存时间(单位:秒) + */ + public static final int PROM_IRC_TIMEOUT = 60 * 3; + /** + * redis中借款人信息默认有效效时间(单位:秒) + */ + public static final int BORROWER_EXPIRE_TIMEOUT = 60 * 3;//redis缓存失效时间 + + + //商品中心KEY配置==begin======================================================================================== + /** + * 商品(资产标)前缀 + */ + public static final String IC_ITEM_PREFIX = IC_PREFIX + "ITEM_"; + /** + * 商品(资产标)列表前缀 + */ + public static final String IC_ITEM_LIST_PREFIX = IC_ITEM_PREFIX + "LIST_"; + /** + * 商品已投金额或份数前缀 + */ + public static final String IC_ITEM_INVESTED_AMOUNT_PREFIX = IC_PREFIX + "INTESTED_AMT_"; + public static final String IC_ITEM_DEAL_CREDITOR_PREFIX = IC_PREFIX + "DEAL_CREDITOR_"; + + //商品中心KEY配置==end========================================================================================== + //优惠中心KEY配置==begin======================================================================================== + /** + * 加息券信息前缀 + */ + public static String PROM_IRC_PREFIX = PROM_PREFIX + "IRC_"; + + /** + * 优惠配置信息前缀 + */ + public static String PROM_CONFIG_PREFIX = PROM_PREFIX + "CONFIG_"; + /** + * 红包列表信息前缀 + */ + public static final String PROM_COUPON_LIST_PREFIX = PROM_PREFIX + "COUPON_LIST_"; + //优惠中心KEY配置==end========================================================================================== + //交易中心KEY配置========================================================================================== + public static final String TC_TRANS_ACCOUNT_PREFIX = TC_PREFIX + "ACC_"; + + public static final String TC_TRANS_CURRENT_DEAL_CONFIG_PREFIX = TC_PREFIX + "CURRENT_DEAL_CONFIG_"; + + public static final String TC_TRANS_EXPERIENCE_MONEY_CONFIG_PREFIX = TC_PREFIX + "EXPERIENCE_MONEY_CONFIG_"; + + public static final String TC_TRANS_CURRENT_DEAL_LOAD_PREFIX = TC_PREFIX + "CURRENT_DEAL_LOAD_"; + + public static final String TC_TRANS_CONST_DEAL_ORDER_LIST_PREFIX = TC_PREFIX + "CURRENT_CONST_DEAL_ORDER_LIST_"; + + /** + * 流水号redis key前缀 + */ + public static final String TC_TRANS_SEQ_PREFIX = TC_PREFIX + "SEQ_"; + + //交易中心KEY配置==end========================================================================================== + //用户中心KEY配置========================================================================================== + /** + * redis中登录用户token的key前缀 + */ + public static String LOGIN_TOKEN_KEY_PREFIX = UIC_PREFIX + "LOGIN_TOKEN_"; + /** + * redis中登录用户USERID的key前缀 + */ + public static String LOGIN_UID_KEY_PREFIX = UIC_PREFIX + "LOGIN_UID_"; + /** + * 用户信息前缀(手机号) + */ + public static String UIC_MOBILE_PREFIX = UIC_PREFIX + "MOB_"; + /** + * 用户角色前缀 + */ + public static String UIC_ROLE_PREFIX = UIC_PREFIX + "B_R_"; + public static String UIC_ROLE_CANCEL_SUFFIX = UIC_PREFIX + "CNL_"; + //用户中心KEY配置==end========================================================================================== + + /** + * 构建分布式锁的key + * + * @param clazz + * @param key + * @return + */ + public String makeLockKey(Class clazz, String key) { + return buildKeyString(LOCK_PREFIX, clazz.getSimpleName(), key); + } + + + /** + * 构建分布式锁的key + * + * @param key + * @return + */ + public String makeLockKey(String key) { + return buildKeyString(LOCK_PREFIX, key); + } + + /** + * 构造商品信息 redis key + * + * @param itemId + * @return + */ + public String makeIcItemKey(long itemId) { + return buildKeyString(IC_ITEM_PREFIX, itemId); + } + + /** + * 构造商品信息列表 redis key + * + * @param pageSize + * @param pageNo + * @return + */ + public String makeIcItemListKey(int pageSize, int pageNo) { + return buildKeyString(IC_ITEM_LIST_PREFIX, pageSize, pageNo); + } + + /** + * 构造资产标已投金额前缀 + * + * @param itemId + * @return + */ + public String makeInvestedAmountKey(long itemId) { + return buildKeyString(IC_ITEM_INVESTED_AMOUNT_PREFIX, itemId); + } + + /** + * 构造交易中心流水号前缀 + * + * @param flag + * @return + */ + public String makeSeqKey(String flag) { + return buildKeyString(TC_TRANS_SEQ_PREFIX, flag); + } + + public static String buildKeyString(Object... objs) { + if (objs == null || objs.length == 0) { + return ""; + } + StringBuilder builder = new StringBuilder(); + boolean isFirst = true; + for (Object obj : objs) { + if (isFirst) { + isFirst = false; + } else { + builder.append("_"); + } + if (obj instanceof Class) { + builder.append(((Class) obj).getName()); + } else { + builder.append(obj); + } + } + return builder.toString(); + } + + +} diff --git a/src/main/java/com/rymcu/vertical/core/service/redis/impl/RedisServiceImpl.java b/src/main/java/com/rymcu/vertical/core/service/redis/impl/RedisServiceImpl.java new file mode 100644 index 0000000..5716c37 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/core/service/redis/impl/RedisServiceImpl.java @@ -0,0 +1,577 @@ +package com.rymcu.vertical.core.service.redis.impl; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; +import com.fasterxml.jackson.databind.JavaType; +import com.rymcu.vertical.core.service.dynProps4Files.DynProps4FilesService; +import com.rymcu.vertical.core.service.redis.RedisResult; +import com.rymcu.vertical.core.service.redis.RedisService; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.JedisPoolConfig; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.annotation.Resource; +import java.io.IOException; +import java.util.*; + +/** + * Redis 服务接口实现类 + * + * @author liwei + * 16/10/30 下午5:28 + */ +@Component("redisService") +public class RedisServiceImpl implements RedisService { + + private static final Logger logger = LoggerFactory.getLogger(RedisServiceImpl.class); + + + private static JedisPool pool = null; + + @Resource + private DynProps4FilesService dynProps4Files; + + @Resource + private Environment env; + + + /** + * 初始化操作 + */ + @Override + @PostConstruct + public void init() { + if (pool != null) { + return; + } + /*JedisPoolConfig config = new JedisPoolConfig(); + config.setMaxIdle(dynProps4Files.getInt("REDIS_MAX_IDLE", JedisPoolConfig.DEFAULT_MAX_IDLE)); + config.setMaxTotal(dynProps4Files.getInt("REDIS_MAX_TOTAL", JedisPoolConfig.DEFAULT_MAX_TOTAL)); + config.setMaxWaitMillis(dynProps4Files.getLong("REDIS_MAX_WAIT", JedisPoolConfig.DEFAULT_MAX_WAIT_MILLIS)); + config.setTestOnBorrow(true); + pool = new JedisPool(config, dynProps4Files.getProperty("REDIS_HOST"), + dynProps4Files.getInt("REDIS_PORT", 6379), dynProps4Files.getInt( + "REDIS_MAX_WAIT", 1000), dynProps4Files.getProperty("REDIS_PASSWORD", null));*/ + + JedisPoolConfig config = new JedisPoolConfig(); + config.setMaxIdle(dynProps4Files.getInt("REDIS_MAX_IDLE", JedisPoolConfig.DEFAULT_MAX_IDLE)); + config.setMaxTotal(dynProps4Files.getInt("REDIS_MAX_TOTAL", JedisPoolConfig.DEFAULT_MAX_TOTAL)); + config.setMaxWaitMillis(dynProps4Files.getLong("REDIS_MAX_WAIT", JedisPoolConfig.DEFAULT_MAX_WAIT_MILLIS)); + config.setTestOnBorrow(true); + pool = new JedisPool(config, env.getProperty("spring.redis.host"), + dynProps4Files.getInt("REDIS_PORT", 6379), dynProps4Files.getInt( + "REDIS_MAX_WAIT", 1000), dynProps4Files.getProperty("REDIS_PASSWORD", env.getProperty("spring.redis.password"))); + + + } + + @Override + @PreDestroy + public void destroy() { + try { + if (pool != null) { + pool.destroy(); + } + + } catch (Exception e) { + //do nothing + } + } + + /** + * 从连接池里取连接(用完连接后必须销毁) + * + * @return + */ + private Jedis getResource() { + return pool.getResource(); + + } + + /** + * 用完后,销毁连接(必须) + * + * @param jedis + */ + private void destroyResource(Jedis jedis) { + if (jedis == null) { + return; + } + IOUtils.closeQuietly(jedis); + } + + /** + * 根据key取数据 + * + * @param key + * @return + */ + public String get(String key) { + + if (StringUtils.isBlank(key)) { + logger.warn("Params key is blank!"); + return StringUtils.EMPTY; + } + Jedis jedis = this.getResource(); + try { + return jedis.get(key); + } finally { + this.destroyResource(jedis); + } + + } + + /** + * 根据key取对象数据(不支持Collection数据类型) + * + * @param key + * @param clazz + * @return + */ + @Override + public T get(String key, Class clazz) { + if (StringUtils.isBlank(key)) { + logger.warn("Params key is blank!"); + return null; + } + if (clazz == null) { + logger.warn("Params clazz is null!"); + return null; + } + String value = get(key); + if (StringUtils.isBlank(value) || StringUtils.equalsIgnoreCase(value, BLANK_CONTENT)) { + return null; + } + T obj = null; + try { + obj = om.readValue(value, clazz); + } catch (IOException e) { + logger.error("Can not unserialize obj to [{}] with string [{}]", clazz.getName(), value); + } + return obj; + } + + /** + * 写入/修改 缓存内容(无论key是否存在,均会更新key对应的值) + * + * @param key + * @param obj + * @param expireTime 缓存 内容过期时间 (单位:秒) ,若expireTime小于0 则表示该内容不过期 + * @return + */ + public String set(String key, Object obj, int expireTime) { + String value = RedisService.BLANK_CONTENT; + if (obj != null) { + try { + value = RedisService.om.writeValueAsString(obj); + } catch (IOException e) { + logger.error("Can not write object to redis:" + obj.toString(), e); + } + } + return set(key, value, expireTime); + } + + /** + * 写入/修改 缓存内容(无论key是否存在,均会更新key对应的值) + * + * @param key + * @param value + * @param expireTime 缓存 内容过期时间 (单位:秒) ,若expireTime小于0 则表示该内容不过期 + * @return + */ + public String set(String key, String value, int expireTime) { + if (StringUtils.isBlank(key)) { + logger.warn("Params key is blank!"); + return null; + } + + if (value == null) { + + logger.warn("Params value is null!"); + return null; + } + + Jedis jedis = this.getResource(); + try { + String result = jedis.set(key, value); + if (expireTime > 0) { + jedis.expire(key, expireTime); + } + return result; + } finally { + this.destroyResource(jedis); + } + } + + /** + * 根据key取对象数据(不支持Collection数据类型) + * + * @param key + * @param clazz + * @return + */ + @Override + public RedisResult getResult(String key, Class clazz) { + if (StringUtils.isBlank(key)) { + logger.warn("Params key is blank!"); + return null; + } + if (clazz == null) { + logger.warn("Params clazz is null!"); + return null; + } + RedisResult redisResult = new RedisResult(); + + String value = get(key); + if (StringUtils.isBlank(value)) { + redisResult.setExist(false); + return redisResult; + } + //到此步,则表明redis中存在key + redisResult.setExist(true); + if (StringUtils.equalsIgnoreCase(value, BLANK_CONTENT)) { + return redisResult; + } + T obj = null; + try { + obj = om.readValue(value, clazz); + redisResult.setResult(obj); + } catch (IOException e) { + logger.error("Can not unserialize obj to [{}] with string [{}]", clazz.getName(), value); + //到此步直接视为无值 + redisResult.setExist(false); + } + return redisResult; + } + + /** + * 根据key取 Collection 对象数据 + * + * @param key + * @param elementClazz 集合元素类型 + * @return + */ + @Override + public RedisResult getListResult(String key, Class elementClazz) { + if (StringUtils.isBlank(key)) { + logger.warn("Params key is blank!"); + return null; + } + + if (elementClazz == null) { + logger.warn("Params elementClazz is null!"); + return null; + } + RedisResult redisResult = new RedisResult(); + + String value = get(key); + if (StringUtils.isBlank(value)) { + redisResult.setExist(false); + return redisResult; + } + + //到此步,则表明redis中存在key + redisResult.setExist(true); + if (StringUtils.equalsIgnoreCase(value, BLANK_CONTENT)) { + return redisResult; + } + + List list = null; + try { + list = om.readValue(value, getCollectionType(List.class, elementClazz)); + redisResult.setListResult(list); + } catch (IOException e) { + logger.error("Can not unserialize list to [{}] with string [{}]", elementClazz.getName(), value); + //到此步直接视为无值 + redisResult.setExist(false); + } + + return redisResult; + } + + /** + * 写入/修改 缓存内容 + * + * @param key + * @param obj + * @return + */ + @Override + public String set(String key, Object obj) { + String value = RedisService.BLANK_CONTENT; + if (obj != null) { + try { + value = RedisService.om.writeValueAsString(obj); + } catch (IOException e) { + logger.error("Can not write object to redis:" + obj.toString(), e); + } + } + return set(key, value); + } + + private static JavaType getCollectionType(Class collectionClazz, + Class elementClazz) { + return om.getTypeFactory().constructCollectionType(collectionClazz, elementClazz); + } + + + /** + * 写入/修改 缓存内容(默认有过期时间 1小时) + * + * @param key + * @param value + * @return + */ + @Override + public String set(String key, String value) { + return this.set(key, value, DEFAULT_EXPIRE_TIME); + } + + + /** + * 写入/修改 缓存内容 + * + * @param key + * @param value + * @param nxxx 缓存写入值模式 详见 {@link RedisService#NXXX_SET_IF_EXISTS}, {@link RedisService#NXXX_SET_IF_NOT_EXISTS} + * @param expx 缓存超时时间单位 详见{@link RedisService#EXPX_SECONDS}, {@link RedisService#EXPX_MILLISECOND} + * @param expiredTime 缓存存活时长,必须 大于0 + * @return + */ + @Override + public String set(String key, String value, String nxxx, String expx, long expiredTime) { + if (StringUtils.isBlank(key)) { + logger.warn("Params key is blank!"); + return null; + } + + if (value == null) { + logger.warn("Params value is null!"); + return null; + } + + Jedis jedis = this.getResource(); + try { + return jedis.set(key, value); + } finally { + this.destroyResource(jedis); + } + + } + + /** + * 仅当redis中不含对应的key时,设定缓存内容 + * + * @param key + * @param value + * @param expiredTime 缓存内容过期时间 (单位:秒) ,expireTime必须大于0 + * @return + */ + @Override + public String setnx(String key, String value, long expiredTime) { + return this.set(key, value, NXXX_SET_IF_NOT_EXISTS, EXPX_SECONDS, expiredTime); + } + + /** + * 仅当redis中含有对应的key时,修改缓存内容 + * + * @param key + * @param value + * @param expiredTime 缓存内容过期时间 (单位:秒) ,expireTime必须大于0 + * @return + */ + @Override + public String setxx(String key, String value, long expiredTime) { + return this.set(key, value, NXXX_SET_IF_EXISTS, EXPX_SECONDS, expiredTime); + } + + /** + * 根据key删除缓存 + * + * @param keys + * @return + */ + public Long delete(String... keys) { + if (keys == null || keys.length == 0) { + logger.warn("Params keys is null or 0 length!"); + return -1L; + } + Jedis jedis = this.getResource(); + try { + return jedis.del(keys); + } finally { + this.destroyResource(jedis); + } + } + + /** + * 判断对应的key是否存在 + * + * @param key + * @return + */ + public boolean exists(String key) { + if (StringUtils.isBlank(key)) { + //不接受空值 + return false; + } + Jedis jedis = this.getResource(); + try { + return jedis.exists(key); + } finally { + this.destroyResource(jedis); + } + + + } + + /** + * redis 加法运算 + * + * @param key + * @param value + * @return + */ + @Override + public Long incrBy(String key, long value) { + if (StringUtils.isBlank(key)) { + logger.warn("Params key is blank!"); + return null; + } + Jedis jedis = this.getResource(); + try { + return jedis.incrBy(key, value); + } finally { + this.destroyResource(jedis); + } + } + + /** + * 设定redis 对应的key的剩余存活时间 + * + * @param key + * @param seconds + */ + @Override + public void setTTL(String key, int seconds) { + if (seconds < 0) { + return; + } + if (StringUtils.isBlank(key)) { + logger.warn("Params key is blank!"); + return; + } + Jedis jedis = this.getResource(); + try { + jedis.expire(key, seconds); + } finally { + this.destroyResource(jedis); + } + } + + /** + * 根据通配符表达式查询key值的set,通配符仅支持* + * + * @param pattern 如 ke6*abc等 + * @return + */ + public Set keys(String pattern) { + + if (StringUtils.isBlank(pattern)) { + logger.warn("Params pattern is blank!"); + return Collections.emptySet(); + } + Jedis jedis = this.getResource(); + try { + return jedis.keys(pattern); + } finally { + this.destroyResource(jedis); + } + } + + + /** + * 将对象转为json字符串。若对象为null,则返回 {@link RedisService#BLANK_CONTENT} + * + * @param object + * @return + */ + @Override + public String toJsonString(Object object) { + if (object == null) { + return BLANK_CONTENT; + } + + if ((object instanceof Collection) && CollectionUtils.isEmpty((Collection) object)) { + return BLANK_CONTENT; + } + + if ((object instanceof Map) && CollectionUtils.isEmpty((Map) object)) { + return BLANK_CONTENT; + } + + try { + return om.writeValueAsString(object); + } catch (IOException e) { + return null; + } + } + + @Override + public String makeSerializedString(Object value) { + if (value == null) { + return BLANK_CONTENT; + } + + if ((value instanceof Collection) && ((Collection) value).size() == 0) { + return BLANK_CONTENT; + } + + if ((value instanceof Map) && ((Map) value).size() == 0) { + return BLANK_CONTENT; + } + + + return JSON.toJSONString(value); + } + + @Override + public String put(String cacheName, String key, Object value) { + String result = get(cacheName); + Map map = new HashMap(); + if (StringUtils.isNotBlank(result)){ + map = JSON.parseObject(result, new TypeReference() {}); + } + map.put(key,value); + return set(cacheName,map); + } + + @Override + public String put(String cacheName, String key, Object value, int expireTime) { + String result = get(cacheName); + Map map = new HashMap(); + if (StringUtils.isNotBlank(result)){ + map = JSON.parseObject(result, new TypeReference() {}); + } + map.put(key,value); + return set(cacheName,map,expireTime); + } + + public Object get(String cacheName, String key){ + String result = get(cacheName); + if (StringUtils.isNotBlank(result)){ + Map map = JSON.parseObject(result, new TypeReference() {}); + return map.get(key); + } + return null; + } +} diff --git a/src/main/java/com/rymcu/vertical/dto/MenuDTO.java b/src/main/java/com/rymcu/vertical/dto/MenuDTO.java new file mode 100644 index 0000000..0213a34 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/dto/MenuDTO.java @@ -0,0 +1,27 @@ +package com.rymcu.vertical.dto; + +import lombok.Data; + +@Data +public class MenuDTO { + + private String id; + + private String parentId; + + private String parentName; + + private String name; + + private Long sort = 50L; + + private String href; + + private String menuType = "0"; + + private String permission; + + private String remarks; + + private String status = "0"; +} diff --git a/src/main/java/com/rymcu/vertical/dto/RoleDTO.java b/src/main/java/com/rymcu/vertical/dto/RoleDTO.java new file mode 100644 index 0000000..13c261e --- /dev/null +++ b/src/main/java/com/rymcu/vertical/dto/RoleDTO.java @@ -0,0 +1,25 @@ +package com.rymcu.vertical.dto; + +import lombok.Data; + +@Data +public class RoleDTO { + + // id + private String id; + + // 名称 + private String name; + + // 英文名称 + private String enname; + + // 用户类别 + private String roleType; + + // 角色授权菜单ids + private String menuIds; + + // 备注 + private String remarks; +} diff --git a/src/main/java/com/rymcu/vertical/dto/UserDTO.java b/src/main/java/com/rymcu/vertical/dto/UserDTO.java new file mode 100644 index 0000000..3b0e589 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/dto/UserDTO.java @@ -0,0 +1,45 @@ +package com.rymcu.vertical.dto; + +import com.alibaba.fastjson.annotation.JSONField; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +@Data +public class UserDTO implements Serializable { + + private String id; + + private String office; + + private String officeId; + + private String loginName; + + private String password; + + private String no; + + private String name; + + private String email; + + private String phone; + + private String mobile; + + private String status; + + private String remarks; + + private String roleIds; + + private String sex = "男"; + + private String inputCode; + + @JSONField(format = "yyyy-MM-dd HH:mm") + private Date lastLoginTime; + +} diff --git a/src/main/java/com/rymcu/vertical/dto/UserExportDTO.java b/src/main/java/com/rymcu/vertical/dto/UserExportDTO.java new file mode 100644 index 0000000..2a0d5dd --- /dev/null +++ b/src/main/java/com/rymcu/vertical/dto/UserExportDTO.java @@ -0,0 +1,19 @@ +package com.rymcu.vertical.dto; + +import lombok.Data; + +@Data +public class UserExportDTO { + + private String officeName; + + private String name; + + private String sex; + + private String loginName; + + private String storePerm; + + private String departPerm; +} diff --git a/src/main/java/com/rymcu/vertical/entity/BaseDO.java b/src/main/java/com/rymcu/vertical/entity/BaseDO.java new file mode 100644 index 0000000..d2abf51 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/entity/BaseDO.java @@ -0,0 +1,22 @@ +package com.rymcu.vertical.entity; + + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +import java.io.Serializable; + +/** + * 基础DO类,提供toString快方法 + * Created by liwei on 2015/6/16. + */ +public class BaseDO implements Serializable { + + private static final long serialVersionUID = -1394589131426860408L; + + public String toString() { + return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); + } + +} + diff --git a/src/main/java/com/rymcu/vertical/entity/Menu.java b/src/main/java/com/rymcu/vertical/entity/Menu.java new file mode 100644 index 0000000..68dcf39 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/entity/Menu.java @@ -0,0 +1,68 @@ +package com.rymcu.vertical.entity; + +import lombok.Data; +import org.apache.ibatis.type.JdbcType; +import tk.mybatis.mapper.annotation.ColumnType; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +@Data +@Table(name = "SYS_MENU") +public class Menu { + + @Id + @Column(name = "ID_MENU") + private Integer idMenu; + + /** + * 上级菜单ID + * */ + @Column(name = "PARENT_ID") + private String parentId; + + /** + * 菜单名称 + * */ + @Column(name = "NAME") + private String name; + + /** + * 排序 + * */ + @Column(name = "SORT") + private Integer sort; + + /** + * 路径 + * */ + @Column(name = "HREF") + private String href; + + /** + * 菜单类型 + * */ + @Column(name = "MENU_TYPE") + private String menuType; + + /** + * 权限标识 + * */ + @ColumnType(column = "PERMISSION", + jdbcType = JdbcType.VARCHAR) + private String permission; + + /** + * 创建时间 + * */ + @Column(name = "CREATED_TIME") + private Date createdTime; + + /** + * 状态 + * */ + @Column(name = "STATUS") + private String status; +} \ No newline at end of file diff --git a/src/main/java/com/rymcu/vertical/entity/Role.java b/src/main/java/com/rymcu/vertical/entity/Role.java new file mode 100644 index 0000000..9c231f8 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/entity/Role.java @@ -0,0 +1,40 @@ +package com.rymcu.vertical.entity; + +import lombok.Data; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +@Data +@Table(name = "SYS_ROLE") +public class Role { + @Id + @Column(name = "ID_ROLE") + private String idRole; + + /** + * 角色名称 + * */ + @Column(name = "NAME") + private String name; + + /** + * 拼音码 + * */ + @Column(name = "INPUT_CODE") + private String inputCode; + + /** + * 状态 + * */ + @Column(name = "STATUS") + private Integer status; + + /** + * 创建时间 + * */ + @Column(name = "CREATED_TIME") + private Date createdTime; +} \ No newline at end of file diff --git a/src/main/java/com/rymcu/vertical/entity/User.java b/src/main/java/com/rymcu/vertical/entity/User.java new file mode 100644 index 0000000..096c3c4 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/entity/User.java @@ -0,0 +1,105 @@ +package com.rymcu.vertical.entity; + +import lombok.Data; +import org.apache.ibatis.type.JdbcType; +import tk.mybatis.mapper.annotation.ColumnType; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +@Table(name = "SYS_USER") +@Data +public class User { + @Id + @Column(name = "ID_USER") + private Integer idUser; + + /** + * 应用ID + * */ + @Column(name = "APP_ID") + private String appId; + + /** + * 登录账号 + * */ + @Column(name = "ACCOUNT") + private String account; + + /** + * 密码 + * */ + @Column(name = "PASSWORD") + private String password; + + /** + * 昵称 + * */ + @Column(name = "NICK_NAME") + private String nickName; + + /** + * 真实姓名 + * */ + @Column(name = "REAL_NAME") + private String realName; + + /** + * 性别 1:男性 2:女性 + * */ + @Column(name = "SEX") + private Integer sex; + + /** + * 头像文件类型 + * */ + @Column(name = "AVATAR_TYPE") + private Integer avatarType; + + /** + * 头像路径 + * */ + @Column(name = "AVATAR_URL") + private String avatarUrl; + + /** + * 邮箱地址 + * */ + @ColumnType(column = "EMAIL", + jdbcType = JdbcType.VARCHAR) + private String email; + + /** + * 手机号码 + * */ + @ColumnType(column = "PHONE", + jdbcType = JdbcType.VARCHAR) + private String phone; + + /** + * 签名 + * */ + @ColumnType(column = "SIGNATURE", + jdbcType = JdbcType.VARCHAR) + private String signature; + + /** + * 状态 + * */ + @Column(name = "STATUS") + private Integer status; + + /** + * 最后登录时间 + * */ + @Column(name = "LAST_LOGIN_TIME") + private Date lastLoginTime; + + /** + * 创建时间 + * */ + @Column(name = "CREATED_TIME") + private Date createdTime; +} \ No newline at end of file diff --git a/src/main/java/com/rymcu/vertical/jwt/aop/RestAuthTokenInterceptor.java b/src/main/java/com/rymcu/vertical/jwt/aop/RestAuthTokenInterceptor.java new file mode 100644 index 0000000..cbd1f9a --- /dev/null +++ b/src/main/java/com/rymcu/vertical/jwt/aop/RestAuthTokenInterceptor.java @@ -0,0 +1,75 @@ +package com.rymcu.vertical.jwt.aop; + + + +import com.rymcu.vertical.jwt.def.JwtConstants; +import com.rymcu.vertical.jwt.model.TokenModel; +import com.rymcu.vertical.jwt.service.TokenManager; +import com.rymcu.vertical.jwt.util.oConvertUtils; +import com.rymcu.vertical.web.api.exception.ErrorCode; +import com.rymcu.vertical.web.api.exception.MallApiException; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureException; +import org.apache.commons.lang.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * Restful请求, Token校验规则拦截器(JWT) + * + * @author scott + * @date 2015/7/30. + */ +public class RestAuthTokenInterceptor implements HandlerInterceptor { + + @Autowired + private TokenManager manager; + + @Override + public void afterCompletion(HttpServletRequest httpservletrequest, HttpServletResponse httpservletresponse, Object obj, Exception exception) throws Exception { + // TODO Auto-generated method stub + } + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object obj) throws Exception { + + //从header中得到token + String authHeader = request.getHeader(JwtConstants.AUTHORIZATION); + if (StringUtils.isBlank(authHeader)) { + throw new MallApiException(ErrorCode.UNAUTHORIZED); + } + // 验证token + Claims claims = null; + try { + claims = Jwts.parser().setSigningKey(JwtConstants.JWT_SECRET).parseClaimsJws(authHeader).getBody(); + }catch (final SignatureException e) { + throw new MallApiException(ErrorCode.INVALID_TOKEN); + } + + Object username = claims.getId(); + if (oConvertUtils.isEmpty(username)) { + throw new MallApiException(ErrorCode.INVALID_TOKEN); + } + TokenModel model = manager.getToken(authHeader,username.toString()); + if (manager.checkToken(model)) { + //如果token验证成功,将对象传递给下一个请求 + request.setAttribute(JwtConstants.CURRENT_TOKEN_CLAIMS, claims); + //如果token验证成功,将token对应的用户id存在request中,便于之后注入 + request.setAttribute(JwtConstants.CURRENT_USER_NAME, model.getUsername()); + return true; + } else { + throw new MallApiException(ErrorCode.TOKEN_); + } + } + + @Override + public void postHandle(HttpServletRequest httpservletrequest, HttpServletResponse httpservletresponse, Object obj, ModelAndView modelandview) throws Exception { + // TODO Auto-generated method stub + } + +} diff --git a/src/main/java/com/rymcu/vertical/jwt/def/JwtConstants.java b/src/main/java/com/rymcu/vertical/jwt/def/JwtConstants.java new file mode 100644 index 0000000..3d888e0 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/jwt/def/JwtConstants.java @@ -0,0 +1,15 @@ +package com.rymcu.vertical.jwt.def; + +public class JwtConstants { + + /** + * 上线需要变更 + */ + public static final String JWT_SECRET = "JYJ5Qv2WF4lA6jPl5GKuAG"; + + public static final String AUTHORIZATION = "Authorization"; + public static final String CURRENT_USER_NAME = "CURRENT_TOKEN_USER_NAME"; + public static final String CURRENT_TOKEN_CLAIMS = "CURRENT_TOKEN_CLAIMS"; + public static final long TOKEN_EXPIRES_HOUR = 2 * 60; + +} diff --git a/src/main/java/com/rymcu/vertical/jwt/model/TokenModel.java b/src/main/java/com/rymcu/vertical/jwt/model/TokenModel.java new file mode 100644 index 0000000..c6ec64a --- /dev/null +++ b/src/main/java/com/rymcu/vertical/jwt/model/TokenModel.java @@ -0,0 +1,36 @@ +package com.rymcu.vertical.jwt.model; + +/** + * Token的Model类,可以增加字段提高安全性,例如时间戳、url签名 + * @author ScienJus + * @date 2015/7/31. + */ +public class TokenModel { + + //用户id + private String username; + + //随机生成的uuid + private String token; + + public TokenModel(String username, String token) { + this.username = username; + this.token = token; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } +} diff --git a/src/main/java/com/rymcu/vertical/jwt/service/RedisTokenManager.java b/src/main/java/com/rymcu/vertical/jwt/service/RedisTokenManager.java new file mode 100644 index 0000000..b3c2659 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/jwt/service/RedisTokenManager.java @@ -0,0 +1,59 @@ +package com.rymcu.vertical.jwt.service; + + +import com.rymcu.vertical.core.service.redis.RedisService; +import com.rymcu.vertical.jwt.def.JwtConstants; +import com.rymcu.vertical.jwt.model.TokenModel; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.concurrent.TimeUnit; + +/** + * 通过Redis存储和验证token的实现类 + * @author ScienJus + * @date 2015/7/31. + */ +@Component +public class RedisTokenManager implements TokenManager { + @Autowired + private StringRedisTemplate redisTemplate; + @Autowired + private RedisService redisService; + + /** + * 生成TOKEN + */ + public String createToken(String id) { + //使用uuid作为源token + String token = Jwts.builder().setId(id).setSubject(id).setIssuedAt(new Date()).signWith(SignatureAlgorithm.HS256, JwtConstants.JWT_SECRET).compact(); + //存储到redis并设置过期时间 + redisTemplate.boundValueOps(id).set(token, JwtConstants.TOKEN_EXPIRES_HOUR, TimeUnit.HOURS); + return token; + } + + public TokenModel getToken(String token, String userid) { + return new TokenModel(userid, token); + } + + public boolean checkToken(TokenModel model) { + if (model == null) { + return false; + } + String token = (String) redisTemplate.boundValueOps(model.getUsername()).get(); + if (token == null || !token.equals(model.getToken())) { + return false; + } + //如果验证成功,说明此用户进行了一次有效操作,延长token的过期时间 + redisTemplate.boundValueOps(model.getUsername()).expire(JwtConstants.TOKEN_EXPIRES_HOUR, TimeUnit.HOURS); + return true; + } + + public void deleteToken(String username) { + redisTemplate.delete(username); + } +} diff --git a/src/main/java/com/rymcu/vertical/jwt/service/TokenManager.java b/src/main/java/com/rymcu/vertical/jwt/service/TokenManager.java new file mode 100644 index 0000000..3ab7b2e --- /dev/null +++ b/src/main/java/com/rymcu/vertical/jwt/service/TokenManager.java @@ -0,0 +1,38 @@ +package com.rymcu.vertical.jwt.service; + + +import com.rymcu.vertical.jwt.model.TokenModel; + +/** + * 对token进行操作的接口 + * @author ScienJus + * @date 2015/7/31. + */ +public interface TokenManager { + + /** + * 创建一个token关联上指定用户 + * @return 生成的token + */ + public String createToken(String id); + + /** + * 检查token是否有效 + * @param model token + * @return 是否有效 + */ + public boolean checkToken(TokenModel model); + + /** + * 从字符串中解析token + * @return + */ + public TokenModel getToken(String token, String userid); + + /** + * 清除token + * @param username 登录用户账号 + */ + public void deleteToken(String username); + +} diff --git a/src/main/java/com/rymcu/vertical/jwt/util/oConvertUtils.java b/src/main/java/com/rymcu/vertical/jwt/util/oConvertUtils.java new file mode 100644 index 0000000..ab6940a --- /dev/null +++ b/src/main/java/com/rymcu/vertical/jwt/util/oConvertUtils.java @@ -0,0 +1,498 @@ +package com.rymcu.vertical.jwt.util; + +import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.lang.StringUtils; + +import javax.servlet.http.HttpServletRequest; +import java.io.UnsupportedEncodingException; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.sql.Date; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * + * @author 张代浩 + * + */ +public class oConvertUtils { + public static boolean isEmpty(Object object) { + if (object == null) { + return (true); + } + if (object.equals("")) { + return (true); + } + if (object.equals("null")) { + return (true); + } + return (false); + } + + public static boolean isNotEmpty(Object object) { + if (object != null && !object.equals("") && !object.equals("null")) { + return (true); + } + return (false); + } + + public static String decode(String strIn, String sourceCode, String targetCode) { + String temp = code2code(strIn, sourceCode, targetCode); + return temp; + } + + public static String StrToUTF(String strIn, String sourceCode, String targetCode) { + strIn = ""; + try { + strIn = new String(strIn.getBytes("ISO-8859-1"), "GBK"); + } catch (UnsupportedEncodingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return strIn; + + } + + private static String code2code(String strIn, String sourceCode, String targetCode) { + String strOut = null; + if (strIn == null || (strIn.trim()).equals("")) + return strIn; + try { + byte[] b = strIn.getBytes(sourceCode); + for (int i = 0; i < b.length; i++) { + System.out.print(b[i] + " "); + } + strOut = new String(b, targetCode); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + return strOut; + } + + public static int getInt(String s, int defval) { + if (s == null || s == "") { + return (defval); + } + try { + return (Integer.parseInt(s)); + } catch (NumberFormatException e) { + return (defval); + } + } + + public static int getInt(String s) { + if (s == null || s == "") { + return 0; + } + try { + return (Integer.parseInt(s)); + } catch (NumberFormatException e) { + return 0; + } + } + + public static int getInt(String s, Integer df) { + if (s == null || s == "") { + return df; + } + try { + return (Integer.parseInt(s)); + } catch (NumberFormatException e) { + return 0; + } + } + + public static Integer[] getInts(String[] s) { + Integer[] integer = new Integer[s.length]; + if (s == null) { + return null; + } + for (int i = 0; i < s.length; i++) { + integer[i] = Integer.parseInt(s[i]); + } + return integer; + + } + + public static double getDouble(String s, double defval) { + if (s == null || s == "") { + return (defval); + } + try { + return (Double.parseDouble(s)); + } catch (NumberFormatException e) { + return (defval); + } + } + + public static double getDou(Double s, double defval) { + if (s == null) { + return (defval); + } + return s; + } + + public static Short getShort(String s) { + if (StringUtils.isNotEmpty(s)) { + return (Short.parseShort(s)); + } else { + return null; + } + } + + public static int getInt(Object object, int defval) { + if (isEmpty(object)) { + return (defval); + } + try { + return (Integer.parseInt(object.toString())); + } catch (NumberFormatException e) { + return (defval); + } + } + + public static int getInt(BigDecimal s, int defval) { + if (s == null) { + return (defval); + } + return s.intValue(); + } + + public static Integer[] getIntegerArry(String[] object) { + int len = object.length; + Integer[] result = new Integer[len]; + try { + for (int i = 0; i < len; i++) { + result[i] = new Integer(object[i].trim()); + } + return result; + } catch (NumberFormatException e) { + return null; + } + } + + public static String getString(String s) { + return (getString(s, "")); + } + + /** + * 转义成Unicode编码 + * @param s + * @return + */ + public static String escapeJava(Object s) { + return StringEscapeUtils.escapeJava(getString(s)); + } + + public static String getString(Object object) { + if (isEmpty(object)) { + return ""; + } + return (object.toString().trim()); + } + + public static String getString(int i) { + return (String.valueOf(i)); + } + + public static String getString(float i) { + return (String.valueOf(i)); + } + + public static String getString(String s, String defval) { + if (isEmpty(s)) { + return (defval); + } + return (s.trim()); + } + + public static String getString(Object s, String defval) { + if (isEmpty(s)) { + return (defval); + } + return (s.toString().trim()); + } + + public static long stringToLong(String str) { + Long test = new Long(0); + try { + test = Long.valueOf(str); + } catch (Exception e) { + } + return test.longValue(); + } + + /** + * 获取本机IP + */ + public static String getIp() { + String ip = null; + try { + InetAddress address = InetAddress.getLocalHost(); + ip = address.getHostAddress(); + + } catch (UnknownHostException e) { + e.printStackTrace(); + } + return ip; + } + + /** + * 判断一个类是否为基本数据类型。 + * + * @param clazz + * 要判断的类。 + * @return true 表示为基本数据类型。 + */ + private static boolean isBaseDataType(Class clazz) throws Exception { + return (clazz.equals(String.class) || clazz.equals(Integer.class) || clazz.equals(Byte.class) || clazz.equals(Long.class) || clazz.equals(Double.class) || clazz.equals(Float.class) || clazz.equals(Character.class) || clazz.equals(Short.class) || clazz.equals(BigDecimal.class) || clazz.equals(BigInteger.class) || clazz.equals(Boolean.class) || clazz.equals(Date.class) || clazz.isPrimitive()); + } + + /** + * @param request + * IP + * @return IP Address + */ + public static String getIpAddrByRequest(HttpServletRequest request) { + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + return ip; + } + + /** + * @return 本机IP + * @throws SocketException + */ + public static String getRealIp() throws SocketException { + String localip = null;// 本地IP,如果没有配置外网IP则返回它 + String netip = null;// 外网IP + + Enumeration netInterfaces = NetworkInterface.getNetworkInterfaces(); + InetAddress ip = null; + boolean finded = false;// 是否找到外网IP + while (netInterfaces.hasMoreElements() && !finded) { + NetworkInterface ni = netInterfaces.nextElement(); + Enumeration address = ni.getInetAddresses(); + while (address.hasMoreElements()) { + ip = address.nextElement(); + if (!ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && ip.getHostAddress().indexOf(":") == -1) {// 外网IP + netip = ip.getHostAddress(); + finded = true; + break; + } else if (ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && ip.getHostAddress().indexOf(":") == -1) {// 内网IP + localip = ip.getHostAddress(); + } + } + } + + if (netip != null && !"".equals(netip)) { + return netip; + } else { + return localip; + } + } + + /** + * java去除字符串中的空格、回车、换行符、制表符 + * + * @param str + * @return + */ + public static String replaceBlank(String str) { + String dest = ""; + if (str != null) { + Pattern p = Pattern.compile("\\s*|\t|\r|\n"); + Matcher m = p.matcher(str); + dest = m.replaceAll(""); + } + return dest; + + } + + /** + * 判断元素是否在数组内 + * + * @param substring + * @param source + * @return + */ + public static boolean isIn(String substring, String[] source) { + if (source == null || source.length == 0) { + return false; + } + for (int i = 0; i < source.length; i++) { + String aSource = source[i]; + if (aSource.equals(substring)) { + return true; + } + } + return false; + } + + /** + * 获取Map对象 + */ + public static Map getHashMap() { + return new HashMap(); + } + + /** + * SET转换MAP + * + * @param str + * @return + */ + public static Map SetToMap(Set setobj) { + Map map = getHashMap(); + for (Iterator iterator = setobj.iterator(); iterator.hasNext();) { + Map.Entry entry = (Map.Entry) iterator.next(); + map.put(entry.getKey().toString(), entry.getValue() == null ? "" : entry.getValue().toString().trim()); + } + return map; + + } + + public static boolean isInnerIP(String ipAddress) { + boolean isInnerIp = false; + long ipNum = getIpNum(ipAddress); + /** + * 私有IP:A类 10.0.0.0-10.255.255.255 B类 172.16.0.0-172.31.255.255 C类 192.168.0.0-192.168.255.255 当然,还有127这个网段是环回地址 + **/ + long aBegin = getIpNum("10.0.0.0"); + long aEnd = getIpNum("10.255.255.255"); + long bBegin = getIpNum("172.16.0.0"); + long bEnd = getIpNum("172.31.255.255"); + long cBegin = getIpNum("192.168.0.0"); + long cEnd = getIpNum("192.168.255.255"); + isInnerIp = isInner(ipNum, aBegin, aEnd) || isInner(ipNum, bBegin, bEnd) || isInner(ipNum, cBegin, cEnd) || ipAddress.equals("127.0.0.1"); + return isInnerIp; + } + + private static long getIpNum(String ipAddress) { + String[] ip = ipAddress.split("\\."); + long a = Integer.parseInt(ip[0]); + long b = Integer.parseInt(ip[1]); + long c = Integer.parseInt(ip[2]); + long d = Integer.parseInt(ip[3]); + + long ipNum = a * 256 * 256 * 256 + b * 256 * 256 + c * 256 + d; + return ipNum; + } + + private static boolean isInner(long userIp, long begin, long end) { + return (userIp >= begin) && (userIp <= end); + } + + /** + * 将下划线大写方式命名的字符串转换为驼峰式。 + * 如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。
+ * 例如:hello_world->helloWorld + * + * @param name + * 转换前的下划线大写方式命名的字符串 + * @return 转换后的驼峰式命名的字符串 + */ + public static String camelName(String name) { + StringBuilder result = new StringBuilder(); + // 快速检查 + if (name == null || name.isEmpty()) { + // 没必要转换 + return ""; + } else if (!name.contains("_")) { + // 不含下划线,仅将首字母小写 + + return name.substring(0, 1).toLowerCase() + name.substring(1).toLowerCase(); + + } + // 用下划线将原始字符串分割 + String camels[] = name.split("_"); + for (String camel : camels) { + // 跳过原始字符串中开头、结尾的下换线或双重下划线 + if (camel.isEmpty()) { + continue; + } + // 处理真正的驼峰片段 + if (result.length() == 0) { + // 第一个驼峰片段,全部字母都小写 + result.append(camel.toLowerCase()); + } else { + // 其他的驼峰片段,首字母大写 + result.append(camel.substring(0, 1).toUpperCase()); + result.append(camel.substring(1).toLowerCase()); + } + } + return result.toString(); + } + + /** + * 将下划线大写方式命名的字符串转换为驼峰式。 + * 如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。
+ * 例如:hello_world,test_id->helloWorld,testId + * + * @param name + * 转换前的下划线大写方式命名的字符串 + * @return 转换后的驼峰式命名的字符串 + */ + public static String camelNames(String names) { + if(names==null||names.equals("")){ + return null; + } + StringBuffer sf = new StringBuffer(); + String[] fs = names.split(","); + for (String field : fs) { + field = camelName(field); + sf.append(field + ","); + } + String result = sf.toString(); + return result.substring(0, result.length() - 1); + } + + /** + * 将下划线大写方式命名的字符串转换为驼峰式。(首字母写) + * 如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。
+ * 例如:hello_world->HelloWorld + * + * @param name + * 转换前的下划线大写方式命名的字符串 + * @return 转换后的驼峰式命名的字符串 + */ + public static String camelNameCapFirst(String name) { + StringBuilder result = new StringBuilder(); + // 快速检查 + if (name == null || name.isEmpty()) { + // 没必要转换 + return ""; + } else if (!name.contains("_")) { + // 不含下划线,仅将首字母小写 + return name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase(); + } + // 用下划线将原始字符串分割 + String camels[] = name.split("_"); + for (String camel : camels) { + // 跳过原始字符串中开头、结尾的下换线或双重下划线 + if (camel.isEmpty()) { + continue; + } + // 其他的驼峰片段,首字母大写 + result.append(camel.substring(0, 1).toUpperCase()); + result.append(camel.substring(1).toLowerCase()); + } + return result.toString(); + } + +} diff --git a/src/main/java/com/rymcu/vertical/mapper/MenuMapper.java b/src/main/java/com/rymcu/vertical/mapper/MenuMapper.java new file mode 100644 index 0000000..ac8ea45 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/mapper/MenuMapper.java @@ -0,0 +1,24 @@ +package com.rymcu.vertical.mapper; + +import com.rymcu.vertical.core.mapper.Mapper; +import com.rymcu.vertical.dto.MenuDTO; +import com.rymcu.vertical.entity.Menu; +import com.rymcu.vertical.entity.Role; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +public interface MenuMapper extends Mapper { + + List selectMenuByRole(@Param("role") Role role); + + List findByParentId(@Param("parentId") String parentId); + + MenuDTO findMenuDTOById(@Param("id") String id); + + void deleteRoleMenu(@Param("menuId") String id); + + List findByParentIdAndUserId(@Param("parentId") String parentId, @Param("userId") String userId); + + void deleteMenu(@Param("id") String id); +} \ No newline at end of file diff --git a/src/main/java/com/rymcu/vertical/mapper/RoleMapper.java b/src/main/java/com/rymcu/vertical/mapper/RoleMapper.java new file mode 100644 index 0000000..f15ac5a --- /dev/null +++ b/src/main/java/com/rymcu/vertical/mapper/RoleMapper.java @@ -0,0 +1,34 @@ +package com.rymcu.vertical.mapper; + +import com.rymcu.vertical.core.mapper.Mapper; +import com.rymcu.vertical.dto.RoleDTO; +import com.rymcu.vertical.dto.UserDTO; +import com.rymcu.vertical.entity.Role; +import com.rymcu.vertical.entity.User; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +public interface RoleMapper extends Mapper { + List findAllDTO(@Param("role") RoleDTO roleDTO); + + List selectRoleByUser(@Param("sysUser") User sysUser); + + void insertRoleMenu(@Param("roleId") String id, @Param("menuId") String menuId); + + void deleteRoleMenuByRoleId(@Param("roleId") String roleId); + + void deleteUserRoleByRoleId(@Param("roleId") String roleId); + + RoleDTO findRoleDTOById(@Param("roleId") String roleId); + + String selectRoleIdsByUser(@Param("user") User user); + + List findUserOfRoleListData(@Param("user") UserDTO userDTO); + + void deleteUser(@Param("userId") String userId, @Param("roleId") String roleId); + + List findUnAddUserOfRole(@Param("userName") String userName, @Param("roleId") String roleId); + + void insertUserRole(@Param("userId") String userId, @Param("roleId") String roleId); +} \ No newline at end of file diff --git a/src/main/java/com/rymcu/vertical/mapper/UserMapper.java b/src/main/java/com/rymcu/vertical/mapper/UserMapper.java new file mode 100644 index 0000000..1a43f61 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/mapper/UserMapper.java @@ -0,0 +1,35 @@ +package com.rymcu.vertical.mapper; + +import com.rymcu.vertical.core.mapper.Mapper; +import com.rymcu.vertical.dto.RoleDTO; +import com.rymcu.vertical.dto.UserDTO; +import com.rymcu.vertical.dto.UserExportDTO; +import com.rymcu.vertical.entity.User; +import org.apache.ibatis.annotations.Param; + +import java.util.Date; +import java.util.List; + +public interface UserMapper extends Mapper { + List findAllDTO(@Param("user") UserDTO userDTO); + + UserDTO findUserDTOById(@Param("idUser") Integer idUser); + + void insertUserRole(@Param("idUser") Integer idUser, @Param("idRole") Integer idRole); + + void deleteUserRoleByUserId(@Param("idUser") Integer idUser); + + void deleteUserById(@Param("idUser") Integer idUser); + + void resetPassword(@Param("idUser") Integer idUser, @Param("password") String password); + + void updateLastLoginTime(@Param("idUser") Integer idUser, @Param("date") Date date); + + User findByLoginName(@Param("loginName") String loginName); + + List queryUserByInputCode(@Param("eName") String eName); + + List findUserExportData(@Param("user") UserDTO userDTO); + + List findRoleOfUser(@Param("idUser") Integer idUser); +} \ No newline at end of file diff --git a/src/main/java/com/rymcu/vertical/service/MenuService.java b/src/main/java/com/rymcu/vertical/service/MenuService.java new file mode 100644 index 0000000..e91904f --- /dev/null +++ b/src/main/java/com/rymcu/vertical/service/MenuService.java @@ -0,0 +1,33 @@ +package com.rymcu.vertical.service; + +import com.rymcu.vertical.core.service.Service; +import com.rymcu.vertical.dto.MenuDTO; +import com.rymcu.vertical.entity.Menu; +import com.rymcu.vertical.entity.User; + +import java.util.List; + + +/** + * Created by CodeGenerator on 2018/05/29. + */ +public interface MenuService extends Service { + + List selectMenuByUser(User sysUser); + + Menu getParent(Menu e); + + List findByParentId(String roleId); + + MenuDTO findMenuDTOById(String id); + + void saveMenu(MenuDTO menuDTO); + + void deleteMenuById(String id); + + void updateMenu(MenuDTO menuDTO); + + List findByParentIdAndUserId(String parentId, String userId); + + void updateUserStatus(String id, String flag); +} diff --git a/src/main/java/com/rymcu/vertical/service/RoleService.java b/src/main/java/com/rymcu/vertical/service/RoleService.java new file mode 100644 index 0000000..683b4cf --- /dev/null +++ b/src/main/java/com/rymcu/vertical/service/RoleService.java @@ -0,0 +1,39 @@ +package com.rymcu.vertical.service; + +import com.rymcu.vertical.core.service.Service; +import com.rymcu.vertical.dto.RoleDTO; +import com.rymcu.vertical.dto.UserDTO; +import com.rymcu.vertical.entity.Role; +import com.rymcu.vertical.entity.User; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; + + +/** + * Created by CodeGenerator on 2018/05/29. + */ +public interface RoleService extends Service { + + List selectRoleByUser(User sysUser); + + List findAllDTO(HttpServletRequest request); + + void saveRole(RoleDTO roleDTO); + + void deleteAssociatedItemsForRole(String id); + + RoleDTO findRoleDTOById(String id); + + void updateRole(RoleDTO roleDTO); + + List findUserOfRoleListData(HttpServletRequest request); + + void deleteUser(String userId, String roleId); + + List findUnAddUserOfRole(HttpServletRequest request); + + String addUserOfRole(String roleId, String ids); + + void updateMenu(RoleDTO roleDTO); +} diff --git a/src/main/java/com/rymcu/vertical/service/UserService.java b/src/main/java/com/rymcu/vertical/service/UserService.java new file mode 100644 index 0000000..369102a --- /dev/null +++ b/src/main/java/com/rymcu/vertical/service/UserService.java @@ -0,0 +1,45 @@ +package com.rymcu.vertical.service; + +import com.rymcu.vertical.core.exception.ServiceException; +import com.rymcu.vertical.core.service.Service; +import com.rymcu.vertical.dto.RoleDTO; +import com.rymcu.vertical.dto.UserDTO; +import com.rymcu.vertical.dto.UserExportDTO; +import com.rymcu.vertical.entity.User; +import org.apache.ibatis.exceptions.TooManyResultsException; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; + + +/** + * Created by CodeGenerator on 2018/05/29. + */ +public interface UserService extends Service { + + List findAllDTO(HttpServletRequest request); + + UserDTO findUserDTOById(Integer id); + + void saveUser(UserDTO userDTO); + + void updateUser(UserDTO userDTO); + + void deleteUserById(Integer id); + + Boolean checkLoginName(String loginName); + + void updateUserStatus(Integer id, Integer flag); + + void resetPassword(Integer userId, String password); + + User findByLoginName(String loginName) throws TooManyResultsException, ServiceException; + + List queryUserByInputCode(String eName); + + void updateLastLoginTime(Integer id); + + List exportExcel(HttpServletRequest request); + + List findRoleOfUser(HttpServletRequest request); +} diff --git a/src/main/java/com/rymcu/vertical/service/impl/MenuServiceImpl.java b/src/main/java/com/rymcu/vertical/service/impl/MenuServiceImpl.java new file mode 100644 index 0000000..de390d2 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/service/impl/MenuServiceImpl.java @@ -0,0 +1,107 @@ +package com.rymcu.vertical.service.impl; + +import com.rymcu.vertical.core.service.AbstractService; +import com.rymcu.vertical.dto.MenuDTO; +import com.rymcu.vertical.entity.Menu; +import com.rymcu.vertical.entity.Role; +import com.rymcu.vertical.entity.User; +import com.rymcu.vertical.mapper.MenuMapper; +import com.rymcu.vertical.service.MenuService; +import com.rymcu.vertical.service.RoleService; +import com.rymcu.vertical.util.BeanCopierUtil; +import org.apache.commons.lang.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.UUID; + + +/** + * Created by CodeGenerator on 2018/05/29. + */ +@Service +@Transactional +public class MenuServiceImpl extends AbstractService implements MenuService { + @Resource + private MenuMapper menuMapper; + + @Resource + private RoleService roleService; + + @Override + public List selectMenuByUser(User sysUser) { + List list = new ArrayList(); + List roles = roleService.selectRoleByUser(sysUser); + roles.forEach(role -> list.addAll(menuMapper.selectMenuByRole(role))); + HashSet hashSet = new HashSet(list); + list.clear(); + list.addAll(hashSet); + return list; + } + + @Override + public Menu getParent(Menu e) { + String parentId = "0"; + if(e != null){ + if(StringUtils.isNotBlank(e.getParentId())){ + parentId = e.getParentId(); + } + } + return menuMapper.selectByPrimaryKey(parentId); + } + + @Override + public List findByParentId(String parentId) { + List list = menuMapper.findByParentId(parentId); + return list; + } + + @Override + public MenuDTO findMenuDTOById(String id) { + return menuMapper.findMenuDTOById(id); + } + + @Override + public void saveMenu(MenuDTO menuDTO) { + Menu menu = new Menu(); + BeanCopierUtil.copy(menuDTO,menu); + //Utils.createBase(menu); + menuMapper.insert(menu); + //Utils.removeAllMenu(); + } + + @Override + public void deleteMenuById(String id) { + menuMapper.deleteRoleMenu(id); + menuMapper.deleteMenu(id); + //Utils.removeAllMenu(); + } + + @Override + public void updateMenu(MenuDTO menuDTO) { + Menu menu = menuMapper.selectByPrimaryKey(menuDTO.getId()); + BeanCopierUtil.copy(menuDTO,menu); + //Utils.updateBase(menu); + menuMapper.updateByPrimaryKey(menu); + //Utils.removeAllMenu(); + //UserUtils.removeMenuList(); + } + + @Override + public List findByParentIdAndUserId(String parentId, String userId) { + return menuMapper.findByParentIdAndUserId(parentId, userId); + } + + @Override + public void updateUserStatus(String id, String flag) { + Menu menu = menuMapper.selectByPrimaryKey(id); + menu.setStatus(flag); + //Utils.updateBase(menu); + menuMapper.updateByPrimaryKey(menu); + //Utils.removeAllMenu(); + } +} diff --git a/src/main/java/com/rymcu/vertical/service/impl/RoleServiceImpl.java b/src/main/java/com/rymcu/vertical/service/impl/RoleServiceImpl.java new file mode 100644 index 0000000..57665b6 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/service/impl/RoleServiceImpl.java @@ -0,0 +1,147 @@ +package com.rymcu.vertical.service.impl; + +import com.rymcu.vertical.core.service.AbstractService; +import com.rymcu.vertical.dto.RoleDTO; +import com.rymcu.vertical.dto.UserDTO; +import com.rymcu.vertical.entity.Role; +import com.rymcu.vertical.entity.User; +import com.rymcu.vertical.mapper.RoleMapper; +import com.rymcu.vertical.service.RoleService; +import com.rymcu.vertical.util.BeanCopierUtil; +import org.apache.commons.lang.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import java.util.List; +import java.util.UUID; + + +/** + * Created by CodeGenerator on 2018/05/29. + */ +@Service +@Transactional +public class RoleServiceImpl extends AbstractService implements RoleService { + @Resource + private RoleMapper roleMapper; + + @Override + public List selectRoleByUser(User sysUser) { + List roles = roleMapper.selectRoleByUser(sysUser); + return roles; + } + + @Override + public List findAllDTO(HttpServletRequest request) { + RoleDTO roleDTO = new RoleDTO(); + roleDTO.setName(request.getParameter("name")); + roleDTO.setEnname(request.getParameter("enName")); + return roleMapper.findAllDTO(roleDTO); + } + + @Override + public void saveRole(RoleDTO roleDTO) { + Role role = new Role(); + BeanCopierUtil.copy(roleDTO,role); + //Utils.createBase(role); + roleMapper.insert(role); + + String menuIds = roleDTO.getMenuIds(); + String[] m = menuIds.split(","); + for (int i = 0,len = m.length; i < len; i++) { + roleMapper.insertRoleMenu(role.getIdRole(),m[i]); + } + } + + @Override + public void deleteAssociatedItemsForRole(String roleId) { + + roleMapper.deleteUserRoleByRoleId(roleId); + + roleMapper.deleteByPrimaryKey(roleId); + } + + @Override + public RoleDTO findRoleDTOById(String id) { + return roleMapper.findRoleDTOById(id); + } + + @Override + public void updateRole(RoleDTO roleDTO) { + String menuIds = ""; + Role role = roleMapper.selectByPrimaryKey(roleDTO.getId()); + + BeanCopierUtil.copy(roleDTO,role); + //Utils.updateBase(role); + roleMapper.updateByPrimaryKey(role); + + if("0".equals(roleDTO.getMenuIds())){ + menuIds = roleMapper.findRoleDTOById(roleDTO.getId()).getMenuIds(); + }else { + menuIds = roleDTO.getMenuIds(); + } + + roleMapper.deleteRoleMenuByRoleId(role.getIdRole()); + + String[] m = menuIds.split(","); + for (int i = 0,len = m.length; i < len; i++) { + roleMapper.insertRoleMenu(role.getIdRole(),m[i]); + } + + } + + @Override + public List findUserOfRoleListData(HttpServletRequest request) { + UserDTO userDTO = new UserDTO(); + userDTO.setLoginName(request.getParameter("loginName")); + userDTO.setInputCode(request.getParameter("inputCode")); + userDTO.setName(request.getParameter("name")); + userDTO.setStatus(request.getParameter("status")); + userDTO.setOfficeId(request.getParameter("officeId")); + userDTO.setRemarks(request.getParameter("roleId"));// 为不创建多余字段,此处使用 Remark 字段存放 roleId 数据 + return roleMapper.findUserOfRoleListData(userDTO); + } + + @Override + public void deleteUser(String userId, String roleId) { + roleMapper.deleteUser(userId,roleId); + } + + @Override + public List findUnAddUserOfRole(HttpServletRequest request) { + String roleId = request.getParameter("roleId"); + String userName = request.getParameter("userName"); + return roleMapper.findUnAddUserOfRole(userName,roleId); + } + + @Override + public String addUserOfRole(String roleId, String ids) { + String message = ""; + String[] userIds = ids.split(","); + for (String userId:userIds) { + if(!(",".equals(userId) || StringUtils.isBlank(userId))){ + roleMapper.insertUserRole(userId,roleId); + } + } + return message; + } + + @Override + public void updateMenu(RoleDTO roleDTO) { + String menuIds; + if("0".equals(roleDTO.getMenuIds())){ + menuIds = roleMapper.findRoleDTOById(roleDTO.getId()).getMenuIds(); + }else { + menuIds = roleDTO.getMenuIds(); + } + + roleMapper.deleteRoleMenuByRoleId(roleDTO.getId()); + + String[] m = menuIds.split(","); + for (int i = 0,len = m.length; i < len; i++) { + roleMapper.insertRoleMenu(roleDTO.getId(),m[i]); + } + } +} diff --git a/src/main/java/com/rymcu/vertical/service/impl/UserServiceImpl.java b/src/main/java/com/rymcu/vertical/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..8c639d1 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/service/impl/UserServiceImpl.java @@ -0,0 +1,167 @@ +package com.rymcu.vertical.service.impl; + +import com.rymcu.vertical.core.exception.ServiceException; +import com.rymcu.vertical.core.service.AbstractService; +import com.rymcu.vertical.dto.RoleDTO; +import com.rymcu.vertical.dto.UserDTO; +import com.rymcu.vertical.dto.UserExportDTO; +import com.rymcu.vertical.entity.User; +import com.rymcu.vertical.mapper.UserMapper; +import com.rymcu.vertical.service.UserService; +import com.rymcu.vertical.util.BeanCopierUtil; +import com.rymcu.vertical.util.Utils; +import org.apache.commons.lang.StringUtils; +import org.apache.ibatis.exceptions.TooManyResultsException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import tk.mybatis.mapper.entity.Condition; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import java.util.Date; +import java.util.List; + + +/** + * Created by CodeGenerator on 2018/05/29. + */ +@Service +public class UserServiceImpl extends AbstractService implements UserService { + @Resource + private UserMapper userMapper; + + @Override + public List findAllDTO(HttpServletRequest request) { + UserDTO userDTO = new UserDTO(); + userDTO.setLoginName(request.getParameter("loginName")); + userDTO.setInputCode(request.getParameter("inputCode")); + userDTO.setName(request.getParameter("name")); + userDTO.setStatus(request.getParameter("status")); + userDTO.setOfficeId(request.getParameter("officeId"));// 为不创建多余字段,此处使用 Remark 字段存放 nodeId 数据 + return userMapper.findAllDTO(userDTO); + } + + @Override + public UserDTO findUserDTOById(Integer id) { + return userMapper.findUserDTOById(id); + } + + @Override + @Transactional + public void saveUser(UserDTO userDTO) { + User user = new User(); + BeanCopierUtil.copy(userDTO, user); + //user.setPassword(Utils.entryptPassword("123456")); + //Utils.createBase(user); + //所有字符转为小写 + user.setAccount(user.getAccount().toLowerCase()); + userMapper.insertSelective(user); + + String roleIds = userDTO.getRoleIds(); + if(StringUtils.isNotBlank(roleIds)){ + String[] r = roleIds.split(","); + for(int i=0,len=r.length;i findRoleOfUser(HttpServletRequest request) { + String userId = request.getParameter("userId"); + return userMapper.findRoleOfUser(Integer.parseInt(userId)); + } +} diff --git a/src/main/java/com/rymcu/vertical/util/BeanCopierUtil.java b/src/main/java/com/rymcu/vertical/util/BeanCopierUtil.java new file mode 100644 index 0000000..18aac4f --- /dev/null +++ b/src/main/java/com/rymcu/vertical/util/BeanCopierUtil.java @@ -0,0 +1,79 @@ +package com.rymcu.vertical.util; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.cglib.beans.BeanCopier; +import org.springframework.util.CollectionUtils; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author jiangjingming + */ +@Slf4j +public class BeanCopierUtil { + /** + * beanCopier缓存 + * (A拷贝到B,确定一个beanCopier) + */ + private static Map,Map, BeanCopier>> beanCopierMap = new ConcurrentHashMap<>(); + + /** + * 拷贝方法 + * @param sourceBean + * @param targetBean + * @param + * @param + */ + public static void copy(S sourceBean,T targetBean){ + @SuppressWarnings("unchecked") + Class sourceClass = (Class) sourceBean.getClass(); + @SuppressWarnings("unchecked") + Class targetClass = (Class) targetBean.getClass(); + + BeanCopier beanCopier = getBeanCopier(sourceClass,targetClass); + beanCopier.copy(sourceBean,targetBean,null); + } + + /** + * 转换方法 + * @param sourceBean 原对象 + * @param targetClass 目标类 + * @param + * @param + * @return + */ + public static T convert(S sourceBean,Class targetClass){ + try { + assert sourceBean!=null; + T targetBean = targetClass.newInstance(); + copy(sourceBean,targetBean); + return targetBean; + } catch (Exception e) { + log.error("Transform bean error",e); + throw new RuntimeException(e); + } + } + + + private static BeanCopier getBeanCopier(Class sourceClass, Class targetClass ){ + Map, BeanCopier> map = beanCopierMap.get(sourceClass); + if(CollectionUtils.isEmpty(map)){ + BeanCopier newBeanCopier = BeanCopier.create(sourceClass, targetClass, false); + Map, BeanCopier> newMap = new ConcurrentHashMap<>(); + newMap.put(targetClass,newBeanCopier); + beanCopierMap.put(sourceClass,newMap); + return newBeanCopier; + } + + BeanCopier beanCopier = map.get(targetClass); + if(beanCopier == null){ + BeanCopier newBeanCopier = BeanCopier.create(sourceClass, targetClass, false); + map.put(targetClass,newBeanCopier); + + return newBeanCopier; + } + + return beanCopier; + } +} diff --git a/src/main/java/com/rymcu/vertical/util/Digests.java b/src/main/java/com/rymcu/vertical/util/Digests.java new file mode 100644 index 0000000..99b42bf --- /dev/null +++ b/src/main/java/com/rymcu/vertical/util/Digests.java @@ -0,0 +1,117 @@ +package com.rymcu.vertical.util; + +import org.apache.commons.lang3.Validate; + +import java.io.IOException; +import java.io.InputStream; +import java.security.GeneralSecurityException; +import java.security.MessageDigest; +import java.security.SecureRandom; + +/** + * 支持SHA-1/MD5消息摘要的工具类. + * + * 返回ByteSource,可进一步被编码为Hex, Base64或UrlSafeBase64 + * + */ +public class Digests { + + private static final String SHA1 = "SHA-1"; + private static final String MD5 = "MD5"; + + private static SecureRandom random = new SecureRandom(); + + /** + * 对输入字符串进行md5散列. + */ + public static byte[] md5(byte[] input) { + return digest(input, MD5, null, 1); + } + public static byte[] md5(byte[] input, int iterations) { + return digest(input, MD5, null, iterations); + } + + /** + * 对输入字符串进行sha1散列. + */ + public static byte[] sha1(byte[] input) { + return digest(input, SHA1, null, 1); + } + + public static byte[] sha1(byte[] input, byte[] salt) { + return digest(input, SHA1, salt, 1); + } + + public static byte[] sha1(byte[] input, byte[] salt, int iterations) { + return digest(input, SHA1, salt, iterations); + } + + /** + * 对字符串进行散列, 支持md5与sha1算法. + */ + private static byte[] digest(byte[] input, String algorithm, byte[] salt, int iterations) { + try { + MessageDigest digest = MessageDigest.getInstance(algorithm); + + if (salt != null) { + digest.update(salt); + } + + byte[] result = digest.digest(input); + + for (int i = 1; i < iterations; i++) { + digest.reset(); + result = digest.digest(result); + } + return result; + } catch (GeneralSecurityException e) { + throw Exceptions.unchecked(e); + } + } + + /** + * 生成随机的Byte[]作为salt. + * + * @param numBytes byte数组的大小 + */ + public static byte[] generateSalt(int numBytes) { + Validate.isTrue(numBytes > 0, "numBytes argument must be a positive integer (1 or larger)", numBytes); + + byte[] bytes = new byte[numBytes]; + random.nextBytes(bytes); + return bytes; + } + + /** + * 对文件进行md5散列. + */ + public static byte[] md5(InputStream input) throws IOException { + return digest(input, MD5); + } + + /** + * 对文件进行sha1散列. + */ + public static byte[] sha1(InputStream input) throws IOException { + return digest(input, SHA1); + } + + private static byte[] digest(InputStream input, String algorithm) throws IOException { + try { + MessageDigest messageDigest = MessageDigest.getInstance(algorithm); + int bufferLength = 8 * 1024; + byte[] buffer = new byte[bufferLength]; + int read = input.read(buffer, 0, bufferLength); + + while (read > -1) { + messageDigest.update(buffer, 0, read); + read = input.read(buffer, 0, bufferLength); + } + + return messageDigest.digest(); + } catch (GeneralSecurityException e) { + throw Exceptions.unchecked(e); + } + } + +} diff --git a/src/main/java/com/rymcu/vertical/util/Encodes.java b/src/main/java/com/rymcu/vertical/util/Encodes.java new file mode 100644 index 0000000..5893aa2 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/util/Encodes.java @@ -0,0 +1,149 @@ +/** + * Copyright © 2012-2016 zkjkgc All rights reserved. + */ +package com.rymcu.vertical.util; + +import org.apache.commons.codec.DecoderException; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.binary.Hex; +import org.apache.commons.text.StringEscapeUtils; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; + +/** + * 封装各种格式的编码解码工具类. + * 1.Commons-Codec的 hex/base64 编码 + * 2.自制的base62 编码 + * 3.Commons-Lang的xml/html escape + * 4.JDK提供的URLEncoder + */ +public class Encodes { + + private static final String DEFAULT_URL_ENCODING = "UTF-8"; + private static final char[] BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray(); + + /** + * Hex编码. + */ + public static String encodeHex(byte[] input) { + return new String(Hex.encodeHex(input)); + } + + /** + * Hex解码. + */ + public static byte[] decodeHex(String input) { + try { + return Hex.decodeHex(input.toCharArray()); + } catch (DecoderException e) { + throw Exceptions.unchecked(e); + } + } + + /** + * Base64编码. + */ + public static String encodeBase64(byte[] input) { + return new String(Base64.encodeBase64(input)); + } + + /** + * Base64编码. + */ + public static String encodeBase64(String input) { + try { + return new String(Base64.encodeBase64(input.getBytes(DEFAULT_URL_ENCODING))); + } catch (UnsupportedEncodingException e) { + return ""; + } + } + +// /** +// * Base64编码, URL安全(将Base64中的URL非法字符'+'和'/'转为'-'和'_', 见RFC3548). +// */ +// public static String encodeUrlSafeBase64(byte[] input) { +// return Base64.encodeBase64URLSafe(input); +// } + + /** + * Base64解码. + */ + public static byte[] decodeBase64(String input) { + return Base64.decodeBase64(input.getBytes()); + } + + /** + * Base64解码. + */ + public static String decodeBase64String(String input) { + try { + return new String(Base64.decodeBase64(input.getBytes()), DEFAULT_URL_ENCODING); + } catch (UnsupportedEncodingException e) { + return ""; + } + } + + /** + * Base62编码。 + */ + public static String encodeBase62(byte[] input) { + char[] chars = new char[input.length]; + for (int i = 0; i < input.length; i++) { + chars[i] = BASE62[((input[i] & 0xFF) % BASE62.length)]; + } + return new String(chars); + } + + /** + * Html 转码. + */ + public static String escapeHtml(String html) { + return StringEscapeUtils.escapeHtml4(html); + } + + /** + * Html 解码. + */ + public static String unescapeHtml(String htmlEscaped) { + return StringEscapeUtils.unescapeHtml4(htmlEscaped); + } + + /** + * Xml 转码. + */ + public static String escapeXml(String xml) { + return StringEscapeUtils.escapeXml10(xml); + } + + /** + * Xml 解码. + */ + public static String unescapeXml(String xmlEscaped) { + return StringEscapeUtils.unescapeXml(xmlEscaped); + } + + /** + * URL 编码, Encode默认为UTF-8. + */ + public static String urlEncode(String part) { + try { + return URLEncoder.encode(part, DEFAULT_URL_ENCODING); + } catch (UnsupportedEncodingException e) { + throw Exceptions.unchecked(e); + } + } + + /** + * URL 解码, Encode默认为UTF-8. + */ + public static String urlDecode(String part) { + + try { + return URLDecoder.decode(part, DEFAULT_URL_ENCODING); + } catch (UnsupportedEncodingException e) { + throw Exceptions.unchecked(e); + } + } +} diff --git a/src/main/java/com/rymcu/vertical/util/ErrorCode.java b/src/main/java/com/rymcu/vertical/util/ErrorCode.java new file mode 100644 index 0000000..96a8e48 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/util/ErrorCode.java @@ -0,0 +1,29 @@ +package com.rymcu.vertical.util; + +/** + * Created by kaenry on 2016/9/20. + * ErrorCode + */ +public enum ErrorCode { + + ILLEGAL_PARAMS("ILLEGAL_PARAMS", "request params invalid"), + UNAUTHORIZED("401", "请求要求用户的身份认证"), + SERVER_ERROR("SERVER_ERROR", "server is busy"); + + ErrorCode(String code, String message) { + this.code = code; + this.message = message; + } + + private String code; + + private String message; + + public String getCode() { + return code; + } + + public String getMessage() { + return message; + } +} diff --git a/src/main/java/com/rymcu/vertical/util/Exceptions.java b/src/main/java/com/rymcu/vertical/util/Exceptions.java new file mode 100644 index 0000000..4cbd14c --- /dev/null +++ b/src/main/java/com/rymcu/vertical/util/Exceptions.java @@ -0,0 +1,69 @@ +/** + * Copyright © 2012-2016 zkjkgc All rights reserved. + */ +package com.rymcu.vertical.util; + +import javax.servlet.http.HttpServletRequest; +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * 关于异常的工具类. + */ +public class Exceptions { + + /** + * 将CheckedException转换为UncheckedException. + */ + public static RuntimeException unchecked(Exception e) { + if (e instanceof RuntimeException) { + return (RuntimeException) e; + } else { + return new RuntimeException(e); + } + } + + /** + * 将ErrorStack转化为String. + */ + public static String getStackTraceAsString(Throwable e) { + if (e == null){ + return ""; + } + StringWriter stringWriter = new StringWriter(); + e.printStackTrace(new PrintWriter(stringWriter)); + return stringWriter.toString(); + } + + /** + * 判断异常是否由某些底层的异常引起. + */ + public static boolean isCausedBy(Exception ex, Class... causeExceptionClasses) { + Throwable cause = ex.getCause(); + while (cause != null) { + for (Class causeClass : causeExceptionClasses) { + if (causeClass.isInstance(cause)) { + return true; + } + } + cause = cause.getCause(); + } + return false; + } + + /** + * 在request中获取异常类 + * @param request + * @return + */ + public static Throwable getThrowable(HttpServletRequest request){ + Throwable ex = null; + if (request.getAttribute("exception") != null) { + ex = (Throwable) request.getAttribute("exception"); + } else if (request.getAttribute("javax.servlet.error.exception") != null) { + ex = (Throwable) request.getAttribute("javax.servlet.error.exception"); + } + return ex; + } + +} diff --git a/src/main/java/com/rymcu/vertical/util/Utils.java b/src/main/java/com/rymcu/vertical/util/Utils.java new file mode 100644 index 0000000..4207483 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/util/Utils.java @@ -0,0 +1,55 @@ +package com.rymcu.vertical.util; + +import com.rymcu.vertical.entity.User; +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.session.InvalidSessionException; +import org.apache.shiro.session.Session; +import org.apache.shiro.subject.Subject; + +public class Utils { + public static final String HASH_ALGORITHM = "SHA-1"; + public static final int HASH_INTERATIONS = 1024; + public static final int SALT_SIZE = 8; + /** + * 生成安全的密码,生成随机的16位salt并经过1024次 sha-1 hash + */ + public static String entryptPassword(String plainPassword) { + String plain = Encodes.unescapeHtml(plainPassword); + byte[] salt = Digests.generateSalt(SALT_SIZE); + byte[] hashPassword = Digests.sha1(plain.getBytes(), salt, HASH_INTERATIONS); + return Encodes.encodeHex(salt)+Encodes.encodeHex(hashPassword); + } + + /** + *一般检查工具密码比对 add by xlf 2018-11-8 + * @param pwd + * @param enpwd 加密的密码 + * @return + */ + public static boolean comparePwd(String pwd,String enpwd){ + byte[] salt = Encodes.decodeHex(enpwd.substring(0,16)); + byte[] hashPassword = Digests.sha1(pwd.getBytes(), salt, HASH_INTERATIONS); + return enpwd.equals(Encodes.encodeHex(salt)+Encodes.encodeHex(hashPassword)); + } + + public static User getCurrentUser() { + return null; + } + + public static Session getSession() { + try{ + Subject subject = SecurityUtils.getSubject(); + Session session = subject.getSession(false); + if (session == null){ + session = subject.getSession(); + } + if (session != null){ + return session; + } +// subject.logout(); + }catch (InvalidSessionException e){ + + } + return null; + } +} diff --git a/src/main/java/com/rymcu/vertical/web/api/exception/ErrorCode.java b/src/main/java/com/rymcu/vertical/web/api/exception/ErrorCode.java new file mode 100644 index 0000000..ebea4c3 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/web/api/exception/ErrorCode.java @@ -0,0 +1,27 @@ +package com.rymcu.vertical.web.api.exception; + +public enum ErrorCode { + + UNAUTHORIZED(401, "请求要求用户的身份认证"),//未认证(签名错误) + INVALID_TOKEN(402, "TOKEN验证失败,无效的TOKEN!"), + TOKEN_(402, "TOKEN验证失败,无效的TOKEN!"), + NOT_FOUND(404, "此接口不存在"),//接口不存在 + INTERNAL_SERVER_ERROR(500, "服务内部异常"); + + private int code; + private String message; + + ErrorCode(int code, String message) { + this.code = code; + this.message = message; + } + + + public int getCode() { + return code; + } + + public String getMessage() { + return message; + } +} \ No newline at end of file diff --git a/src/main/java/com/rymcu/vertical/web/api/exception/MallApiException.java b/src/main/java/com/rymcu/vertical/web/api/exception/MallApiException.java new file mode 100644 index 0000000..29c397f --- /dev/null +++ b/src/main/java/com/rymcu/vertical/web/api/exception/MallApiException.java @@ -0,0 +1,27 @@ +package com.rymcu.vertical.web.api.exception; + +/** + * 服务(业务)异常如“ 账号或密码错误 ”,该异常只做INFO级别的日志记录 @see WebMvcConfigurer + */ +public class MallApiException extends Exception { + + private int code; + private String message; + + public MallApiException(ErrorCode errorCode) { + super(errorCode.getMessage()); + this.code = errorCode.getCode(); + this.message = errorCode.getMessage(); + } + + + public int getCode() { + return code; + } + + public String getExtraMessage() { + return message; + } + +} + diff --git a/src/main/java/com/rymcu/vertical/web/api/exception/MallApiExceptionHandler.java b/src/main/java/com/rymcu/vertical/web/api/exception/MallApiExceptionHandler.java new file mode 100644 index 0000000..5337a75 --- /dev/null +++ b/src/main/java/com/rymcu/vertical/web/api/exception/MallApiExceptionHandler.java @@ -0,0 +1,34 @@ +package com.rymcu.vertical.web.api.exception; + +import com.rymcu.vertical.config.HpeisExceptionHandler; +import com.rymcu.vertical.core.result.GlobalResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@ControllerAdvice(basePackages = {"com.rymcu.vertical.web.api", "com.rymcu.vertical.jwt"} ) +public class MallApiExceptionHandler { + + private final Logger logger = LoggerFactory.getLogger(HpeisExceptionHandler.class); + + @ExceptionHandler(MallApiException.class) + @ResponseBody + public GlobalResult handlerException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { + logger.error(ex.getMessage()); + GlobalResult result = new GlobalResult(); + if (ex instanceof MallApiException) { + result.setCode(((MallApiException) ex).getCode()); + result.setMessage(ex.getMessage()); + } /*else if (ex instanceof Exception) { + result.setCode(ErrorCode.INTERNAL_SERVER_ERROR.getCode()); + result.setMessage("用户无权限"); + }*/ + result.setSuccess(false); + return result; + } +} diff --git a/src/main/java/mapper/MenuMapper.xml b/src/main/java/mapper/MenuMapper.xml new file mode 100644 index 0000000..c2fa40e --- /dev/null +++ b/src/main/java/mapper/MenuMapper.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + ID, + PARENT_ID parentId, + (select NAME + from SYS_MENU + where ID = #{parentId}) parentName, + NAME, + SORT, + HREF, + MENU_TYPE menuType, + PERMISSION, + REMARKS, + STATUS + + + + ID, + PARENT_ID parentId, + (select NAME + from SYS_MENU + where ID = m.PARENT_ID) parentName, + NAME, + SORT, + HREF, + MENU_TYPE menuType, + PERMISSION, + REMARKS, + STATUS + + + + delete SYS_ROLE_MENU + where MENU_ID = #{menuId} + + + update SYS_MENU set STATUS = '2' where ID = #{id} + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/mapper/RoleMapper.xml b/src/main/java/mapper/RoleMapper.xml new file mode 100644 index 0000000..ea97e22 --- /dev/null +++ b/src/main/java/mapper/RoleMapper.xml @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + r.ID, + r.NAME, + r.ROLE_TYPE, + r.ENNAME, + r.REMARKS + + + + insert into SYS_ROLE_MENU (ROLE_ID, MENU_ID) VALUES (#{roleId}, #{menuId}) + + + insert into SYS_USER_ROLE (USER_ID, ROLE_ID) VALUES (#{userId}, #{roleId}) + + + + delete SYS_ROLE_MENU where ROLE_ID = #{roleId} + + + + delete SYS_USER_ROLE where ROLE_ID = #{roleId} + + + delete SYS_USER_ROLE where ROLE_ID = #{roleId} AND USER_ID = #{userId} + + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/mapper/UserMapper.xml b/src/main/java/mapper/UserMapper.xml new file mode 100644 index 0000000..f7c7103 --- /dev/null +++ b/src/main/java/mapper/UserMapper.xml @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + insert into SYS_USER_ROLE (USER_ID, ROLE_ID) VALUES (#{userId}, #{roleId}) + + + delete SYS_USER_ROLE + where USER_ID = #{userId} + + + update SYS_USER set STATUS = 2 where ID = #{id} + + + UPDATE SYS_USER SET PASSWORD = #{password} WHERE ID = #{userId} + + + UPDATE SYS_USER SET LAST_LOGIN_TIME = #{date} WHERE ID = #{id} + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1 @@ + diff --git a/src/test/java/com/rymcu/vertical/VerticalApplicationTests.java b/src/test/java/com/rymcu/vertical/VerticalApplicationTests.java new file mode 100644 index 0000000..b3e4934 --- /dev/null +++ b/src/test/java/com/rymcu/vertical/VerticalApplicationTests.java @@ -0,0 +1,13 @@ +package com.rymcu.vertical; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class VerticalApplicationTests { + + @Test + void contextLoads() { + } + +}