diff --git a/.gitignore b/.gitignore index 6143e53..3f695ff 100644 --- a/.gitignore +++ b/.gitignore @@ -1,22 +1,25 @@ -# Compiled class file -*.class +*/target/* +!.mvn/wrapper/maven-wrapper.jar -# Log file -*.log +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache -# BlueJ files -*.ctxt +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# Package Files # -*.jar -*.war -*.ear -*.zip -*.tar.gz -*.rar - -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* +### NetBeans ### +nbproject/private/ +build/ +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ \ No newline at end of file diff --git a/ClientDemo/.gitignore b/ClientDemo/.gitignore new file mode 100644 index 0000000..82eca33 --- /dev/null +++ b/ClientDemo/.gitignore @@ -0,0 +1,25 @@ +/target/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/build/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ \ No newline at end of file diff --git a/ClientDemo/.mvn/wrapper/maven-wrapper.jar b/ClientDemo/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000..9cc84ea Binary files /dev/null and b/ClientDemo/.mvn/wrapper/maven-wrapper.jar differ diff --git a/ClientDemo/.mvn/wrapper/maven-wrapper.properties b/ClientDemo/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..b573bb5 --- /dev/null +++ b/ClientDemo/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.3/apache-maven-3.5.3-bin.zip diff --git a/ClientDemo/mvnw b/ClientDemo/mvnw new file mode 100644 index 0000000..5bf251c --- /dev/null +++ b/ClientDemo/mvnw @@ -0,0 +1,225 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Migwn, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + # TODO classpath? +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +echo $MAVEN_PROJECTBASEDIR +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/ClientDemo/mvnw.cmd b/ClientDemo/mvnw.cmd new file mode 100644 index 0000000..019bd74 --- /dev/null +++ b/ClientDemo/mvnw.cmd @@ -0,0 +1,143 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" + +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +%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/ClientDemo/pom.xml b/ClientDemo/pom.xml new file mode 100644 index 0000000..2fdf94a --- /dev/null +++ b/ClientDemo/pom.xml @@ -0,0 +1,165 @@ + + + 4.0.0 + + cn.zifangsky + clientdemo + 0.0.1-SNAPSHOT + jar + + ClientDemo + Demo project for License + + + org.springframework.boot + spring-boot-starter-parent + 2.0.3.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + true + + 4.12 + + 3.7 + 2.6 + 1.11 + + 4.5.5 + + 1.2.47 + + 4.0.1 + + 1.33 + + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + junit + junit + ${junit} + test + + + + + org.apache.commons + commons-lang3 + ${commons-lang3} + + + commons-io + commons-io + ${commons-io} + + + commons-codec + commons-codec + ${commons-codec} + + + + + org.apache.httpcomponents + httpclient + ${httpclient} + + + + + com.alibaba + fastjson + ${fastjson} + + + + + javax.servlet + javax.servlet-api + ${javax.servlet-api} + provided + + + + + com.fasterxml.jackson.jaxrs + jackson-jaxrs-json-provider + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + + + + + org.slf4j + slf4j-api + + + org.slf4j + jcl-over-slf4j + + + + + de.schlichtherle.truelicense + truelicense-core + ${truelicense} + + + + net.sourceforge.nekohtml + nekohtml + 1.9.22 + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + src/main/resources + + + src/main/java + + **/*.properties + **/*.xml + **/*.tld + + false + + + + + diff --git a/ClientDemo/src/main/java/cn/zifangsky/ClientDemoApplication.java b/ClientDemo/src/main/java/cn/zifangsky/ClientDemoApplication.java new file mode 100644 index 0000000..c1037a1 --- /dev/null +++ b/ClientDemo/src/main/java/cn/zifangsky/ClientDemoApplication.java @@ -0,0 +1,19 @@ +package cn.zifangsky; + +import org.springframework.boot.Banner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.servlet.ServletComponentScan; +import org.springframework.context.annotation.PropertySource; + +@SpringBootApplication +@ServletComponentScan +@PropertySource({"license-config.properties"}) //加载额外的配置 +public class ClientDemoApplication { + + public static void main(String[] args) { + SpringApplication application = new SpringApplication(ClientDemoApplication.class); + application.setBannerMode(Banner.Mode.CONSOLE); + application.run(args); + } +} diff --git a/ClientDemo/src/main/java/cn/zifangsky/common/SpringContextUtils.java b/ClientDemo/src/main/java/cn/zifangsky/common/SpringContextUtils.java new file mode 100644 index 0000000..f801168 --- /dev/null +++ b/ClientDemo/src/main/java/cn/zifangsky/common/SpringContextUtils.java @@ -0,0 +1,72 @@ +package cn.zifangsky.common; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import java.util.Map; + +/** + * 自定义Spring工具类 + * @author zifangsky + */ +@Component +public class SpringContextUtils implements ApplicationContextAware { + private static ApplicationContext applicationContext; + + @Override + public void setApplicationContext(ApplicationContext context) throws BeansException { + applicationContext = context; + } + + /** + * 获取ApplicationContext对象 + * @return + */ + public static ApplicationContext getApplicationContext(){ + return applicationContext; + } + + /** + * 根据bean的名称获取bean + * @param name + * @return + */ + public static Object getBeanByName(String name){ + return applicationContext.getBean(name); + } + + /** + * 根据bean的class来查找对象 + * @param + * @param c + * @return + */ + public static T getBeanByClass(Class c){ + return applicationContext.getBean(c); + } + + /** + * 根据bean的class来查找所有的对象(包括子类) + * @param + * @param c + * @return + */ + public static Map getBeansByClass(Class c){ + return applicationContext.getBeansOfType(c); + } + + /** + * 获取HttpServletRequest + * @return + */ + public static HttpServletRequest getRequest() { + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); + HttpServletRequest request = attributes.getRequest(); + return request; + } +} diff --git a/ClientDemo/src/main/java/cn/zifangsky/config/RestTemplateConfig.java b/ClientDemo/src/main/java/cn/zifangsky/config/RestTemplateConfig.java new file mode 100644 index 0000000..623b57d --- /dev/null +++ b/ClientDemo/src/main/java/cn/zifangsky/config/RestTemplateConfig.java @@ -0,0 +1,94 @@ +package cn.zifangsky.config; + +import org.apache.http.Header; +import org.apache.http.client.HttpClient; +import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy; +import org.apache.http.impl.client.DefaultHttpRequestRetryHandler; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.message.BasicHeader; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +@Configuration +public class RestTemplateConfig { + + /** + * 返回RestTemplate + * @param factory ClientHttpRequestFactory + * @return RestTemplate + */ + @Bean + public RestTemplate restTemplate(ClientHttpRequestFactory factory){ + return new RestTemplate(factory); + } + + /** + * ClientHttpRequestFactory接口的第一种实现方式,即: + * SimpleClientHttpRequestFactory:底层使用java.net包提供的方式创建Http连接请求 + * @return + */ +// @Bean +// public SimpleClientHttpRequestFactory simpleClientHttpRequestFactory(){ +// SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); +// +// requestFactory.setReadTimeout(5000); +// requestFactory.setConnectTimeout(5000); +// +// return requestFactory; +// } + + /** + * ClientHttpRequestFactory接口的另一种实现方式(推荐使用),即: + * HttpComponentsClientHttpRequestFactory:底层使用Httpclient连接池的方式创建Http连接请求 + * @return HttpComponentsClientHttpRequestFactory + */ + @Bean + public HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory(){ + //Httpclient连接池,长连接保持30秒 + PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(30, TimeUnit.SECONDS); + + //设置总连接数 + connectionManager.setMaxTotal(1000); + //设置同路由的并发数 + connectionManager.setDefaultMaxPerRoute(1000); + + //设置header + List
headers = new ArrayList
(); + headers.add(new BasicHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.3; rv:36.0) Gecko/20100101 Firefox/36.04")); + headers.add(new BasicHeader("Accept-Encoding", "gzip, deflate")); + headers.add(new BasicHeader("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3")); + headers.add(new BasicHeader("Connection", "keep-alive")); + + //创建HttpClient + HttpClient httpClient = HttpClientBuilder.create() + .setConnectionManager(connectionManager) + .setDefaultHeaders(headers) + .setRetryHandler(new DefaultHttpRequestRetryHandler(3, true)) //设置重试次数 + .setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy()) //设置保持长连接 + .build(); + + //创建HttpComponentsClientHttpRequestFactory实例 + HttpComponentsClientHttpRequestFactory requestFactory = + new HttpComponentsClientHttpRequestFactory(httpClient); + + //设置客户端和服务端建立连接的超时时间 + requestFactory.setConnectTimeout(5000); + //设置客户端从服务端读取数据的超时时间 + requestFactory.setReadTimeout(5000); + //设置从连接池获取连接的超时时间,不宜过长 + requestFactory.setConnectionRequestTimeout(200); + //缓冲请求数据,默认为true。通过POST或者PUT大量发送数据时,建议将此更改为false,以免耗尽内存 + requestFactory.setBufferRequestBody(false); + + return requestFactory; + } + +} diff --git a/ClientDemo/src/main/java/cn/zifangsky/config/WebMvcConfig.java b/ClientDemo/src/main/java/cn/zifangsky/config/WebMvcConfig.java new file mode 100644 index 0000000..5942406 --- /dev/null +++ b/ClientDemo/src/main/java/cn/zifangsky/config/WebMvcConfig.java @@ -0,0 +1,34 @@ +package cn.zifangsky.config; + +import cn.zifangsky.license.LicenseCheckInterceptor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * Web相关配置 + * @author zifangsky + * @date 2018/7/9 + * @since 1.0.0 + */ +@Configuration +public class WebMvcConfig implements WebMvcConfigurer { + + /** + * 视图控制器 + */ + @Override + public void addViewControllers(ViewControllerRegistry registry) { + registry.addViewController("/index").setViewName("index"); + registry.addViewController("/login").setViewName("login"); + } + + /** + * 添加拦截器 + */ + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(new LicenseCheckInterceptor()).addPathPatterns("/check"); + } +} diff --git a/ClientDemo/src/main/java/cn/zifangsky/controller/LoginController.java b/ClientDemo/src/main/java/cn/zifangsky/controller/LoginController.java new file mode 100644 index 0000000..d816618 --- /dev/null +++ b/ClientDemo/src/main/java/cn/zifangsky/controller/LoginController.java @@ -0,0 +1,56 @@ +package cn.zifangsky.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.Map; + +/** + * 登录 + * @author zifangsky + * @date 2018/7/9 + * @since 1.0.0 + */ +@Controller +public class LoginController { + + /** + * 模拟登录验证 + * @author zifangsky + * @date 2018/7/9 17:09 + * @since 1.0.0 + * @param username 用户名 + * @param password 密码 + * @return java.util.Map + */ + @PostMapping("/check") + @ResponseBody + public Map test(@RequestParam(required = true) String username, @RequestParam(required = true) String password){ + Map result = new HashMap<>(1); + System.out.println(MessageFormat.format("用户名:{0},密码:{1}",username,password)); + + //模拟登录 + System.out.println("模拟登录流程"); + result.put("code",200); + + return result; + } + + /** + * 用户首页 + * @author zifangsky + * @date 2018/7/9 17:10 + * @since 1.0.0 + * @return java.lang.String + */ + @RequestMapping("/userIndex") + public String userIndex(){ + return "userIndex"; + } + +} diff --git a/ClientDemo/src/main/java/cn/zifangsky/license/AbstractServerInfos.java b/ClientDemo/src/main/java/cn/zifangsky/license/AbstractServerInfos.java new file mode 100644 index 0000000..4424a1c --- /dev/null +++ b/ClientDemo/src/main/java/cn/zifangsky/license/AbstractServerInfos.java @@ -0,0 +1,144 @@ +package cn.zifangsky.license; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; + +/** + * 用于获取客户服务器的基本信息,如:IP、Mac地址、CPU序列号、主板序列号等 + * + * @author zifangsky + * @date 2018/4/23 + * @since 1.0.0 + */ +public abstract class AbstractServerInfos { + private static Logger logger = LogManager.getLogger(AbstractServerInfos.class); + + /** + * 组装需要额外校验的License参数 + * @author zifangsky + * @date 2018/4/23 14:23 + * @since 1.0.0 + * @return demo.LicenseCheckModel + */ + public LicenseCheckModel getServerInfos(){ + LicenseCheckModel result = new LicenseCheckModel(); + + try { + result.setIpAddress(this.getIpAddress()); + result.setMacAddress(this.getMacAddress()); + result.setCpuSerial(this.getCPUSerial()); + result.setMainBoardSerial(this.getMainBoardSerial()); + }catch (Exception e){ + logger.error("获取服务器硬件信息失败",e); + } + + return result; + } + + /** + * 获取IP地址 + * @author zifangsky + * @date 2018/4/23 11:32 + * @since 1.0.0 + * @return java.util.List + */ + protected abstract List getIpAddress() throws Exception; + + /** + * 获取Mac地址 + * @author zifangsky + * @date 2018/4/23 11:32 + * @since 1.0.0 + * @return java.util.List + */ + protected abstract List getMacAddress() throws Exception; + + /** + * 获取CPU序列号 + * @author zifangsky + * @date 2018/4/23 11:35 + * @since 1.0.0 + * @return java.lang.String + */ + protected abstract String getCPUSerial() throws Exception; + + /** + * 获取主板序列号 + * @author zifangsky + * @date 2018/4/23 11:35 + * @since 1.0.0 + * @return java.lang.String + */ + protected abstract String getMainBoardSerial() throws Exception; + + /** + * 获取当前服务器所有符合条件的InetAddress + * @author zifangsky + * @date 2018/4/23 17:38 + * @since 1.0.0 + * @return java.util.List + */ + protected List getLocalAllInetAddress() throws Exception { + List result = new ArrayList<>(4); + + // 遍历所有的网络接口 + for (Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); networkInterfaces.hasMoreElements(); ) { + NetworkInterface iface = (NetworkInterface) networkInterfaces.nextElement(); + // 在所有的接口下再遍历IP + for (Enumeration inetAddresses = iface.getInetAddresses(); inetAddresses.hasMoreElements(); ) { + InetAddress inetAddr = (InetAddress) inetAddresses.nextElement(); + + //排除LoopbackAddress、SiteLocalAddress、LinkLocalAddress、MulticastAddress类型的IP地址 + if(!inetAddr.isLoopbackAddress() /*&& !inetAddr.isSiteLocalAddress()*/ + && !inetAddr.isLinkLocalAddress() && !inetAddr.isMulticastAddress()){ + result.add(inetAddr); + } + } + } + + return result; + } + + /** + * 获取某个网络接口的Mac地址 + * @author zifangsky + * @date 2018/4/23 18:08 + * @since 1.0.0 + * @param + * @return void + */ + protected String getMacByInetAddress(InetAddress inetAddr){ + try { + byte[] mac = NetworkInterface.getByInetAddress(inetAddr).getHardwareAddress(); + StringBuffer stringBuffer = new StringBuffer(); + + for(int i=0;i + * 用于将公私钥存储文件存放到其他磁盘位置而不是项目中 + * @author zifangsky + * @date 2018/4/26 18:28 + * @since 1.0.0 + * @param + * @return java.io.InputStream + */ + @Override + public InputStream getStream() throws IOException { + final InputStream in = new FileInputStream(new File(storePath)); + if (null == in){ + throw new FileNotFoundException(storePath); + } + + return in; + } +} diff --git a/ClientDemo/src/main/java/cn/zifangsky/license/CustomLicenseManager.java b/ClientDemo/src/main/java/cn/zifangsky/license/CustomLicenseManager.java new file mode 100644 index 0000000..ce90b97 --- /dev/null +++ b/ClientDemo/src/main/java/cn/zifangsky/license/CustomLicenseManager.java @@ -0,0 +1,292 @@ +package cn.zifangsky.license; + +import de.schlichtherle.license.LicenseContent; +import de.schlichtherle.license.LicenseContentException; +import de.schlichtherle.license.LicenseManager; +import de.schlichtherle.license.LicenseNotary; +import de.schlichtherle.license.LicenseParam; +import de.schlichtherle.license.NoLicenseInstalledException; +import de.schlichtherle.xml.GenericCertificate; +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.beans.XMLDecoder; +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.UnsupportedEncodingException; +import java.util.Date; +import java.util.List; + +/** + * 自定义LicenseManager,用于增加额外的服务器硬件信息校验 + * + * @author zifangsky + * @date 2018/4/23 + * @since 1.0.0 + */ +public class CustomLicenseManager extends LicenseManager{ + private static Logger logger = LogManager.getLogger(CustomLicenseManager.class); + + //XML编码 + private static final String XML_CHARSET = "UTF-8"; + //默认BUFSIZE + private static final int DEFAULT_BUFSIZE = 8 * 1024; + + public CustomLicenseManager() { + + } + + public CustomLicenseManager(LicenseParam param) { + super(param); + } + + /** + * 复写create方法 + * @author zifangsky + * @date 2018/4/23 10:36 + * @since 1.0.0 + * @param + * @return byte[] + */ + @Override + protected synchronized byte[] create( + LicenseContent content, + LicenseNotary notary) + throws Exception { + initialize(content); + this.validateCreate(content); + final GenericCertificate certificate = notary.sign(content); + return getPrivacyGuard().cert2key(certificate); + } + + /** + * 复写install方法,其中validate方法调用本类中的validate方法,校验IP地址、Mac地址等其他信息 + * @author zifangsky + * @date 2018/4/23 10:40 + * @since 1.0.0 + * @param + * @return de.schlichtherle.license.LicenseContent + */ + @Override + protected synchronized LicenseContent install( + final byte[] key, + final LicenseNotary notary) + throws Exception { + final GenericCertificate certificate = getPrivacyGuard().key2cert(key); + + notary.verify(certificate); + final LicenseContent content = (LicenseContent)this.load(certificate.getEncoded()); + this.validate(content); + setLicenseKey(key); + setCertificate(certificate); + + return content; + } + + /** + * 复写verify方法,调用本类中的validate方法,校验IP地址、Mac地址等其他信息 + * @author zifangsky + * @date 2018/4/23 10:40 + * @since 1.0.0 + * @param + * @return de.schlichtherle.license.LicenseContent + */ + @Override + protected synchronized LicenseContent verify(final LicenseNotary notary) + throws Exception { + GenericCertificate certificate = getCertificate(); + + // Load license key from preferences, + final byte[] key = getLicenseKey(); + if (null == key){ + throw new NoLicenseInstalledException(getLicenseParam().getSubject()); + } + + certificate = getPrivacyGuard().key2cert(key); + notary.verify(certificate); + final LicenseContent content = (LicenseContent)this.load(certificate.getEncoded()); + this.validate(content); + setCertificate(certificate); + + return content; + } + + /** + * 校验生成证书的参数信息 + * @author zifangsky + * @date 2018/5/2 15:43 + * @since 1.0.0 + * @param content 证书正文 + */ + protected synchronized void validateCreate(final LicenseContent content) + throws LicenseContentException { + final LicenseParam param = getLicenseParam(); + + final Date now = new Date(); + final Date notBefore = content.getNotBefore(); + final Date notAfter = content.getNotAfter(); + if (null != notAfter && now.after(notAfter)){ + throw new LicenseContentException("证书失效时间不能早于当前时间"); + } + if (null != notBefore && null != notAfter && notAfter.before(notBefore)){ + throw new LicenseContentException("证书生效时间不能晚于证书失效时间"); + } + final String consumerType = content.getConsumerType(); + if (null == consumerType){ + throw new LicenseContentException("用户类型不能为空"); + } + } + + + /** + * 复写validate方法,增加IP地址、Mac地址等其他信息校验 + * @author zifangsky + * @date 2018/4/23 10:40 + * @since 1.0.0 + * @param content LicenseContent + */ + @Override + protected synchronized void validate(final LicenseContent content) + throws LicenseContentException { + //1. 首先调用父类的validate方法 + super.validate(content); + + //2. 然后校验自定义的License参数 + //License中可被允许的参数信息 + LicenseCheckModel expectedCheckModel = (LicenseCheckModel) content.getExtra(); + //当前服务器真实的参数信息 + LicenseCheckModel serverCheckModel = getServerInfos(); + + if(expectedCheckModel != null && serverCheckModel != null){ + //校验IP地址 + if(!checkIpAddress(expectedCheckModel.getIpAddress(),serverCheckModel.getIpAddress())){ + throw new LicenseContentException("当前服务器的IP没在授权范围内"); + } + + //校验Mac地址 + if(!checkIpAddress(expectedCheckModel.getMacAddress(),serverCheckModel.getMacAddress())){ + throw new LicenseContentException("当前服务器的Mac地址没在授权范围内"); + } + + //校验主板序列号 + if(!checkSerial(expectedCheckModel.getMainBoardSerial(),serverCheckModel.getMainBoardSerial())){ + throw new LicenseContentException("当前服务器的主板序列号没在授权范围内"); + } + + //校验CPU序列号 + if(!checkSerial(expectedCheckModel.getCpuSerial(),serverCheckModel.getCpuSerial())){ + throw new LicenseContentException("当前服务器的CPU序列号没在授权范围内"); + } + }else{ + throw new LicenseContentException("不能获取服务器硬件信息"); + } + } + + + /** + * 重写XMLDecoder解析XML + * @author zifangsky + * @date 2018/4/25 14:02 + * @since 1.0.0 + * @param encoded XML类型字符串 + * @return java.lang.Object + */ + private Object load(String encoded){ + BufferedInputStream inputStream = null; + XMLDecoder decoder = null; + try { + inputStream = new BufferedInputStream(new ByteArrayInputStream(encoded.getBytes(XML_CHARSET))); + + decoder = new XMLDecoder(new BufferedInputStream(inputStream, DEFAULT_BUFSIZE),null,null); + + return decoder.readObject(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } finally { + try { + if(decoder != null){ + decoder.close(); + } + if(inputStream != null){ + inputStream.close(); + } + } catch (Exception e) { + logger.error("XMLDecoder解析XML失败",e); + } + } + + return null; + } + + /** + * 获取当前服务器需要额外校验的License参数 + * @author zifangsky + * @date 2018/4/23 14:33 + * @since 1.0.0 + * @return demo.LicenseCheckModel + */ + private LicenseCheckModel getServerInfos(){ + //操作系统类型 + String osName = System.getProperty("os.name").toLowerCase(); + AbstractServerInfos abstractServerInfos = null; + + //根据不同操作系统类型选择不同的数据获取方法 + if (osName.startsWith("windows")) { + abstractServerInfos = new WindowsServerInfos(); + } else if (osName.startsWith("linux")) { + abstractServerInfos = new LinuxServerInfos(); + }else{//其他服务器类型 + abstractServerInfos = new LinuxServerInfos(); + } + + return abstractServerInfos.getServerInfos(); + } + + /** + * 校验当前服务器的IP/Mac地址是否在可被允许的IP范围内
+ * 如果存在IP在可被允许的IP/Mac地址范围内,则返回true + * @author zifangsky + * @date 2018/4/24 11:44 + * @since 1.0.0 + * @return boolean + */ + private boolean checkIpAddress(List expectedList,List serverList){ + if(expectedList != null && expectedList.size() > 0){ + if(serverList != null && serverList.size() > 0){ + for(String expected : expectedList){ + if(serverList.contains(expected.trim())){ + return true; + } + } + } + + return false; + }else { + return true; + } + } + + /** + * 校验当前服务器硬件(主板、CPU等)序列号是否在可允许范围内 + * @author zifangsky + * @date 2018/4/24 14:38 + * @since 1.0.0 + * @param + * @return boolean + */ + private boolean checkSerial(String expectedSerial,String serverSerial){ + if(StringUtils.isNotBlank(expectedSerial)){ + if(StringUtils.isNotBlank(serverSerial)){ + if(expectedSerial.equals(serverSerial)){ + return true; + } + } + + return false; + }else{ + return true; + } + } + +} diff --git a/ClientDemo/src/main/java/cn/zifangsky/license/LicenseCheckInterceptor.java b/ClientDemo/src/main/java/cn/zifangsky/license/LicenseCheckInterceptor.java new file mode 100644 index 0000000..1d8829d --- /dev/null +++ b/ClientDemo/src/main/java/cn/zifangsky/license/LicenseCheckInterceptor.java @@ -0,0 +1,43 @@ +package cn.zifangsky.license; + +import com.alibaba.fastjson.JSON; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.HashMap; +import java.util.Map; + +/** + * LicenseCheckInterceptor + * + * @author zifangsky + * @date 2018/4/25 + * @since 1.0.0 + */ +public class LicenseCheckInterceptor extends HandlerInterceptorAdapter{ + private static Logger logger = LogManager.getLogger(LicenseCheckInterceptor.class); + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + LicenseVerify licenseVerify = new LicenseVerify(); + + //校验证书是否有效 + boolean verifyResult = licenseVerify.verify(); + + if(verifyResult){ + return true; + }else{ + response.setCharacterEncoding("utf-8"); + Map result = new HashMap<>(1); + result.put("result","您的证书无效,请核查服务器是否取得授权或重新申请证书!"); + + response.getWriter().write(JSON.toJSONString(result)); + + return false; + } + } + +} diff --git a/ClientDemo/src/main/java/cn/zifangsky/license/LicenseCheckListener.java b/ClientDemo/src/main/java/cn/zifangsky/license/LicenseCheckListener.java new file mode 100644 index 0000000..4adfaa0 --- /dev/null +++ b/ClientDemo/src/main/java/cn/zifangsky/license/LicenseCheckListener.java @@ -0,0 +1,76 @@ +package cn.zifangsky.license; + +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.stereotype.Component; + +/** + * 在项目启动时安装证书 + * + * @author zifangsky + * @date 2018/4/24 + * @since 1.0.0 + */ +@Component +public class LicenseCheckListener implements ApplicationListener { + private static Logger logger = LogManager.getLogger(LicenseCheckListener.class); + + /** + * 证书subject + */ + @Value("${license.subject}") + private String subject; + + /** + * 公钥别称 + */ + @Value("${license.publicAlias}") + private String publicAlias; + + /** + * 访问公钥库的密码 + */ + @Value("${license.storePass}") + private String storePass; + + /** + * 证书生成路径 + */ + @Value("${license.licensePath}") + private String licensePath; + + /** + * 密钥库存储路径 + */ + @Value("${license.publicKeysStorePath}") + private String publicKeysStorePath; + + @Override + public void onApplicationEvent(ContextRefreshedEvent event) { + //root application context 没有parent + ApplicationContext context = event.getApplicationContext().getParent(); + if(context == null){ + if(StringUtils.isNotBlank(licensePath)){ + logger.info("++++++++ 开始安装证书 ++++++++"); + + LicenseVerifyParam param = new LicenseVerifyParam(); + param.setSubject(subject); + param.setPublicAlias(publicAlias); + param.setStorePass(storePass); + param.setLicensePath(licensePath); + param.setPublicKeysStorePath(publicKeysStorePath); + + LicenseVerify licenseVerify = new LicenseVerify(); + //安装证书 + licenseVerify.install(param); + + logger.info("++++++++ 证书安装结束 ++++++++"); + } + } + } +} diff --git a/ClientDemo/src/main/java/cn/zifangsky/license/LicenseCheckModel.java b/ClientDemo/src/main/java/cn/zifangsky/license/LicenseCheckModel.java new file mode 100644 index 0000000..7ce412c --- /dev/null +++ b/ClientDemo/src/main/java/cn/zifangsky/license/LicenseCheckModel.java @@ -0,0 +1,77 @@ +package cn.zifangsky.license; + +import java.io.Serializable; +import java.util.List; + +/** + * 自定义需要校验的License参数 + * + * @author zifangsky + * @date 2018/4/23 + * @since 1.0.0 + */ +public class LicenseCheckModel implements Serializable{ + + private static final long serialVersionUID = 8600137500316662317L; + /** + * 可被允许的IP地址 + */ + private List ipAddress; + + /** + * 可被允许的MAC地址 + */ + private List macAddress; + + /** + * 可被允许的CPU序列号 + */ + private String cpuSerial; + + /** + * 可被允许的主板序列号 + */ + private String mainBoardSerial; + + public List getIpAddress() { + return ipAddress; + } + + public void setIpAddress(List ipAddress) { + this.ipAddress = ipAddress; + } + + public List getMacAddress() { + return macAddress; + } + + public void setMacAddress(List macAddress) { + this.macAddress = macAddress; + } + + public String getCpuSerial() { + return cpuSerial; + } + + public void setCpuSerial(String cpuSerial) { + this.cpuSerial = cpuSerial; + } + + public String getMainBoardSerial() { + return mainBoardSerial; + } + + public void setMainBoardSerial(String mainBoardSerial) { + this.mainBoardSerial = mainBoardSerial; + } + + @Override + public String toString() { + return "LicenseCheckModel{" + + "ipAddress=" + ipAddress + + ", macAddress=" + macAddress + + ", cpuSerial='" + cpuSerial + '\'' + + ", mainBoardSerial='" + mainBoardSerial + '\'' + + '}'; + } +} diff --git a/ClientDemo/src/main/java/cn/zifangsky/license/LicenseManagerHolder.java b/ClientDemo/src/main/java/cn/zifangsky/license/LicenseManagerHolder.java new file mode 100644 index 0000000..2f8dd04 --- /dev/null +++ b/ClientDemo/src/main/java/cn/zifangsky/license/LicenseManagerHolder.java @@ -0,0 +1,29 @@ +package cn.zifangsky.license; + +import de.schlichtherle.license.LicenseManager; +import de.schlichtherle.license.LicenseParam; + +/** + * de.schlichtherle.license.LicenseManager的单例 + * + * @author zifangsky + * @date 2018/4/19 + * @since 1.0.0 + */ +public class LicenseManagerHolder { + + private static volatile LicenseManager LICENSE_MANAGER; + + public static LicenseManager getInstance(LicenseParam param){ + if(LICENSE_MANAGER == null){ + synchronized (LicenseManagerHolder.class){ + if(LICENSE_MANAGER == null){ + LICENSE_MANAGER = new CustomLicenseManager(param); + } + } + } + + return LICENSE_MANAGER; + } + +} diff --git a/ClientDemo/src/main/java/cn/zifangsky/license/LicenseVerify.java b/ClientDemo/src/main/java/cn/zifangsky/license/LicenseVerify.java new file mode 100644 index 0000000..90823b2 --- /dev/null +++ b/ClientDemo/src/main/java/cn/zifangsky/license/LicenseVerify.java @@ -0,0 +1,96 @@ +package cn.zifangsky.license; + +import de.schlichtherle.license.*; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.File; +import java.text.DateFormat; +import java.text.MessageFormat; +import java.text.SimpleDateFormat; +import java.util.prefs.Preferences; + +/** + * License校验类 + * + * @author zifangsky + * @date 2018/4/20 + * @since 1.0.0 + */ +public class LicenseVerify { + private static Logger logger = LogManager.getLogger(LicenseVerify.class); + + /** + * 安装License证书 + * @author zifangsky + * @date 2018/4/20 16:26 + * @since 1.0.0 + */ + public synchronized LicenseContent install(LicenseVerifyParam param){ + LicenseContent result = null; + DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + //1. 安装证书 + try{ + LicenseManager licenseManager = LicenseManagerHolder.getInstance(initLicenseParam(param)); + licenseManager.uninstall(); + + result = licenseManager.install(new File(param.getLicensePath())); + logger.info(MessageFormat.format("证书安装成功,证书有效期:{0} - {1}",format.format(result.getNotBefore()),format.format(result.getNotAfter()))); + }catch (Exception e){ + logger.error("证书安装失败!",e); + } + + return result; + } + + /** + * 校验License证书 + * @author zifangsky + * @date 2018/4/20 16:26 + * @since 1.0.0 + * @return boolean + */ + public boolean verify(){ + LicenseManager licenseManager = LicenseManagerHolder.getInstance(null); + DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + //2. 校验证书 + try { + LicenseContent licenseContent = licenseManager.verify(); +// System.out.println(licenseContent.getSubject()); + + logger.info(MessageFormat.format("证书校验通过,证书有效期:{0} - {1}",format.format(licenseContent.getNotBefore()),format.format(licenseContent.getNotAfter()))); + return true; + }catch (Exception e){ + logger.error("证书校验失败!",e); + return false; + } + } + + /** + * 初始化证书生成参数 + * @author zifangsky + * @date 2018/4/20 10:56 + * @since 1.0.0 + * @param param License校验类需要的参数 + * @return de.schlichtherle.license.LicenseParam + */ + private LicenseParam initLicenseParam(LicenseVerifyParam param){ + Preferences preferences = Preferences.userNodeForPackage(LicenseVerify.class); + + CipherParam cipherParam = new DefaultCipherParam(param.getStorePass()); + + KeyStoreParam publicStoreParam = new CustomKeyStoreParam(LicenseVerify.class + ,param.getPublicKeysStorePath() + ,param.getPublicAlias() + ,param.getStorePass() + ,null); + + return new DefaultLicenseParam(param.getSubject() + ,preferences + ,publicStoreParam + ,cipherParam); + } + +} diff --git a/ClientDemo/src/main/java/cn/zifangsky/license/LicenseVerifyParam.java b/ClientDemo/src/main/java/cn/zifangsky/license/LicenseVerifyParam.java new file mode 100644 index 0000000..7f45ba3 --- /dev/null +++ b/ClientDemo/src/main/java/cn/zifangsky/license/LicenseVerifyParam.java @@ -0,0 +1,99 @@ +package cn.zifangsky.license; + +/** + * License校验类需要的参数 + * + * @author zifangsky + * @date 2018/4/20 + * @since 1.0.0 + */ +public class LicenseVerifyParam { + + /** + * 证书subject + */ + private String subject; + + /** + * 公钥别称 + */ + private String publicAlias; + + /** + * 访问公钥库的密码 + */ + private String storePass; + + /** + * 证书生成路径 + */ + private String licensePath; + + /** + * 密钥库存储路径 + */ + private String publicKeysStorePath; + + public LicenseVerifyParam() { + + } + + public LicenseVerifyParam(String subject, String publicAlias, String storePass, String licensePath, String publicKeysStorePath) { + this.subject = subject; + this.publicAlias = publicAlias; + this.storePass = storePass; + this.licensePath = licensePath; + this.publicKeysStorePath = publicKeysStorePath; + } + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public String getPublicAlias() { + return publicAlias; + } + + public void setPublicAlias(String publicAlias) { + this.publicAlias = publicAlias; + } + + public String getStorePass() { + return storePass; + } + + public void setStorePass(String storePass) { + this.storePass = storePass; + } + + public String getLicensePath() { + return licensePath; + } + + public void setLicensePath(String licensePath) { + this.licensePath = licensePath; + } + + public String getPublicKeysStorePath() { + return publicKeysStorePath; + } + + public void setPublicKeysStorePath(String publicKeysStorePath) { + this.publicKeysStorePath = publicKeysStorePath; + } + + @Override + public String toString() { + return "LicenseVerifyParam{" + + "subject='" + subject + '\'' + + ", publicAlias='" + publicAlias + '\'' + + ", storePass='" + storePass + '\'' + + ", licensePath='" + licensePath + '\'' + + ", publicKeysStorePath='" + publicKeysStorePath + '\'' + + '}'; + } +} diff --git a/ClientDemo/src/main/java/cn/zifangsky/license/LinuxServerInfos.java b/ClientDemo/src/main/java/cn/zifangsky/license/LinuxServerInfos.java new file mode 100644 index 0000000..1735f6e --- /dev/null +++ b/ClientDemo/src/main/java/cn/zifangsky/license/LinuxServerInfos.java @@ -0,0 +1,90 @@ +package cn.zifangsky.license; + +import org.apache.commons.lang3.StringUtils; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.InetAddress; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 用于获取客户Linux服务器的基本信息 + * + * @author zifangsky + * @date 2018/4/23 + * @since 1.0.0 + */ +public class LinuxServerInfos extends AbstractServerInfos{ + + @Override + protected List getIpAddress() throws Exception { + List result = null; + + //获取所有网络接口 + List inetAddresses = getLocalAllInetAddress(); + + if(inetAddresses != null && inetAddresses.size() > 0){ + result = inetAddresses.stream().map(InetAddress::getHostAddress).distinct().map(String::toLowerCase).collect(Collectors.toList()); + } + + return result; + } + + @Override + protected List getMacAddress() throws Exception { + List result = null; + + //1. 获取所有网络接口 + List inetAddresses = getLocalAllInetAddress(); + + if(inetAddresses != null && inetAddresses.size() > 0){ + //2. 获取所有网络接口的Mac地址 + result = inetAddresses.stream().map(this::getMacByInetAddress).distinct().collect(Collectors.toList()); + } + + return result; + } + + @Override + protected String getCPUSerial() throws Exception { + //序列号 + String serialNumber = ""; + + //使用dmidecode命令获取CPU序列号 + String[] shell = {"/bin/bash","-c","dmidecode -t processor | grep 'ID' | awk -F ':' '{print $2}' | head -n 1"}; + Process process = Runtime.getRuntime().exec(shell); + process.getOutputStream().close(); + + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + + String line = reader.readLine().trim(); + if(StringUtils.isNotBlank(line)){ + serialNumber = line; + } + + reader.close(); + return serialNumber; + } + + @Override + protected String getMainBoardSerial() throws Exception { + //序列号 + String serialNumber = ""; + + //使用dmidecode命令获取主板序列号 + String[] shell = {"/bin/bash","-c","dmidecode | grep 'Serial Number' | awk -F ':' '{print $2}' | head -n 1"}; + Process process = Runtime.getRuntime().exec(shell); + process.getOutputStream().close(); + + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + + String line = reader.readLine().trim(); + if(StringUtils.isNotBlank(line)){ + serialNumber = line; + } + + reader.close(); + return serialNumber; + } +} diff --git a/ClientDemo/src/main/java/cn/zifangsky/license/WindowsServerInfos.java b/ClientDemo/src/main/java/cn/zifangsky/license/WindowsServerInfos.java new file mode 100644 index 0000000..c9d3cd5 --- /dev/null +++ b/ClientDemo/src/main/java/cn/zifangsky/license/WindowsServerInfos.java @@ -0,0 +1,89 @@ +package cn.zifangsky.license; + +import java.net.InetAddress; +import java.util.List; +import java.util.Scanner; +import java.util.stream.Collectors; + +/** + * 用于获取客户Windows服务器的基本信息 + * + * @author zifangsky + * @date 2018/4/23 + * @since 1.0.0 + */ +public class WindowsServerInfos extends AbstractServerInfos{ + + @Override + protected List getIpAddress() throws Exception { + List result = null; + + //获取所有网络接口 + List inetAddresses = getLocalAllInetAddress(); + + if(inetAddresses != null && inetAddresses.size() > 0){ + result = inetAddresses.stream().map(InetAddress::getHostAddress).distinct().map(String::toLowerCase).collect(Collectors.toList()); + } + + return result; + } + + @Override + protected List getMacAddress() throws Exception { + List result = null; + + //1. 获取所有网络接口 + List inetAddresses = getLocalAllInetAddress(); + + if(inetAddresses != null && inetAddresses.size() > 0){ + //2. 获取所有网络接口的Mac地址 + result = inetAddresses.stream().map(this::getMacByInetAddress).distinct().collect(Collectors.toList()); + } + + return result; + } + + @Override + protected String getCPUSerial() throws Exception { + //序列号 + String serialNumber = ""; + + //使用WMIC获取CPU序列号 + Process process = Runtime.getRuntime().exec("wmic cpu get processorid"); + process.getOutputStream().close(); + Scanner scanner = new Scanner(process.getInputStream()); + + if(scanner != null && scanner.hasNext()){ + scanner.next(); + } + + if(scanner.hasNext()){ + serialNumber = scanner.next().trim(); + } + + scanner.close(); + return serialNumber; + } + + @Override + protected String getMainBoardSerial() throws Exception { + //序列号 + String serialNumber = ""; + + //使用WMIC获取主板序列号 + Process process = Runtime.getRuntime().exec("wmic baseboard get serialnumber"); + process.getOutputStream().close(); + Scanner scanner = new Scanner(process.getInputStream()); + + if(scanner != null && scanner.hasNext()){ + scanner.next(); + } + + if(scanner.hasNext()){ + serialNumber = scanner.next().trim(); + } + + scanner.close(); + return serialNumber; + } +} diff --git a/ClientDemo/src/main/resources/application-dev.properties b/ClientDemo/src/main/resources/application-dev.properties new file mode 100644 index 0000000..1eb99ce --- /dev/null +++ b/ClientDemo/src/main/resources/application-dev.properties @@ -0,0 +1,18 @@ +server.port=7080 + +#session超时时间 +server.servlet.session.timeout=300 + +#是否启用debug模式 +#debug=true + +logging.level.org.springframework.web=DEBUG +#logging.file=D:/runtime/test.log + +#thymeleaf start +spring.thymeleaf.mode=LEGACYHTML5 +spring.thymeleaf.prefix=classpath:/templates/ +spring.thymeleaf.suffix=.html +spring.thymeleaf.template-resolver-order=0 +spring.thymeleaf.cache=false +#thymeleaf end \ No newline at end of file diff --git a/ClientDemo/src/main/resources/application-prod.properties b/ClientDemo/src/main/resources/application-prod.properties new file mode 100644 index 0000000..a3ac65c --- /dev/null +++ b/ClientDemo/src/main/resources/application-prod.properties @@ -0,0 +1 @@ +server.port=8080 \ No newline at end of file diff --git a/ClientDemo/src/main/resources/application.properties b/ClientDemo/src/main/resources/application.properties new file mode 100644 index 0000000..66d6d0c --- /dev/null +++ b/ClientDemo/src/main/resources/application.properties @@ -0,0 +1,2 @@ +#使用哪个环境的配置 +spring.profiles.active=dev diff --git a/ClientDemo/src/main/resources/banner.txt b/ClientDemo/src/main/resources/banner.txt new file mode 100644 index 0000000..f7febe4 --- /dev/null +++ b/ClientDemo/src/main/resources/banner.txt @@ -0,0 +1,22 @@ +${AnsiColor.BLUE} + _ooOoo_ + o8888888o + 88" . "88 + (| ^_^ |) + O\ = /O + ____/`---'\____ + .' \\| | `. + / \\||| : ||| \ + / _||||| -:- |||||- \ + | | \\\ - / | | + | \_| ''\---/'' | | + \ .-\__ `-` ___/-. / + ___`. .' /--.--\ `. . ___ + ."" '< `.___\_<|>_/___.' >'"". + | | : `- \`.;`\ _ /`;.`/ - ` : | | + \ \ `-. \_ __\ /__ _/ .-` / / + ========`-.____`-.___\_____/___.-`____.-'======== + `=---=' + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 佛祖保佑 永无BUG + \ No newline at end of file diff --git a/ClientDemo/src/main/resources/license-config.properties b/ClientDemo/src/main/resources/license-config.properties new file mode 100644 index 0000000..8de928f --- /dev/null +++ b/ClientDemo/src/main/resources/license-config.properties @@ -0,0 +1,6 @@ +#License相关配置 +license.subject=license_demo +license.publicAlias=publicCert +license.storePass=public_password1234 +license.licensePath=C:/Users/zifangsky/Desktop/license_demo/license.lic +license.publicKeysStorePath=C:/Users/zifangsky/Desktop/license_demo/publicCerts.keystore diff --git a/ClientDemo/src/main/resources/static/css/style.css b/ClientDemo/src/main/resources/static/css/style.css new file mode 100644 index 0000000..a49c452 --- /dev/null +++ b/ClientDemo/src/main/resources/static/css/style.css @@ -0,0 +1,24 @@ +body{ + background: url("../img/3.jpg"); + animation-name:myfirst; + animation-duration:20s; + /*变换时间*/ + animation-delay:5s; + /*动画开始时间*/ + animation-iteration-count:infinite; + /*下一周期循环播放*/ + animation-play-state:running; + /*动画开始运行*/ +} +@keyframes myfirst +{ + 0% {background:url("../img/3.jpg");} + 34% {background:url("../img/2.jpg");} + 67% {background:url("../img/1.jpg");} + 100% {background:url("../img/3.jpg");} +} +.form{background: rgba(255,255,255,0.2);width:400px;margin:120px auto;} +/*阴影*/ +.fa{display: inline-block;top: 27px;left: 6px;position: relative;color: #ccc;} +input[type="text"],input[type="password"]{padding-left:26px;} +.errMsg{color: red;} \ No newline at end of file diff --git a/ClientDemo/src/main/resources/static/favicon.ico b/ClientDemo/src/main/resources/static/favicon.ico new file mode 100644 index 0000000..0ad0e67 Binary files /dev/null and b/ClientDemo/src/main/resources/static/favicon.ico differ diff --git a/ClientDemo/src/main/resources/static/img/1.jpg b/ClientDemo/src/main/resources/static/img/1.jpg new file mode 100644 index 0000000..23fd153 Binary files /dev/null and b/ClientDemo/src/main/resources/static/img/1.jpg differ diff --git a/ClientDemo/src/main/resources/static/img/2.jpg b/ClientDemo/src/main/resources/static/img/2.jpg new file mode 100644 index 0000000..b794cfe Binary files /dev/null and b/ClientDemo/src/main/resources/static/img/2.jpg differ diff --git a/ClientDemo/src/main/resources/static/img/3.jpg b/ClientDemo/src/main/resources/static/img/3.jpg new file mode 100644 index 0000000..b88bbbb Binary files /dev/null and b/ClientDemo/src/main/resources/static/img/3.jpg differ diff --git a/ClientDemo/src/main/resources/static/img/4.jpg b/ClientDemo/src/main/resources/static/img/4.jpg new file mode 100644 index 0000000..8bea6bd Binary files /dev/null and b/ClientDemo/src/main/resources/static/img/4.jpg differ diff --git a/ClientDemo/src/main/resources/static/img/5.jpg b/ClientDemo/src/main/resources/static/img/5.jpg new file mode 100644 index 0000000..eff17a2 Binary files /dev/null and b/ClientDemo/src/main/resources/static/img/5.jpg differ diff --git a/ClientDemo/src/main/resources/static/img/6.jpg b/ClientDemo/src/main/resources/static/img/6.jpg new file mode 100644 index 0000000..0422284 Binary files /dev/null and b/ClientDemo/src/main/resources/static/img/6.jpg differ diff --git a/ClientDemo/src/main/resources/static/img/7.jpg b/ClientDemo/src/main/resources/static/img/7.jpg new file mode 100644 index 0000000..f26e836 Binary files /dev/null and b/ClientDemo/src/main/resources/static/img/7.jpg differ diff --git a/ClientDemo/src/main/resources/templates/index.html b/ClientDemo/src/main/resources/templates/index.html new file mode 100644 index 0000000..2ffc3ee --- /dev/null +++ b/ClientDemo/src/main/resources/templates/index.html @@ -0,0 +1,46 @@ + + + + + + 首页 + + + + + + + + + \ No newline at end of file diff --git a/ClientDemo/src/main/resources/templates/login.html b/ClientDemo/src/main/resources/templates/login.html new file mode 100644 index 0000000..40cd92c --- /dev/null +++ b/ClientDemo/src/main/resources/templates/login.html @@ -0,0 +1,93 @@ + + + + + + 登录页面 + < + + + + + + + + + +
+
+
+

LOGIN

+
+
+ + +
+
+ + +
+
+ +
+
+ +
+
+
+
+
+ + \ No newline at end of file diff --git a/ClientDemo/src/main/resources/templates/userIndex.html b/ClientDemo/src/main/resources/templates/userIndex.html new file mode 100644 index 0000000..1e989dd --- /dev/null +++ b/ClientDemo/src/main/resources/templates/userIndex.html @@ -0,0 +1,19 @@ + + + + + + 用户首页 + + + + + + + + + \ No newline at end of file diff --git a/ClientDemo/src/test/java/cn/zifangsky/ClientDemoApplicationTests.java b/ClientDemo/src/test/java/cn/zifangsky/ClientDemoApplicationTests.java new file mode 100644 index 0000000..e471e40 --- /dev/null +++ b/ClientDemo/src/test/java/cn/zifangsky/ClientDemoApplicationTests.java @@ -0,0 +1,16 @@ +package cn.zifangsky; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class ClientDemoApplicationTests { + + @Test + public void contextLoads() { + } + +} diff --git a/README.md b/README.md index d8e0aad..74dd864 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,65 @@ # LicenseDemo #### 项目介绍 -有关基于Spring的项目中使用 TrueLicense 生成和验证License(服务器许可)的示例代码 +在基于Spring的项目中使用 `TrueLicense `生成和验证`License证书`(服务器许可)的示例代码 -#### 软件架构 -软件架构说明 +#### 技术依赖: +* `Spring Boot`:项目基础架构,包括提供`基本Web服务`和`定时调度服务` +* `TrueLicense `:基于`Java`实现的生成和验证服务器许可的简单框架 +#### 环境依赖: +* `JDK8+` -#### 安装教程 +#### 两个子项目说明: #### -1. xxxx -2. xxxx -3. xxxx +- `ServerDemo`:用于**开发者**给客户生成`License证书`的示例代码 +- `ClientDemo`:**模拟需要给客户部署的业务项目** -#### 使用说明 +#### ServerDemo项目: #### -1. xxxx -2. xxxx -3. xxxx +对外发布了两个RESTful接口: -#### 参与贡献 +(1)获取服务器硬件信息 : -1. Fork 本项目 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request +请求地址:`http://127.0.0.1:7000/license/getServerInfos` +![获取服务器硬件信息](https://www.zifangsky.cn/wp-content/uploads/2018/07/20180710140711.png) -#### 码云特技 +(2)生成证书 : + +请求地址:`http://127.0.0.1:7000/license/generateLicense` + +请求时需要在Header中添加一个 **Content-Type** ,其值为:**application/json;charset=UTF-8**。请求参数如下: + +```json +{ + "subject": "license_demo", + "privateAlias": "privateKey", + "keyPass": "private_password1234", + "storePass": "public_password1234", + "licensePath": "C:/Users/zifangsky/Desktop/license_demo/license.lic", + "privateKeysStorePath": "C:/Users/zifangsky/Desktop/license_demo/privateKeys.keystore", + "issuedTime": "2018-07-10 00:00:01", + "expiryTime": "2019-12-31 23:59:59", + "consumerType": "User", + "consumerAmount": 1, + "description": "这是证书描述信息", + "licenseCheckModel": { + "ipAddress": ["192.168.245.1", "10.0.5.22"], + "macAddress": ["00-50-56-C0-00-01", "50-7B-9D-F9-18-41"], + "cpuSerial": "BFEBFBFF000406E3", + "mainBoardSerial": "L1HF65E00X9" + } +} +``` + +![生成证书](https://www.zifangsky.cn/wp-content/uploads/2018/07/20180710141528.png) + +#### ClientDemo项目: #### + +项目启动时安装证书,通过`cn/zifangsky/license/LicenseCheckListener.java`类实现。用户登录时校验证书的可用性,通过`cn/zifangsky/license/LicenseCheckInterceptor.java`类实现。 + +#### 特别说明: #### + +详细开发思路可以参考我写的这篇文章:[https://www.zifangsky.cn/1277.html](https://www.zifangsky.cn/1277.html) -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. 码云官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解码云上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是码云最有价值开源项目,是码云综合评定出的优秀开源项目 -5. 码云官方提供的使用手册 [http://git.mydoc.io/](http://git.mydoc.io/) -6. 码云封面人物是一档用来展示码云会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) \ No newline at end of file diff --git a/ServerDemo/.gitignore b/ServerDemo/.gitignore new file mode 100644 index 0000000..82eca33 --- /dev/null +++ b/ServerDemo/.gitignore @@ -0,0 +1,25 @@ +/target/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/build/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ \ No newline at end of file diff --git a/ServerDemo/.mvn/wrapper/maven-wrapper.jar b/ServerDemo/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000..9cc84ea Binary files /dev/null and b/ServerDemo/.mvn/wrapper/maven-wrapper.jar differ diff --git a/ServerDemo/.mvn/wrapper/maven-wrapper.properties b/ServerDemo/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..b573bb5 --- /dev/null +++ b/ServerDemo/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.3/apache-maven-3.5.3-bin.zip diff --git a/ServerDemo/mvnw b/ServerDemo/mvnw new file mode 100644 index 0000000..5bf251c --- /dev/null +++ b/ServerDemo/mvnw @@ -0,0 +1,225 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Migwn, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + # TODO classpath? +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +echo $MAVEN_PROJECTBASEDIR +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/ServerDemo/mvnw.cmd b/ServerDemo/mvnw.cmd new file mode 100644 index 0000000..019bd74 --- /dev/null +++ b/ServerDemo/mvnw.cmd @@ -0,0 +1,143 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" + +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +%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/ServerDemo/pom.xml b/ServerDemo/pom.xml new file mode 100644 index 0000000..56f41f2 --- /dev/null +++ b/ServerDemo/pom.xml @@ -0,0 +1,165 @@ + + + 4.0.0 + + cn.zifangsky + serverdemo + 0.0.1-SNAPSHOT + jar + + ServerDemo + Demo project for License + + + org.springframework.boot + spring-boot-starter-parent + 2.0.3.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + true + + 4.12 + + 3.7 + 2.6 + 1.11 + + 4.5.5 + + 1.2.47 + + 4.0.1 + + 1.33 + + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + junit + junit + ${junit} + test + + + + + org.apache.commons + commons-lang3 + ${commons-lang3} + + + commons-io + commons-io + ${commons-io} + + + commons-codec + commons-codec + ${commons-codec} + + + + + org.apache.httpcomponents + httpclient + ${httpclient} + + + + + com.alibaba + fastjson + ${fastjson} + + + + + javax.servlet + javax.servlet-api + ${javax.servlet-api} + provided + + + + + com.fasterxml.jackson.jaxrs + jackson-jaxrs-json-provider + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + + + + + org.slf4j + slf4j-api + + + org.slf4j + jcl-over-slf4j + + + + + de.schlichtherle.truelicense + truelicense-core + ${truelicense} + + + + net.sourceforge.nekohtml + nekohtml + 1.9.22 + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + src/main/resources + + + src/main/java + + **/*.properties + **/*.xml + **/*.tld + + false + + + + + \ No newline at end of file diff --git a/ServerDemo/src/main/java/cn/zifangsky/ServerDemoApplication.java b/ServerDemo/src/main/java/cn/zifangsky/ServerDemoApplication.java new file mode 100644 index 0000000..95dbd34 --- /dev/null +++ b/ServerDemo/src/main/java/cn/zifangsky/ServerDemoApplication.java @@ -0,0 +1,16 @@ +package cn.zifangsky; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.servlet.ServletComponentScan; +import org.springframework.context.annotation.PropertySource; + +@SpringBootApplication +@ServletComponentScan +@PropertySource({"license-config.properties"}) //加载额外的配置 +public class ServerDemoApplication { + + public static void main(String[] args) { + SpringApplication.run(ServerDemoApplication.class, args); + } +} diff --git a/ServerDemo/src/main/java/cn/zifangsky/common/SpringContextUtils.java b/ServerDemo/src/main/java/cn/zifangsky/common/SpringContextUtils.java new file mode 100644 index 0000000..f801168 --- /dev/null +++ b/ServerDemo/src/main/java/cn/zifangsky/common/SpringContextUtils.java @@ -0,0 +1,72 @@ +package cn.zifangsky.common; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import java.util.Map; + +/** + * 自定义Spring工具类 + * @author zifangsky + */ +@Component +public class SpringContextUtils implements ApplicationContextAware { + private static ApplicationContext applicationContext; + + @Override + public void setApplicationContext(ApplicationContext context) throws BeansException { + applicationContext = context; + } + + /** + * 获取ApplicationContext对象 + * @return + */ + public static ApplicationContext getApplicationContext(){ + return applicationContext; + } + + /** + * 根据bean的名称获取bean + * @param name + * @return + */ + public static Object getBeanByName(String name){ + return applicationContext.getBean(name); + } + + /** + * 根据bean的class来查找对象 + * @param + * @param c + * @return + */ + public static T getBeanByClass(Class c){ + return applicationContext.getBean(c); + } + + /** + * 根据bean的class来查找所有的对象(包括子类) + * @param + * @param c + * @return + */ + public static Map getBeansByClass(Class c){ + return applicationContext.getBeansOfType(c); + } + + /** + * 获取HttpServletRequest + * @return + */ + public static HttpServletRequest getRequest() { + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); + HttpServletRequest request = attributes.getRequest(); + return request; + } +} diff --git a/ServerDemo/src/main/java/cn/zifangsky/config/RestTemplateConfig.java b/ServerDemo/src/main/java/cn/zifangsky/config/RestTemplateConfig.java new file mode 100644 index 0000000..623b57d --- /dev/null +++ b/ServerDemo/src/main/java/cn/zifangsky/config/RestTemplateConfig.java @@ -0,0 +1,94 @@ +package cn.zifangsky.config; + +import org.apache.http.Header; +import org.apache.http.client.HttpClient; +import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy; +import org.apache.http.impl.client.DefaultHttpRequestRetryHandler; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.message.BasicHeader; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +@Configuration +public class RestTemplateConfig { + + /** + * 返回RestTemplate + * @param factory ClientHttpRequestFactory + * @return RestTemplate + */ + @Bean + public RestTemplate restTemplate(ClientHttpRequestFactory factory){ + return new RestTemplate(factory); + } + + /** + * ClientHttpRequestFactory接口的第一种实现方式,即: + * SimpleClientHttpRequestFactory:底层使用java.net包提供的方式创建Http连接请求 + * @return + */ +// @Bean +// public SimpleClientHttpRequestFactory simpleClientHttpRequestFactory(){ +// SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); +// +// requestFactory.setReadTimeout(5000); +// requestFactory.setConnectTimeout(5000); +// +// return requestFactory; +// } + + /** + * ClientHttpRequestFactory接口的另一种实现方式(推荐使用),即: + * HttpComponentsClientHttpRequestFactory:底层使用Httpclient连接池的方式创建Http连接请求 + * @return HttpComponentsClientHttpRequestFactory + */ + @Bean + public HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory(){ + //Httpclient连接池,长连接保持30秒 + PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(30, TimeUnit.SECONDS); + + //设置总连接数 + connectionManager.setMaxTotal(1000); + //设置同路由的并发数 + connectionManager.setDefaultMaxPerRoute(1000); + + //设置header + List
headers = new ArrayList
(); + headers.add(new BasicHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.3; rv:36.0) Gecko/20100101 Firefox/36.04")); + headers.add(new BasicHeader("Accept-Encoding", "gzip, deflate")); + headers.add(new BasicHeader("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3")); + headers.add(new BasicHeader("Connection", "keep-alive")); + + //创建HttpClient + HttpClient httpClient = HttpClientBuilder.create() + .setConnectionManager(connectionManager) + .setDefaultHeaders(headers) + .setRetryHandler(new DefaultHttpRequestRetryHandler(3, true)) //设置重试次数 + .setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy()) //设置保持长连接 + .build(); + + //创建HttpComponentsClientHttpRequestFactory实例 + HttpComponentsClientHttpRequestFactory requestFactory = + new HttpComponentsClientHttpRequestFactory(httpClient); + + //设置客户端和服务端建立连接的超时时间 + requestFactory.setConnectTimeout(5000); + //设置客户端从服务端读取数据的超时时间 + requestFactory.setReadTimeout(5000); + //设置从连接池获取连接的超时时间,不宜过长 + requestFactory.setConnectionRequestTimeout(200); + //缓冲请求数据,默认为true。通过POST或者PUT大量发送数据时,建议将此更改为false,以免耗尽内存 + requestFactory.setBufferRequestBody(false); + + return requestFactory; + } + +} diff --git a/ServerDemo/src/main/java/cn/zifangsky/config/WebMvcConfig.java b/ServerDemo/src/main/java/cn/zifangsky/config/WebMvcConfig.java new file mode 100644 index 0000000..0235e3e --- /dev/null +++ b/ServerDemo/src/main/java/cn/zifangsky/config/WebMvcConfig.java @@ -0,0 +1,23 @@ +package cn.zifangsky.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * Web相关配置 + * @author zifangsky + * @date 2018/7/9 + * @since 1.0.0 + */ +@Configuration +public class WebMvcConfig implements WebMvcConfigurer { + + /** + * 视图控制器 + */ + @Override + public void addViewControllers(ViewControllerRegistry registry) { + registry.addViewController("/index").setViewName("index"); + } +} diff --git a/ServerDemo/src/main/java/cn/zifangsky/controller/LicenseCreatorController.java b/ServerDemo/src/main/java/cn/zifangsky/controller/LicenseCreatorController.java new file mode 100644 index 0000000..83b38ea --- /dev/null +++ b/ServerDemo/src/main/java/cn/zifangsky/controller/LicenseCreatorController.java @@ -0,0 +1,97 @@ +package cn.zifangsky.controller; + +import cn.zifangsky.license.LicenseCreator; +import cn.zifangsky.license.LinuxServerInfos; +import cn.zifangsky.license.AbstractServerInfos; +import cn.zifangsky.license.LicenseCheckModel; +import cn.zifangsky.license.LicenseCreatorParam; +import cn.zifangsky.license.WindowsServerInfos; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.Map; + +/** + * + * 用于生成证书文件,不能放在给客户部署的代码里 + * @author zifangsky + * @date 2018/4/26 + * @since 1.0.0 + */ +@RestController +@RequestMapping("/license") +public class LicenseCreatorController { + + /** + * 证书生成路径 + */ + @Value("${license.licensePath}") + private String licensePath; + + /** + * 获取服务器硬件信息 + * @author zifangsky + * @date 2018/4/26 13:13 + * @since 1.0.0 + * @param osName 操作系统类型,如果为空则自动判断 + * @return com.ccx.models.license.LicenseCheckModel + */ + @RequestMapping(value = "/getServerInfos",produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}) + public LicenseCheckModel getServerInfos(@RequestParam(value = "osName",required = false) String osName) { + //操作系统类型 + if(StringUtils.isBlank(osName)){ + osName = System.getProperty("os.name"); + } + osName = osName.toLowerCase(); + + AbstractServerInfos abstractServerInfos = null; + + //根据不同操作系统类型选择不同的数据获取方法 + if (osName.startsWith("windows")) { + abstractServerInfos = new WindowsServerInfos(); + } else if (osName.startsWith("linux")) { + abstractServerInfos = new LinuxServerInfos(); + }else{//其他服务器类型 + abstractServerInfos = new LinuxServerInfos(); + } + + return abstractServerInfos.getServerInfos(); + } + + /** + * 生成证书 + * @author zifangsky + * @date 2018/4/26 13:13 + * @since 1.0.0 + * @param param 生成证书需要的参数,如:{"subject":"ccx-models","privateAlias":"privateKey","keyPass":"5T7Zz5Y0dJFcqTxvzkH5LDGJJSGMzQ","storePass":"3538cef8e7","licensePath":"C:/Users/zifangsky/Desktop/license.lic","privateKeysStorePath":"C:/Users/zifangsky/Desktop/privateKeys.keystore","issuedTime":"2018-04-26 14:48:12","expiryTime":"2018-12-31 00:00:00","consumerType":"User","consumerAmount":1,"description":"这是证书描述信息","licenseCheckModel":{"ipAddress":["192.168.245.1","10.0.5.22"],"macAddress":["00-50-56-C0-00-01","50-7B-9D-F9-18-41"],"cpuSerial":"BFEBFBFF000406E3","mainBoardSerial":"L1HF65E00X9"}} + * @return java.util.Map + */ + @RequestMapping(value = "/generateLicense",produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}) + public Map generateLicense(@RequestBody(required = true) LicenseCreatorParam param) { + Map resultMap = new HashMap<>(2); + + if(StringUtils.isBlank(param.getLicensePath())){ + param.setLicensePath(licensePath); + } + + LicenseCreator licenseCreator = new LicenseCreator(param); + boolean result = licenseCreator.generateLicense(); + + if(result){ + resultMap.put("result","ok"); + resultMap.put("msg",param); + }else{ + resultMap.put("result","error"); + resultMap.put("msg","证书文件生成失败!"); + } + + return resultMap; + } + +} diff --git a/ServerDemo/src/main/java/cn/zifangsky/license/AbstractServerInfos.java b/ServerDemo/src/main/java/cn/zifangsky/license/AbstractServerInfos.java new file mode 100644 index 0000000..4424a1c --- /dev/null +++ b/ServerDemo/src/main/java/cn/zifangsky/license/AbstractServerInfos.java @@ -0,0 +1,144 @@ +package cn.zifangsky.license; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; + +/** + * 用于获取客户服务器的基本信息,如:IP、Mac地址、CPU序列号、主板序列号等 + * + * @author zifangsky + * @date 2018/4/23 + * @since 1.0.0 + */ +public abstract class AbstractServerInfos { + private static Logger logger = LogManager.getLogger(AbstractServerInfos.class); + + /** + * 组装需要额外校验的License参数 + * @author zifangsky + * @date 2018/4/23 14:23 + * @since 1.0.0 + * @return demo.LicenseCheckModel + */ + public LicenseCheckModel getServerInfos(){ + LicenseCheckModel result = new LicenseCheckModel(); + + try { + result.setIpAddress(this.getIpAddress()); + result.setMacAddress(this.getMacAddress()); + result.setCpuSerial(this.getCPUSerial()); + result.setMainBoardSerial(this.getMainBoardSerial()); + }catch (Exception e){ + logger.error("获取服务器硬件信息失败",e); + } + + return result; + } + + /** + * 获取IP地址 + * @author zifangsky + * @date 2018/4/23 11:32 + * @since 1.0.0 + * @return java.util.List + */ + protected abstract List getIpAddress() throws Exception; + + /** + * 获取Mac地址 + * @author zifangsky + * @date 2018/4/23 11:32 + * @since 1.0.0 + * @return java.util.List + */ + protected abstract List getMacAddress() throws Exception; + + /** + * 获取CPU序列号 + * @author zifangsky + * @date 2018/4/23 11:35 + * @since 1.0.0 + * @return java.lang.String + */ + protected abstract String getCPUSerial() throws Exception; + + /** + * 获取主板序列号 + * @author zifangsky + * @date 2018/4/23 11:35 + * @since 1.0.0 + * @return java.lang.String + */ + protected abstract String getMainBoardSerial() throws Exception; + + /** + * 获取当前服务器所有符合条件的InetAddress + * @author zifangsky + * @date 2018/4/23 17:38 + * @since 1.0.0 + * @return java.util.List + */ + protected List getLocalAllInetAddress() throws Exception { + List result = new ArrayList<>(4); + + // 遍历所有的网络接口 + for (Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); networkInterfaces.hasMoreElements(); ) { + NetworkInterface iface = (NetworkInterface) networkInterfaces.nextElement(); + // 在所有的接口下再遍历IP + for (Enumeration inetAddresses = iface.getInetAddresses(); inetAddresses.hasMoreElements(); ) { + InetAddress inetAddr = (InetAddress) inetAddresses.nextElement(); + + //排除LoopbackAddress、SiteLocalAddress、LinkLocalAddress、MulticastAddress类型的IP地址 + if(!inetAddr.isLoopbackAddress() /*&& !inetAddr.isSiteLocalAddress()*/ + && !inetAddr.isLinkLocalAddress() && !inetAddr.isMulticastAddress()){ + result.add(inetAddr); + } + } + } + + return result; + } + + /** + * 获取某个网络接口的Mac地址 + * @author zifangsky + * @date 2018/4/23 18:08 + * @since 1.0.0 + * @param + * @return void + */ + protected String getMacByInetAddress(InetAddress inetAddr){ + try { + byte[] mac = NetworkInterface.getByInetAddress(inetAddr).getHardwareAddress(); + StringBuffer stringBuffer = new StringBuffer(); + + for(int i=0;i + * 用于将公私钥存储文件存放到其他磁盘位置而不是项目中 + * @author zifangsky + * @date 2018/4/26 18:28 + * @since 1.0.0 + * @param + * @return java.io.InputStream + */ + @Override + public InputStream getStream() throws IOException { + final InputStream in = new FileInputStream(new File(storePath)); + if (null == in){ + throw new FileNotFoundException(storePath); + } + + return in; + } +} diff --git a/ServerDemo/src/main/java/cn/zifangsky/license/CustomLicenseManager.java b/ServerDemo/src/main/java/cn/zifangsky/license/CustomLicenseManager.java new file mode 100644 index 0000000..af82889 --- /dev/null +++ b/ServerDemo/src/main/java/cn/zifangsky/license/CustomLicenseManager.java @@ -0,0 +1,291 @@ +package cn.zifangsky.license; + +import de.schlichtherle.license.LicenseContent; +import de.schlichtherle.license.LicenseContentException; +import de.schlichtherle.license.LicenseManager; +import de.schlichtherle.license.LicenseNotary; +import de.schlichtherle.license.LicenseParam; +import de.schlichtherle.license.NoLicenseInstalledException; +import de.schlichtherle.xml.GenericCertificate; +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.beans.XMLDecoder; +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.UnsupportedEncodingException; +import java.util.Date; +import java.util.List; + +/** + * 自定义LicenseManager,用于增加额外的服务器硬件信息校验 + * + * @author zifangsky + * @date 2018/4/23 + * @since 1.0.0 + */ +public class CustomLicenseManager extends LicenseManager{ + private static Logger logger = LogManager.getLogger(CustomLicenseManager.class); + + //XML编码 + private static final String XML_CHARSET = "UTF-8"; + //默认BUFSIZE + private static final int DEFAULT_BUFSIZE = 8 * 1024; + + public CustomLicenseManager() { + + } + + public CustomLicenseManager(LicenseParam param) { + super(param); + } + + /** + * 复写create方法 + * @author zifangsky + * @date 2018/4/23 10:36 + * @since 1.0.0 + * @param + * @return byte[] + */ + @Override + protected synchronized byte[] create( + LicenseContent content, + LicenseNotary notary) + throws Exception { + initialize(content); + this.validateCreate(content); + final GenericCertificate certificate = notary.sign(content); + return getPrivacyGuard().cert2key(certificate); + } + + /** + * 复写install方法,其中validate方法调用本类中的validate方法,校验IP地址、Mac地址等其他信息 + * @author zifangsky + * @date 2018/4/23 10:40 + * @since 1.0.0 + * @param + * @return de.schlichtherle.license.LicenseContent + */ + @Override + protected synchronized LicenseContent install( + final byte[] key, + final LicenseNotary notary) + throws Exception { + final GenericCertificate certificate = getPrivacyGuard().key2cert(key); + + notary.verify(certificate); + final LicenseContent content = (LicenseContent)this.load(certificate.getEncoded()); + this.validate(content); + setLicenseKey(key); + setCertificate(certificate); + + return content; + } + + /** + * 复写verify方法,调用本类中的validate方法,校验IP地址、Mac地址等其他信息 + * @author zifangsky + * @date 2018/4/23 10:40 + * @since 1.0.0 + * @param + * @return de.schlichtherle.license.LicenseContent + */ + @Override + protected synchronized LicenseContent verify(final LicenseNotary notary) + throws Exception { + GenericCertificate certificate = getCertificate(); + + // Load license key from preferences, + final byte[] key = getLicenseKey(); + if (null == key){ + throw new NoLicenseInstalledException(getLicenseParam().getSubject()); + } + + certificate = getPrivacyGuard().key2cert(key); + notary.verify(certificate); + final LicenseContent content = (LicenseContent)this.load(certificate.getEncoded()); + this.validate(content); + setCertificate(certificate); + + return content; + } + + /** + * 校验生成证书的参数信息 + * @author zifangsky + * @date 2018/5/2 15:43 + * @since 1.0.0 + * @param content 证书正文 + */ + protected synchronized void validateCreate(final LicenseContent content) + throws LicenseContentException { + final LicenseParam param = getLicenseParam(); + + final Date now = new Date(); + final Date notBefore = content.getNotBefore(); + final Date notAfter = content.getNotAfter(); + if (null != notAfter && now.after(notAfter)){ + throw new LicenseContentException("证书失效时间不能早于当前时间"); + } + if (null != notBefore && null != notAfter && notAfter.before(notBefore)){ + throw new LicenseContentException("证书生效时间不能晚于证书失效时间"); + } + final String consumerType = content.getConsumerType(); + if (null == consumerType){ + throw new LicenseContentException("用户类型不能为空"); + } + } + + + /** + * 复写validate方法,增加IP地址、Mac地址等其他信息校验 + * @author zifangsky + * @date 2018/4/23 10:40 + * @since 1.0.0 + * @param content LicenseContent + */ + @Override + protected synchronized void validate(final LicenseContent content) + throws LicenseContentException { + //1. 首先调用父类的validate方法 + super.validate(content); + + //2. 然后校验自定义的License参数 + //License中可被允许的参数信息 + LicenseCheckModel expectedCheckModel = (LicenseCheckModel) content.getExtra(); + //当前服务器真实的参数信息 + LicenseCheckModel serverCheckModel = getServerInfos(); + + if(expectedCheckModel != null && serverCheckModel != null){ + //校验IP地址 + if(!checkIpAddress(expectedCheckModel.getIpAddress(),serverCheckModel.getIpAddress())){ + throw new LicenseContentException("当前服务器的IP没在授权范围内"); + } + + //校验Mac地址 + if(!checkIpAddress(expectedCheckModel.getMacAddress(),serverCheckModel.getMacAddress())){ + throw new LicenseContentException("当前服务器的Mac地址没在授权范围内"); + } + + //校验主板序列号 + if(!checkSerial(expectedCheckModel.getMainBoardSerial(),serverCheckModel.getMainBoardSerial())){ + throw new LicenseContentException("当前服务器的主板序列号没在授权范围内"); + } + + //校验CPU序列号 + if(!checkSerial(expectedCheckModel.getCpuSerial(),serverCheckModel.getCpuSerial())){ + throw new LicenseContentException("当前服务器的CPU序列号没在授权范围内"); + } + }else{ + throw new LicenseContentException("不能获取服务器硬件信息"); + } + } + + + /** + * 重写XMLDecoder解析XML + * @author zifangsky + * @date 2018/4/25 14:02 + * @since 1.0.0 + * @param encoded XML类型字符串 + * @return java.lang.Object + */ + private Object load(String encoded){ + BufferedInputStream inputStream = null; + XMLDecoder decoder = null; + try { + inputStream = new BufferedInputStream(new ByteArrayInputStream(encoded.getBytes(XML_CHARSET))); + + decoder = new XMLDecoder(new BufferedInputStream(inputStream, DEFAULT_BUFSIZE),null,null); + + return decoder.readObject(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } finally { + try { + if(decoder != null){ + decoder.close(); + } + if(inputStream != null){ + inputStream.close(); + } + } catch (Exception e) { + logger.error("XMLDecoder解析XML失败",e); + } + } + + return null; + } + + /** + * 获取当前服务器需要额外校验的License参数 + * @author zifangsky + * @date 2018/4/23 14:33 + * @since 1.0.0 + * @return demo.LicenseCheckModel + */ + private LicenseCheckModel getServerInfos(){ + //操作系统类型 + String osName = System.getProperty("os.name").toLowerCase(); + AbstractServerInfos abstractServerInfos = null; + + //根据不同操作系统类型选择不同的数据获取方法 + if (osName.startsWith("windows")) { + abstractServerInfos = new WindowsServerInfos(); + } else if (osName.startsWith("linux")) { + abstractServerInfos = new LinuxServerInfos(); + }else{//其他服务器类型 + abstractServerInfos = new LinuxServerInfos(); + } + + return abstractServerInfos.getServerInfos(); + } + + /** + * 校验当前服务器的IP/Mac地址是否在可被允许的IP范围内
+ * 如果存在IP在可被允许的IP/Mac地址范围内,则返回true + * @author zifangsky + * @date 2018/4/24 11:44 + * @since 1.0.0 + * @return boolean + */ + private boolean checkIpAddress(List expectedList,List serverList){ + if(expectedList != null && expectedList.size() > 0){ + if(serverList != null && serverList.size() > 0){ + for(String expected : expectedList){ + if(serverList.contains(expected.trim())){ + return true; + } + } + } + + return false; + }else { + return true; + } + } + + /** + * 校验当前服务器硬件(主板、CPU等)序列号是否在可允许范围内 + * @author zifangsky + * @date 2018/4/24 14:38 + * @since 1.0.0 + * @return boolean + */ + private boolean checkSerial(String expectedSerial,String serverSerial){ + if(StringUtils.isNotBlank(expectedSerial)){ + if(StringUtils.isNotBlank(serverSerial)){ + if(expectedSerial.equals(serverSerial)){ + return true; + } + } + + return false; + }else{ + return true; + } + } + +} diff --git a/ServerDemo/src/main/java/cn/zifangsky/license/LicenseCheckModel.java b/ServerDemo/src/main/java/cn/zifangsky/license/LicenseCheckModel.java new file mode 100644 index 0000000..7ce412c --- /dev/null +++ b/ServerDemo/src/main/java/cn/zifangsky/license/LicenseCheckModel.java @@ -0,0 +1,77 @@ +package cn.zifangsky.license; + +import java.io.Serializable; +import java.util.List; + +/** + * 自定义需要校验的License参数 + * + * @author zifangsky + * @date 2018/4/23 + * @since 1.0.0 + */ +public class LicenseCheckModel implements Serializable{ + + private static final long serialVersionUID = 8600137500316662317L; + /** + * 可被允许的IP地址 + */ + private List ipAddress; + + /** + * 可被允许的MAC地址 + */ + private List macAddress; + + /** + * 可被允许的CPU序列号 + */ + private String cpuSerial; + + /** + * 可被允许的主板序列号 + */ + private String mainBoardSerial; + + public List getIpAddress() { + return ipAddress; + } + + public void setIpAddress(List ipAddress) { + this.ipAddress = ipAddress; + } + + public List getMacAddress() { + return macAddress; + } + + public void setMacAddress(List macAddress) { + this.macAddress = macAddress; + } + + public String getCpuSerial() { + return cpuSerial; + } + + public void setCpuSerial(String cpuSerial) { + this.cpuSerial = cpuSerial; + } + + public String getMainBoardSerial() { + return mainBoardSerial; + } + + public void setMainBoardSerial(String mainBoardSerial) { + this.mainBoardSerial = mainBoardSerial; + } + + @Override + public String toString() { + return "LicenseCheckModel{" + + "ipAddress=" + ipAddress + + ", macAddress=" + macAddress + + ", cpuSerial='" + cpuSerial + '\'' + + ", mainBoardSerial='" + mainBoardSerial + '\'' + + '}'; + } +} diff --git a/ServerDemo/src/main/java/cn/zifangsky/license/LicenseCreator.java b/ServerDemo/src/main/java/cn/zifangsky/license/LicenseCreator.java new file mode 100644 index 0000000..91e8d9b --- /dev/null +++ b/ServerDemo/src/main/java/cn/zifangsky/license/LicenseCreator.java @@ -0,0 +1,108 @@ +package cn.zifangsky.license; + +import de.schlichtherle.license.CipherParam; +import de.schlichtherle.license.DefaultCipherParam; +import de.schlichtherle.license.DefaultLicenseParam; +import de.schlichtherle.license.KeyStoreParam; +import de.schlichtherle.license.LicenseContent; +import de.schlichtherle.license.LicenseManager; +import de.schlichtherle.license.LicenseParam; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.security.auth.x500.X500Principal; +import java.io.File; +import java.text.MessageFormat; +import java.util.prefs.Preferences; + +/** + * License生成类 + * + * @author zifangsky + * @date 2018/4/19 + * @since 1.0.0 + */ +public class LicenseCreator { + private static Logger logger = LogManager.getLogger(LicenseCreator.class); + private final static X500Principal DEFAULT_HOLDER_AND_ISSUER = new X500Principal("CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN"); + private LicenseCreatorParam param; + + public LicenseCreator(LicenseCreatorParam param) { + this.param = param; + } + + /** + * 生成License证书 + * @author zifangsky + * @date 2018/4/20 10:58 + * @since 1.0.0 + * @return boolean + */ + public boolean generateLicense(){ + try { + LicenseManager licenseManager = new CustomLicenseManager(initLicenseParam()); + LicenseContent licenseContent = initLicenseContent(); + + licenseManager.store(licenseContent,new File(param.getLicensePath())); + + return true; + }catch (Exception e){ + logger.error(MessageFormat.format("证书生成失败:{0}",param),e); + return false; + } + } + + /** + * 初始化证书生成参数 + * @author zifangsky + * @date 2018/4/20 10:56 + * @since 1.0.0 + * @return de.schlichtherle.license.LicenseParam + */ + private LicenseParam initLicenseParam(){ + Preferences preferences = Preferences.userNodeForPackage(LicenseCreator.class); + + //设置对证书内容加密的秘钥 + CipherParam cipherParam = new DefaultCipherParam(param.getStorePass()); + + KeyStoreParam privateStoreParam = new CustomKeyStoreParam(LicenseCreator.class + ,param.getPrivateKeysStorePath() + ,param.getPrivateAlias() + ,param.getStorePass() + ,param.getKeyPass()); + + LicenseParam licenseParam = new DefaultLicenseParam(param.getSubject() + ,preferences + ,privateStoreParam + ,cipherParam); + + return licenseParam; + } + + /** + * 设置证书生成正文信息 + * @author zifangsky + * @date 2018/4/20 10:57 + * @since 1.0.0 + * @return de.schlichtherle.license.LicenseContent + */ + private LicenseContent initLicenseContent(){ + LicenseContent licenseContent = new LicenseContent(); + licenseContent.setHolder(DEFAULT_HOLDER_AND_ISSUER); + licenseContent.setIssuer(DEFAULT_HOLDER_AND_ISSUER); + + licenseContent.setSubject(param.getSubject()); + licenseContent.setIssued(param.getIssuedTime()); + licenseContent.setNotBefore(param.getIssuedTime()); + licenseContent.setNotAfter(param.getExpiryTime()); + licenseContent.setConsumerType(param.getConsumerType()); + licenseContent.setConsumerAmount(param.getConsumerAmount()); + licenseContent.setInfo(param.getDescription()); + + //扩展校验服务器硬件信息 + licenseContent.setExtra(param.getLicenseCheckModel()); + + return licenseContent; + } + +} diff --git a/ServerDemo/src/main/java/cn/zifangsky/license/LicenseCreatorParam.java b/ServerDemo/src/main/java/cn/zifangsky/license/LicenseCreatorParam.java new file mode 100644 index 0000000..6519091 --- /dev/null +++ b/ServerDemo/src/main/java/cn/zifangsky/license/LicenseCreatorParam.java @@ -0,0 +1,193 @@ +package cn.zifangsky.license; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.io.Serializable; +import java.util.Date; + +/** + * License生成类需要的参数 + * + * @author zifangsky + * @date 2018/4/19 + * @since 1.0.0 + */ +public class LicenseCreatorParam implements Serializable { + + private static final long serialVersionUID = -7793154252684580872L; + /** + * 证书subject + */ + private String subject; + + /** + * 密钥别称 + */ + private String privateAlias; + + /** + * 密钥密码(需要妥善保管,不能让使用者知道) + */ + private String keyPass; + + /** + * 访问秘钥库的密码 + */ + private String storePass; + + /** + * 证书生成路径 + */ + private String licensePath; + + /** + * 密钥库存储路径 + */ + private String privateKeysStorePath; + + /** + * 证书生效时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date issuedTime = new Date(); + + /** + * 证书失效时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date expiryTime; + + /** + * 用户类型 + */ + private String consumerType = "user"; + + /** + * 用户数量 + */ + private Integer consumerAmount = 1; + + /** + * 描述信息 + */ + private String description = ""; + + /** + * 额外的服务器硬件校验信息 + */ + private LicenseCheckModel licenseCheckModel; + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public String getPrivateAlias() { + return privateAlias; + } + + public void setPrivateAlias(String privateAlias) { + this.privateAlias = privateAlias; + } + + public String getKeyPass() { + return keyPass; + } + + public void setKeyPass(String keyPass) { + this.keyPass = keyPass; + } + + public String getStorePass() { + return storePass; + } + + public void setStorePass(String storePass) { + this.storePass = storePass; + } + + public String getLicensePath() { + return licensePath; + } + + public void setLicensePath(String licensePath) { + this.licensePath = licensePath; + } + + public String getPrivateKeysStorePath() { + return privateKeysStorePath; + } + + public void setPrivateKeysStorePath(String privateKeysStorePath) { + this.privateKeysStorePath = privateKeysStorePath; + } + + public Date getIssuedTime() { + return issuedTime; + } + + public void setIssuedTime(Date issuedTime) { + this.issuedTime = issuedTime; + } + + public Date getExpiryTime() { + return expiryTime; + } + + public void setExpiryTime(Date expiryTime) { + this.expiryTime = expiryTime; + } + + public String getConsumerType() { + return consumerType; + } + + public void setConsumerType(String consumerType) { + this.consumerType = consumerType; + } + + public Integer getConsumerAmount() { + return consumerAmount; + } + + public void setConsumerAmount(Integer consumerAmount) { + this.consumerAmount = consumerAmount; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public LicenseCheckModel getLicenseCheckModel() { + return licenseCheckModel; + } + + public void setLicenseCheckModel(LicenseCheckModel licenseCheckModel) { + this.licenseCheckModel = licenseCheckModel; + } + + @Override + public String toString() { + return "LicenseCreatorParam{" + + "subject='" + subject + '\'' + + ", privateAlias='" + privateAlias + '\'' + + ", keyPass='" + keyPass + '\'' + + ", storePass='" + storePass + '\'' + + ", licensePath='" + licensePath + '\'' + + ", privateKeysStorePath='" + privateKeysStorePath + '\'' + + ", issuedTime=" + issuedTime + + ", expiryTime=" + expiryTime + + ", consumerType='" + consumerType + '\'' + + ", consumerAmount=" + consumerAmount + + ", description='" + description + '\'' + + ", licenseCheckModel=" + licenseCheckModel + + '}'; + } +} diff --git a/ServerDemo/src/main/java/cn/zifangsky/license/LinuxServerInfos.java b/ServerDemo/src/main/java/cn/zifangsky/license/LinuxServerInfos.java new file mode 100644 index 0000000..42ac30b --- /dev/null +++ b/ServerDemo/src/main/java/cn/zifangsky/license/LinuxServerInfos.java @@ -0,0 +1,90 @@ +package cn.zifangsky.license; + +import org.apache.commons.lang3.StringUtils; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.InetAddress; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 用于获取客户Linux服务器的基本信息 + * + * @author zifangsky + * @date 2018/4/23 + * @since 1.0.0 + */ +public class LinuxServerInfos extends AbstractServerInfos { + + @Override + protected List getIpAddress() throws Exception { + List result = null; + + //获取所有网络接口 + List inetAddresses = getLocalAllInetAddress(); + + if(inetAddresses != null && inetAddresses.size() > 0){ + result = inetAddresses.stream().map(InetAddress::getHostAddress).distinct().map(String::toLowerCase).collect(Collectors.toList()); + } + + return result; + } + + @Override + protected List getMacAddress() throws Exception { + List result = null; + + //1. 获取所有网络接口 + List inetAddresses = getLocalAllInetAddress(); + + if(inetAddresses != null && inetAddresses.size() > 0){ + //2. 获取所有网络接口的Mac地址 + result = inetAddresses.stream().map(this::getMacByInetAddress).distinct().collect(Collectors.toList()); + } + + return result; + } + + @Override + protected String getCPUSerial() throws Exception { + //序列号 + String serialNumber = ""; + + //使用dmidecode命令获取CPU序列号 + String[] shell = {"/bin/bash","-c","dmidecode -t processor | grep 'ID' | awk -F ':' '{print $2}' | head -n 1"}; + Process process = Runtime.getRuntime().exec(shell); + process.getOutputStream().close(); + + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + + String line = reader.readLine().trim(); + if(StringUtils.isNotBlank(line)){ + serialNumber = line; + } + + reader.close(); + return serialNumber; + } + + @Override + protected String getMainBoardSerial() throws Exception { + //序列号 + String serialNumber = ""; + + //使用dmidecode命令获取主板序列号 + String[] shell = {"/bin/bash","-c","dmidecode | grep 'Serial Number' | awk -F ':' '{print $2}' | head -n 1"}; + Process process = Runtime.getRuntime().exec(shell); + process.getOutputStream().close(); + + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + + String line = reader.readLine().trim(); + if(StringUtils.isNotBlank(line)){ + serialNumber = line; + } + + reader.close(); + return serialNumber; + } +} diff --git a/ServerDemo/src/main/java/cn/zifangsky/license/WindowsServerInfos.java b/ServerDemo/src/main/java/cn/zifangsky/license/WindowsServerInfos.java new file mode 100644 index 0000000..bec7359 --- /dev/null +++ b/ServerDemo/src/main/java/cn/zifangsky/license/WindowsServerInfos.java @@ -0,0 +1,89 @@ +package cn.zifangsky.license; + +import java.net.InetAddress; +import java.util.List; +import java.util.Scanner; +import java.util.stream.Collectors; + +/** + * 用于获取客户Windows服务器的基本信息 + * + * @author zifangsky + * @date 2018/4/23 + * @since 1.0.0 + */ +public class WindowsServerInfos extends AbstractServerInfos { + + @Override + protected List getIpAddress() throws Exception { + List result = null; + + //获取所有网络接口 + List inetAddresses = getLocalAllInetAddress(); + + if(inetAddresses != null && inetAddresses.size() > 0){ + result = inetAddresses.stream().map(InetAddress::getHostAddress).distinct().map(String::toLowerCase).collect(Collectors.toList()); + } + + return result; + } + + @Override + protected List getMacAddress() throws Exception { + List result = null; + + //1. 获取所有网络接口 + List inetAddresses = getLocalAllInetAddress(); + + if(inetAddresses != null && inetAddresses.size() > 0){ + //2. 获取所有网络接口的Mac地址 + result = inetAddresses.stream().map(this::getMacByInetAddress).distinct().collect(Collectors.toList()); + } + + return result; + } + + @Override + protected String getCPUSerial() throws Exception { + //序列号 + String serialNumber = ""; + + //使用WMIC获取CPU序列号 + Process process = Runtime.getRuntime().exec("wmic cpu get processorid"); + process.getOutputStream().close(); + Scanner scanner = new Scanner(process.getInputStream()); + + if(scanner.hasNext()){ + scanner.next(); + } + + if(scanner.hasNext()){ + serialNumber = scanner.next().trim(); + } + + scanner.close(); + return serialNumber; + } + + @Override + protected String getMainBoardSerial() throws Exception { + //序列号 + String serialNumber = ""; + + //使用WMIC获取主板序列号 + Process process = Runtime.getRuntime().exec("wmic baseboard get serialnumber"); + process.getOutputStream().close(); + Scanner scanner = new Scanner(process.getInputStream()); + + if(scanner.hasNext()){ + scanner.next(); + } + + if(scanner.hasNext()){ + serialNumber = scanner.next().trim(); + } + + scanner.close(); + return serialNumber; + } +} diff --git a/ServerDemo/src/main/resources/application-dev.properties b/ServerDemo/src/main/resources/application-dev.properties new file mode 100644 index 0000000..a99b60a --- /dev/null +++ b/ServerDemo/src/main/resources/application-dev.properties @@ -0,0 +1,18 @@ +server.port=7000 + +#session超时时间 +server.servlet.session.timeout=300 + +#是否启用debug模式 +#debug=true + +logging.level.org.springframework.web=DEBUG +#logging.file=D:/runtime/test.log + +#thymeleaf start +spring.thymeleaf.mode=LEGACYHTML5 +spring.thymeleaf.prefix=classpath:/templates/ +spring.thymeleaf.suffix=.html +spring.thymeleaf.template-resolver-order=0 +spring.thymeleaf.cache=false +#thymeleaf end \ No newline at end of file diff --git a/ServerDemo/src/main/resources/application-prod.properties b/ServerDemo/src/main/resources/application-prod.properties new file mode 100644 index 0000000..205ed95 --- /dev/null +++ b/ServerDemo/src/main/resources/application-prod.properties @@ -0,0 +1 @@ +server.port=8000 \ No newline at end of file diff --git a/ServerDemo/src/main/resources/application.properties b/ServerDemo/src/main/resources/application.properties new file mode 100644 index 0000000..66d6d0c --- /dev/null +++ b/ServerDemo/src/main/resources/application.properties @@ -0,0 +1,2 @@ +#使用哪个环境的配置 +spring.profiles.active=dev diff --git a/ServerDemo/src/main/resources/banner.txt b/ServerDemo/src/main/resources/banner.txt new file mode 100644 index 0000000..f7febe4 --- /dev/null +++ b/ServerDemo/src/main/resources/banner.txt @@ -0,0 +1,22 @@ +${AnsiColor.BLUE} + _ooOoo_ + o8888888o + 88" . "88 + (| ^_^ |) + O\ = /O + ____/`---'\____ + .' \\| | `. + / \\||| : ||| \ + / _||||| -:- |||||- \ + | | \\\ - / | | + | \_| ''\---/'' | | + \ .-\__ `-` ___/-. / + ___`. .' /--.--\ `. . ___ + ."" '< `.___\_<|>_/___.' >'"". + | | : `- \`.;`\ _ /`;.`/ - ` : | | + \ \ `-. \_ __\ /__ _/ .-` / / + ========`-.____`-.___\_____/___.-`____.-'======== + `=---=' + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 佛祖保佑 永无BUG + \ No newline at end of file diff --git a/ServerDemo/src/main/resources/license-config.properties b/ServerDemo/src/main/resources/license-config.properties new file mode 100644 index 0000000..2ab483d --- /dev/null +++ b/ServerDemo/src/main/resources/license-config.properties @@ -0,0 +1,2 @@ +#License相关配置 +license.licensePath=C:/Users/zifangsky/Desktop/license_demo/license.lic diff --git a/ServerDemo/src/main/resources/static/favicon.ico b/ServerDemo/src/main/resources/static/favicon.ico new file mode 100644 index 0000000..0ad0e67 Binary files /dev/null and b/ServerDemo/src/main/resources/static/favicon.ico differ diff --git a/ServerDemo/src/main/resources/static/img/1.jpg b/ServerDemo/src/main/resources/static/img/1.jpg new file mode 100644 index 0000000..23fd153 Binary files /dev/null and b/ServerDemo/src/main/resources/static/img/1.jpg differ diff --git a/ServerDemo/src/main/resources/static/img/2.jpg b/ServerDemo/src/main/resources/static/img/2.jpg new file mode 100644 index 0000000..b794cfe Binary files /dev/null and b/ServerDemo/src/main/resources/static/img/2.jpg differ diff --git a/ServerDemo/src/main/resources/static/img/3.jpg b/ServerDemo/src/main/resources/static/img/3.jpg new file mode 100644 index 0000000..b88bbbb Binary files /dev/null and b/ServerDemo/src/main/resources/static/img/3.jpg differ diff --git a/ServerDemo/src/main/resources/static/img/4.jpg b/ServerDemo/src/main/resources/static/img/4.jpg new file mode 100644 index 0000000..8bea6bd Binary files /dev/null and b/ServerDemo/src/main/resources/static/img/4.jpg differ diff --git a/ServerDemo/src/main/resources/static/img/5.jpg b/ServerDemo/src/main/resources/static/img/5.jpg new file mode 100644 index 0000000..eff17a2 Binary files /dev/null and b/ServerDemo/src/main/resources/static/img/5.jpg differ diff --git a/ServerDemo/src/main/resources/static/img/6.jpg b/ServerDemo/src/main/resources/static/img/6.jpg new file mode 100644 index 0000000..0422284 Binary files /dev/null and b/ServerDemo/src/main/resources/static/img/6.jpg differ diff --git a/ServerDemo/src/main/resources/static/img/7.jpg b/ServerDemo/src/main/resources/static/img/7.jpg new file mode 100644 index 0000000..f26e836 Binary files /dev/null and b/ServerDemo/src/main/resources/static/img/7.jpg differ diff --git a/ServerDemo/src/main/resources/templates/index.html b/ServerDemo/src/main/resources/templates/index.html new file mode 100644 index 0000000..2f1c8fa --- /dev/null +++ b/ServerDemo/src/main/resources/templates/index.html @@ -0,0 +1,46 @@ + + + + + + 首页 + + + + + + + + + \ No newline at end of file diff --git a/ServerDemo/src/test/java/cn/zifangsky/ServerDemoApplicationTests.java b/ServerDemo/src/test/java/cn/zifangsky/ServerDemoApplicationTests.java new file mode 100644 index 0000000..104d429 --- /dev/null +++ b/ServerDemo/src/test/java/cn/zifangsky/ServerDemoApplicationTests.java @@ -0,0 +1,16 @@ +package cn.zifangsky; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class ServerDemoApplicationTests { + + @Test + public void contextLoads() { + } + +}