├── .gitignore
├── .mvn
└── wrapper
│ ├── maven-wrapper.jar
│ └── maven-wrapper.properties
├── README.MD
├── application.yml.sample
├── docker-compose.yml
├── dockerFiles
├── java
│ └── Dockerfile
└── mysql
│ ├── Dockerfile
│ ├── charset.cnf
│ └── database.sql
├── mvnw
├── mvnw.cmd
├── mysql.env
├── pom.xml
└── src
├── main
├── java
│ └── com
│ │ └── hpm
│ │ └── blog
│ │ ├── SpringBootBlogApplication.java
│ │ ├── annotation
│ │ ├── CurrentUser.java
│ │ └── LoginRequired.java
│ │ ├── api
│ │ ├── AuthenticationApi.java
│ │ ├── PostApi.java
│ │ └── UserApi.java
│ │ ├── config
│ │ ├── AuthenticationInterceptor.java
│ │ ├── CurrentUserMethodArgumentResolver.java
│ │ ├── FastJsonHttpMessageConverterEx.java
│ │ ├── GlobalExceptionHandler.java
│ │ └── WebMvcConfigurer.java
│ │ ├── mapper
│ │ ├── PostMapper.java
│ │ └── UserMapper.java
│ │ ├── model
│ │ ├── Post.java
│ │ └── User.java
│ │ └── service
│ │ ├── AuthenticationService.java
│ │ ├── PostService.java
│ │ └── UserService.java
└── resources
│ ├── application.yml
│ ├── com
│ └── hpm
│ │ └── blog
│ │ └── mapper
│ │ ├── PostMapper.xml
│ │ └── UserMapper.xml
│ ├── logback-spring.xml
│ ├── mybatis.xml
│ └── static
│ ├── css
│ ├── bulma.css
│ ├── common.css
│ └── font-awesome.min.css
│ ├── fonts
│ ├── FontAwesome.otf
│ ├── fontawesome-webfont.eot
│ ├── fontawesome-webfont.svg
│ ├── fontawesome-webfont.ttf
│ ├── fontawesome-webfont.woff
│ └── fontawesome-webfont.woff2
│ ├── index.html
│ └── js
│ ├── axios.min.js
│ ├── index.js
│ ├── marked.min.js
│ └── vue.js
└── test
└── java
└── com
└── hpm
└── blog
└── SpringBootBlogApplicationTests.java
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 | log/
4 | # 根目录配置文件
5 | /application.yml
6 |
7 | ### docker ###
8 | # mysql 最终配置文件
9 | mysql.env.local
10 | /maven
11 | /mysql
12 |
13 | ### IntelliJ IDEA ###
14 | .idea
15 | *.iml
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyrijk/spring-boot-blog/34160e7cd92c7171d7fe3da2748e8933e47222f4/.mvn/wrapper/maven-wrapper.jar
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip
2 |
--------------------------------------------------------------------------------
/README.MD:
--------------------------------------------------------------------------------
1 | Spring Boot step by step
2 |
3 | 1. [使用 idea 新建 Spring Boot 项目](http://www.jianshu.com/p/45ba074dbc81)
4 | 2. [使用 Spring Maven 仓库加速 Maven jar 包下载](http://www.jianshu.com/p/87100dd1ec52)
5 | 3. [实现用户注册接口](http://www.jianshu.com/p/07184349738a)
6 | 4. [实现用户登录接口](http://www.jianshu.com/p/d99e4de60e5d)
7 | 5. [配置 logback 和全局异常处理](http://www.jianshu.com/p/e0b6f29f9676)
8 | 6. [使用 spring 拦截器和自定义注解进行登录拦截](http://www.jianshu.com/p/97362fdf039e)
9 | 7. [通过自定义 @CurrentUser 获取当前登录用户](http://www.jianshu.com/p/01a6a61d9e02)
10 | 8. [实现文章添加和查询接口](http://www.jianshu.com/p/e54077b0bf37)
11 | 9. [使用 fastjson 解析数据](http://www.jianshu.com/p/45682cd30ca2)
12 |
13 | # 部署说明
14 |
15 | ---
16 |
17 | ## docker
18 | 默认 18080 端口
19 |
20 | 1. [安装 docker](https://get.daocloud.io/#install-docker)
21 | 2. [安装 docker-compose](https://get.daocloud.io/#install-compose)
22 | 3. 修改 mysql 用户名和密码
23 | ```
24 | $ cp application.yml.sample application.yml
25 | $ vi application.yml
26 | ```
27 |
28 | ```
29 | $ cp mysql.env mysql.env.local
30 | $ vi mysql.env.local
31 | ```
32 | 4. package
33 | ```
34 | $ docker-compose up maven
35 | ```
36 | 5. run
37 | - 直接运行
38 | ```
39 | $ docker-compose up app
40 | ```
41 | - 后台运行
42 | ```
43 | $ docker-compose start app
44 | ```
45 |
46 | ---
47 |
48 | ## 非docker
49 | 默认 8080 端口
50 |
51 | 1. package
52 | ```
53 | $ mvn clean package
54 | ```
55 | 2. run
56 | ```
57 | $ nohub java -jar target/spring-boot-blog-0.0.1-SNAPSHOT.jar &
58 | ```
59 |
--------------------------------------------------------------------------------
/application.yml.sample:
--------------------------------------------------------------------------------
1 | spring:
2 | datasource:
3 | url: jdbc:mysql://mysql:3306/spring-blog?useUnicode=true&characterEncoding=utf8&autoReconnect=true
4 | username: username
5 | password: password
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | maven:
2 | image: maven:3.3-jdk-8
3 | command: mvn clean package -Dmaven.test.skip=true
4 | volumes:
5 | - .:/code
6 | - ./maven:/root/.m2
7 | working_dir: /code
8 |
9 | mysql:
10 | build: ./dockerFiles/mysql
11 | volumes:
12 | - ./mysql:/var/lib/mysql
13 | working_dir: /var/lib/mysql
14 | ports:
15 | - "13306:3306"
16 | env_file:
17 | - ./mysql.env
18 | - ./mysql.env.local
19 |
20 | app:
21 | build: ./dockerFiles/java
22 | command: java -jar target/spring-boot-blog-0.0.1-SNAPSHOT.jar
23 | volumes:
24 | - .:/code
25 | working_dir: /code
26 | ports:
27 | - "18080:8080"
28 | links:
29 | - mysql:mysql
--------------------------------------------------------------------------------
/dockerFiles/java/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM java:8-jdk
2 | RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
3 | RUN echo "Asia/Shanghai" > /etc/timezone
4 | RUN dpkg-reconfigure -f noninteractive tzdata
--------------------------------------------------------------------------------
/dockerFiles/mysql/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mysql:5.6
2 | ADD charset.cnf /etc/mysql/conf.d/charset.cnf
3 |
4 | ADD database.sql /docker-entrypoint-initdb.d
5 |
6 | RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
7 | RUN echo "Asia/Shanghai" > /etc/timezone
8 | RUN dpkg-reconfigure -f noninteractive tzdata
--------------------------------------------------------------------------------
/dockerFiles/mysql/charset.cnf:
--------------------------------------------------------------------------------
1 | # https://segmentfault.com/a/1190000000616820 emoji存储
2 | [client]
3 | default-character-set=utf8mb4
4 |
5 | [mysql]
6 | default-character-set=utf8mb4
7 |
8 | [mysqld]
9 | skip-character-set-client-handshake
10 | character-set-server=utf8mb4
11 | collation-server=utf8mb4_general_ci
12 | init-connect=SET NAMES utf8mb4
13 |
--------------------------------------------------------------------------------
/dockerFiles/mysql/database.sql:
--------------------------------------------------------------------------------
1 | /*
2 | Navicat Premium Data Transfer
3 |
4 | Source Server : localhost-root
5 | Source Server Type : MySQL
6 | Source Server Version : 50716
7 | Source Host : localhost
8 | Source Database : spring-blog
9 |
10 | Target Server Type : MySQL
11 | Target Server Version : 50716
12 | File Encoding : utf-8
13 |
14 | Date: 02/07/2017 19:55:25 PM
15 | */
16 |
17 | SET NAMES utf8mb4;
18 | SET FOREIGN_KEY_CHECKS = 0;
19 |
20 | -- ----------------------------
21 | -- Table structure for `post`
22 | -- ----------------------------
23 | DROP TABLE IF EXISTS `post`;
24 | CREATE TABLE `post` (
25 | `id` int(11) NOT NULL AUTO_INCREMENT,
26 | `author_id` int(11) NOT NULL,
27 | `title` varchar(100) NOT NULL,
28 | `content` text NOT NULL,
29 | `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
30 | `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
31 | PRIMARY KEY (`id`)
32 | ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
33 |
34 | -- ----------------------------
35 | -- Table structure for `user`
36 | -- ----------------------------
37 | DROP TABLE IF EXISTS `user`;
38 | CREATE TABLE `user` (
39 | `id` int(11) NOT NULL AUTO_INCREMENT,
40 | `name` varchar(20) NOT NULL,
41 | `password` varchar(255) NOT NULL,
42 | PRIMARY KEY (`id`),
43 | UNIQUE KEY `user_name_uindex` (`name`)
44 | ) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4;
45 |
46 | SET FOREIGN_KEY_CHECKS = 1;
47 |
--------------------------------------------------------------------------------
/mvnw:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # ----------------------------------------------------------------------------
3 | # Licensed to the Apache Software Foundation (ASF) under one
4 | # or more contributor license agreements. See the NOTICE file
5 | # distributed with this work for additional information
6 | # regarding copyright ownership. The ASF licenses this file
7 | # to you under the Apache License, Version 2.0 (the
8 | # "License"); you may not use this file except in compliance
9 | # with the License. You may obtain a copy of the License at
10 | #
11 | # http://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing,
14 | # software distributed under the License is distributed on an
15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | # KIND, either express or implied. See the License for the
17 | # specific language governing permissions and limitations
18 | # under the License.
19 | # ----------------------------------------------------------------------------
20 |
21 | # ----------------------------------------------------------------------------
22 | # Maven2 Start Up Batch script
23 | #
24 | # Required ENV vars:
25 | # ------------------
26 | # JAVA_HOME - location of a JDK home dir
27 | #
28 | # Optional ENV vars
29 | # -----------------
30 | # M2_HOME - location of maven2's installed home dir
31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven
32 | # e.g. to debug Maven itself, use
33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files
35 | # ----------------------------------------------------------------------------
36 |
37 | if [ -z "$MAVEN_SKIP_RC" ] ; then
38 |
39 | if [ -f /etc/mavenrc ] ; then
40 | . /etc/mavenrc
41 | fi
42 |
43 | if [ -f "$HOME/.mavenrc" ] ; then
44 | . "$HOME/.mavenrc"
45 | fi
46 |
47 | fi
48 |
49 | # OS specific support. $var _must_ be set to either true or false.
50 | cygwin=false;
51 | darwin=false;
52 | mingw=false
53 | case "`uname`" in
54 | CYGWIN*) cygwin=true ;;
55 | MINGW*) mingw=true;;
56 | Darwin*) darwin=true
57 | #
58 | # Look for the Apple JDKs first to preserve the existing behaviour, and then look
59 | # for the new JDKs provided by Oracle.
60 | #
61 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then
62 | #
63 | # Apple JDKs
64 | #
65 | export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home
66 | fi
67 |
68 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then
69 | #
70 | # Apple JDKs
71 | #
72 | export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
73 | fi
74 |
75 | if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then
76 | #
77 | # Oracle JDKs
78 | #
79 | export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
80 | fi
81 |
82 | if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then
83 | #
84 | # Apple JDKs
85 | #
86 | export JAVA_HOME=`/usr/libexec/java_home`
87 | fi
88 | ;;
89 | esac
90 |
91 | if [ -z "$JAVA_HOME" ] ; then
92 | if [ -r /etc/gentoo-release ] ; then
93 | JAVA_HOME=`java-config --jre-home`
94 | fi
95 | fi
96 |
97 | if [ -z "$M2_HOME" ] ; then
98 | ## resolve links - $0 may be a link to maven's home
99 | PRG="$0"
100 |
101 | # need this for relative symlinks
102 | while [ -h "$PRG" ] ; do
103 | ls=`ls -ld "$PRG"`
104 | link=`expr "$ls" : '.*-> \(.*\)$'`
105 | if expr "$link" : '/.*' > /dev/null; then
106 | PRG="$link"
107 | else
108 | PRG="`dirname "$PRG"`/$link"
109 | fi
110 | done
111 |
112 | saveddir=`pwd`
113 |
114 | M2_HOME=`dirname "$PRG"`/..
115 |
116 | # make it fully qualified
117 | M2_HOME=`cd "$M2_HOME" && pwd`
118 |
119 | cd "$saveddir"
120 | # echo Using m2 at $M2_HOME
121 | fi
122 |
123 | # For Cygwin, ensure paths are in UNIX format before anything is touched
124 | if $cygwin ; then
125 | [ -n "$M2_HOME" ] &&
126 | M2_HOME=`cygpath --unix "$M2_HOME"`
127 | [ -n "$JAVA_HOME" ] &&
128 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
129 | [ -n "$CLASSPATH" ] &&
130 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
131 | fi
132 |
133 | # For Migwn, ensure paths are in UNIX format before anything is touched
134 | if $mingw ; then
135 | [ -n "$M2_HOME" ] &&
136 | M2_HOME="`(cd "$M2_HOME"; pwd)`"
137 | [ -n "$JAVA_HOME" ] &&
138 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
139 | # TODO classpath?
140 | fi
141 |
142 | if [ -z "$JAVA_HOME" ]; then
143 | javaExecutable="`which javac`"
144 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
145 | # readlink(1) is not available as standard on Solaris 10.
146 | readLink=`which readlink`
147 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
148 | if $darwin ; then
149 | javaHome="`dirname \"$javaExecutable\"`"
150 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
151 | else
152 | javaExecutable="`readlink -f \"$javaExecutable\"`"
153 | fi
154 | javaHome="`dirname \"$javaExecutable\"`"
155 | javaHome=`expr "$javaHome" : '\(.*\)/bin'`
156 | JAVA_HOME="$javaHome"
157 | export JAVA_HOME
158 | fi
159 | fi
160 | fi
161 |
162 | if [ -z "$JAVACMD" ] ; then
163 | if [ -n "$JAVA_HOME" ] ; then
164 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
165 | # IBM's JDK on AIX uses strange locations for the executables
166 | JAVACMD="$JAVA_HOME/jre/sh/java"
167 | else
168 | JAVACMD="$JAVA_HOME/bin/java"
169 | fi
170 | else
171 | JAVACMD="`which java`"
172 | fi
173 | fi
174 |
175 | if [ ! -x "$JAVACMD" ] ; then
176 | echo "Error: JAVA_HOME is not defined correctly." >&2
177 | echo " We cannot execute $JAVACMD" >&2
178 | exit 1
179 | fi
180 |
181 | if [ -z "$JAVA_HOME" ] ; then
182 | echo "Warning: JAVA_HOME environment variable is not set."
183 | fi
184 |
185 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
186 |
187 | # For Cygwin, switch paths to Windows format before running java
188 | if $cygwin; then
189 | [ -n "$M2_HOME" ] &&
190 | M2_HOME=`cygpath --path --windows "$M2_HOME"`
191 | [ -n "$JAVA_HOME" ] &&
192 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
193 | [ -n "$CLASSPATH" ] &&
194 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
195 | fi
196 |
197 | # traverses directory structure from process work directory to filesystem root
198 | # first directory with .mvn subdirectory is considered project base directory
199 | find_maven_basedir() {
200 | local basedir=$(pwd)
201 | local wdir=$(pwd)
202 | while [ "$wdir" != '/' ] ; do
203 | if [ -d "$wdir"/.mvn ] ; then
204 | basedir=$wdir
205 | break
206 | fi
207 | wdir=$(cd "$wdir/.."; pwd)
208 | done
209 | echo "${basedir}"
210 | }
211 |
212 | # concatenates all lines of a file
213 | concat_lines() {
214 | if [ -f "$1" ]; then
215 | echo "$(tr -s '\n' ' ' < "$1")"
216 | fi
217 | }
218 |
219 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)}
220 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
221 |
222 | # Provide a "standardized" way to retrieve the CLI args that will
223 | # work with both Windows and non-Windows executions.
224 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
225 | export MAVEN_CMD_LINE_ARGS
226 |
227 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
228 |
229 | exec "$JAVACMD" \
230 | $MAVEN_OPTS \
231 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
232 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
233 | ${WRAPPER_LAUNCHER} "$@"
234 |
--------------------------------------------------------------------------------
/mvnw.cmd:
--------------------------------------------------------------------------------
1 | @REM ----------------------------------------------------------------------------
2 | @REM Licensed to the Apache Software Foundation (ASF) under one
3 | @REM or more contributor license agreements. See the NOTICE file
4 | @REM distributed with this work for additional information
5 | @REM regarding copyright ownership. The ASF licenses this file
6 | @REM to you under the Apache License, Version 2.0 (the
7 | @REM "License"); you may not use this file except in compliance
8 | @REM with the License. You may obtain a copy of the License at
9 | @REM
10 | @REM http://www.apache.org/licenses/LICENSE-2.0
11 | @REM
12 | @REM Unless required by applicable law or agreed to in writing,
13 | @REM software distributed under the License is distributed on an
14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | @REM KIND, either express or implied. See the License for the
16 | @REM specific language governing permissions and limitations
17 | @REM under the License.
18 | @REM ----------------------------------------------------------------------------
19 |
20 | @REM ----------------------------------------------------------------------------
21 | @REM Maven2 Start Up Batch script
22 | @REM
23 | @REM Required ENV vars:
24 | @REM JAVA_HOME - location of a JDK home dir
25 | @REM
26 | @REM Optional ENV vars
27 | @REM M2_HOME - location of maven2's installed home dir
28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
31 | @REM e.g. to debug Maven itself, use
32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
34 | @REM ----------------------------------------------------------------------------
35 |
36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
37 | @echo off
38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
40 |
41 | @REM set %HOME% to equivalent of $HOME
42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
43 |
44 | @REM Execute a user defined script before this one
45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending
47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
49 | :skipRcPre
50 |
51 | @setlocal
52 |
53 | set ERROR_CODE=0
54 |
55 | @REM To isolate internal variables from possible post scripts, we use another setlocal
56 | @setlocal
57 |
58 | @REM ==== START VALIDATION ====
59 | if not "%JAVA_HOME%" == "" goto OkJHome
60 |
61 | echo.
62 | echo Error: JAVA_HOME not found in your environment. >&2
63 | echo Please set the JAVA_HOME variable in your environment to match the >&2
64 | echo location of your Java installation. >&2
65 | echo.
66 | goto error
67 |
68 | :OkJHome
69 | if exist "%JAVA_HOME%\bin\java.exe" goto init
70 |
71 | echo.
72 | echo Error: JAVA_HOME is set to an invalid directory. >&2
73 | echo JAVA_HOME = "%JAVA_HOME%" >&2
74 | echo Please set the JAVA_HOME variable in your environment to match the >&2
75 | echo location of your Java installation. >&2
76 | echo.
77 | goto error
78 |
79 | @REM ==== END VALIDATION ====
80 |
81 | :init
82 |
83 | set MAVEN_CMD_LINE_ARGS=%*
84 |
85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
86 | @REM Fallback to current working directory if not found.
87 |
88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
90 |
91 | set EXEC_DIR=%CD%
92 | set WDIR=%EXEC_DIR%
93 | :findBaseDir
94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound
95 | cd ..
96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound
97 | set WDIR=%CD%
98 | goto findBaseDir
99 |
100 | :baseDirFound
101 | set MAVEN_PROJECTBASEDIR=%WDIR%
102 | cd "%EXEC_DIR%"
103 | goto endDetectBaseDir
104 |
105 | :baseDirNotFound
106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
107 | cd "%EXEC_DIR%"
108 |
109 | :endDetectBaseDir
110 |
111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
112 |
113 | @setlocal EnableExtensions EnableDelayedExpansion
114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
116 |
117 | :endReadAdditionalConfig
118 |
119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
120 |
121 | set WRAPPER_JAR="".\.mvn\wrapper\maven-wrapper.jar""
122 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
123 |
124 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS%
125 | if ERRORLEVEL 1 goto error
126 | goto end
127 |
128 | :error
129 | set ERROR_CODE=1
130 |
131 | :end
132 | @endlocal & set ERROR_CODE=%ERROR_CODE%
133 |
134 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
135 | @REM check for post script, once with legacy .bat ending and once with .cmd ending
136 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
137 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
138 | :skipRcPost
139 |
140 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
141 | if "%MAVEN_BATCH_PAUSE%" == "on" pause
142 |
143 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
144 |
145 | exit /B %ERROR_CODE%
--------------------------------------------------------------------------------
/mysql.env:
--------------------------------------------------------------------------------
1 | # MySQL docker 默认配置
2 | MYSQL_ROOT_PASSWORD=root
3 | MYSQL_DATABASE=spring-blog
4 | MYSQL_USER=username
5 | MYSQL_PASSWORD=password
6 | MYSQL_ALLOW_EMPTY_PASSWORD=no
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | com.hpm.blog
7 | spring-boot-blog
8 | 0.0.1-SNAPSHOT
9 | jar
10 |
11 | spring-boot-blog
12 | Demo project for Spring Boot
13 |
14 |
15 | org.springframework.boot
16 | spring-boot-starter-parent
17 | 1.4.3.RELEASE
18 |
19 |
20 |
21 |
22 | UTF-8
23 | UTF-8
24 | 1.8
25 |
26 |
27 |
28 |
29 | org.mybatis.spring.boot
30 | mybatis-spring-boot-starter
31 | 1.1.1
32 |
33 |
34 | org.springframework.boot
35 | spring-boot-starter-web
36 |
37 |
38 | com.alibaba
39 | fastjson
40 | 1.2.83
41 |
42 |
43 |
44 | org.springframework.boot
45 | spring-boot-devtools
46 |
47 |
48 |
49 | mysql
50 | mysql-connector-java
51 | runtime
52 |
53 |
54 | org.springframework.boot
55 | spring-boot-starter-test
56 | test
57 |
58 |
59 |
60 | com.auth0
61 | java-jwt
62 | 3.0.2
63 |
64 |
65 |
66 | commons-lang
67 | commons-lang
68 | 2.6
69 |
70 |
71 |
72 | com.google.guava
73 | guava
74 | 21.0
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | org.springframework.boot
83 | spring-boot-maven-plugin
84 |
85 |
86 |
87 |
88 |
89 |
90 | spring-releases
91 | https://repo.spring.io/libs-release
92 |
93 |
94 |
95 |
96 | spring-releases
97 | https://repo.spring.io/libs-release
98 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/src/main/java/com/hpm/blog/SpringBootBlogApplication.java:
--------------------------------------------------------------------------------
1 | package com.hpm.blog;
2 |
3 | import org.mybatis.spring.annotation.MapperScan;
4 | import org.springframework.boot.SpringApplication;
5 | import org.springframework.boot.autoconfigure.SpringBootApplication;
6 |
7 | @SpringBootApplication
8 | @MapperScan("com.hpm.blog.mapper")
9 | public class SpringBootBlogApplication {
10 |
11 | public static void main(String[] args) {
12 | SpringApplication.run(SpringBootBlogApplication.class, args);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/hpm/blog/annotation/CurrentUser.java:
--------------------------------------------------------------------------------
1 | package com.hpm.blog.annotation;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * 在Controller的方法参数中使用此注解,该方法在映射时会注入当前登录的User对象
10 | */
11 | @Target(ElementType.PARAMETER) // 可用在方法的参数上
12 | @Retention(RetentionPolicy.RUNTIME) // 运行时有效
13 | public @interface CurrentUser {
14 | }
--------------------------------------------------------------------------------
/src/main/java/com/hpm/blog/annotation/LoginRequired.java:
--------------------------------------------------------------------------------
1 | package com.hpm.blog.annotation;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * 在需要登录验证的Controller的方法上使用此注解
10 | */
11 | @Target({ElementType.METHOD})
12 | @Retention(RetentionPolicy.RUNTIME)
13 | public @interface LoginRequired {
14 | }
15 |
16 |
--------------------------------------------------------------------------------
/src/main/java/com/hpm/blog/api/AuthenticationApi.java:
--------------------------------------------------------------------------------
1 | package com.hpm.blog.api;
2 |
3 | import com.alibaba.fastjson.JSONObject;
4 | import com.hpm.blog.model.User;
5 | import com.hpm.blog.service.AuthenticationService;
6 | import com.hpm.blog.service.UserService;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.web.bind.annotation.PostMapping;
9 | import org.springframework.web.bind.annotation.RequestBody;
10 | import org.springframework.web.bind.annotation.RequestMapping;
11 | import org.springframework.web.bind.annotation.RestController;
12 |
13 | @RestController
14 | @RequestMapping("/api/authentication")
15 | public class AuthenticationApi {
16 | private AuthenticationService authenticationService;
17 | private UserService userService;
18 |
19 | @Autowired
20 | public AuthenticationApi(AuthenticationService authenticationService, UserService userService) {
21 | this.authenticationService = authenticationService;
22 | this.userService = userService;
23 | }
24 |
25 | @PostMapping("")
26 | public Object login(@RequestBody User user) {
27 | User userInDataBase = userService.findByName(user.getName());
28 | JSONObject jsonObject = new JSONObject();
29 | if (userInDataBase == null) {
30 | jsonObject.put("error", "用户不存在");
31 | } else if (!userService.comparePassword(user, userInDataBase)) {
32 | jsonObject.put("error", "密码不正确");
33 | } else {
34 | String token = authenticationService.getToken(userInDataBase);
35 | jsonObject.put("token", token);
36 | jsonObject.put("user", userInDataBase);
37 | }
38 | return jsonObject;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/com/hpm/blog/api/PostApi.java:
--------------------------------------------------------------------------------
1 | package com.hpm.blog.api;
2 |
3 | import com.alibaba.fastjson.JSONObject;
4 | import com.hpm.blog.annotation.CurrentUser;
5 | import com.hpm.blog.annotation.LoginRequired;
6 | import com.hpm.blog.model.Post;
7 | import com.hpm.blog.model.User;
8 | import com.hpm.blog.service.PostService;
9 | import org.springframework.beans.factory.annotation.Autowired;
10 | import org.springframework.web.bind.annotation.*;
11 |
12 | import java.util.List;
13 |
14 | /**
15 | * 文章接口
16 | */
17 | @RestController
18 | @RequestMapping("/api/post")
19 | public class PostApi {
20 | private PostService postService;
21 |
22 | @Autowired
23 | public PostApi(PostService postService) {
24 | this.postService = postService;
25 | }
26 |
27 | @PostMapping("")
28 | @LoginRequired
29 | public Post add(@RequestBody Post post, @CurrentUser User user) {
30 | post.setAuthorId(user.getId()); // 添加作者信息
31 | post = postService.add(post);
32 | return post;
33 | }
34 |
35 | @GetMapping("/{id}")
36 | public Object findById(@PathVariable int id) {
37 | Post post = postService.findById(id);
38 | return postService.findById(id);
39 | }
40 |
41 | @GetMapping("")
42 | public List all() {
43 | return postService.all();
44 | }
45 |
46 | /**
47 | * 更新文章,需要登录
48 | * @param post 需要修改的内容
49 | * @param id 文章 id
50 | * @param currentUser 当前用户
51 | * @return 更新之后的文章
52 | */
53 | @LoginRequired
54 | @PutMapping("/{id}")
55 | public Post update(@RequestBody Post post, @PathVariable int id, @CurrentUser User currentUser) {
56 | post.setId(id);
57 | return postService.update(post, currentUser);
58 | }
59 |
60 | /**
61 | * 删除文章,需要登录
62 | * @param id 文章 id
63 | * @param currentUser 当前登录用户
64 | * @return 提示信息
65 | */
66 | @LoginRequired
67 | @DeleteMapping("/{id}")
68 | public Object delete(@PathVariable int id, @CurrentUser User currentUser) {
69 | postService.delete(id, currentUser);
70 | JSONObject jsonObject = new JSONObject();
71 | jsonObject.put("message", "删除成功");
72 | return jsonObject;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/main/java/com/hpm/blog/api/UserApi.java:
--------------------------------------------------------------------------------
1 | package com.hpm.blog.api;
2 |
3 |
4 | import com.alibaba.fastjson.JSONObject;
5 | import com.hpm.blog.model.User;
6 | import com.hpm.blog.service.UserService;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.web.bind.annotation.*;
9 |
10 | @RestController
11 | @RequestMapping("/api/user")
12 | public class UserApi {
13 | private UserService userService;
14 |
15 | @Autowired
16 | public UserApi(UserService userService) {
17 | this.userService = userService;
18 | }
19 |
20 | @PostMapping("")
21 | public Object add(@RequestBody User user) {
22 | if (userService.findByName(user.getName()) != null) {
23 | JSONObject jsonObject = new JSONObject();
24 | jsonObject.put("error","用户名已被使用");
25 | return jsonObject;
26 | }
27 | return userService.add(user);
28 | }
29 |
30 | @GetMapping("{id}")
31 | public Object findById(@PathVariable int id) {
32 | return userService.findById(id);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/com/hpm/blog/config/AuthenticationInterceptor.java:
--------------------------------------------------------------------------------
1 | package com.hpm.blog.config;
2 |
3 | import com.auth0.jwt.JWT;
4 | import com.auth0.jwt.JWTVerifier;
5 | import com.auth0.jwt.algorithms.Algorithm;
6 | import com.auth0.jwt.exceptions.JWTDecodeException;
7 | import com.auth0.jwt.exceptions.JWTVerificationException;
8 | import com.hpm.blog.annotation.LoginRequired;
9 | import com.hpm.blog.model.User;
10 | import com.hpm.blog.service.UserService;
11 | import org.springframework.beans.factory.annotation.Autowired;
12 | import org.springframework.web.method.HandlerMethod;
13 | import org.springframework.web.servlet.HandlerInterceptor;
14 | import org.springframework.web.servlet.ModelAndView;
15 |
16 | import javax.servlet.http.HttpServletRequest;
17 | import javax.servlet.http.HttpServletResponse;
18 | import java.io.UnsupportedEncodingException;
19 | import java.lang.reflect.Method;
20 |
21 |
22 | public class AuthenticationInterceptor implements HandlerInterceptor {
23 | @Autowired
24 | private UserService userService;
25 |
26 | public boolean preHandle(HttpServletRequest request,
27 | HttpServletResponse response, Object handler) throws Exception {
28 | // 如果不是映射到方法直接通过
29 | if (!(handler instanceof HandlerMethod)) {
30 | return true;
31 | }
32 | HandlerMethod handlerMethod = (HandlerMethod) handler;
33 | Method method = handlerMethod.getMethod();
34 |
35 | // 判断接口是否需要登录
36 | LoginRequired methodAnnotation = method.getAnnotation(LoginRequired.class);
37 | // 有 @LoginRequired 注解,需要认证
38 | if (methodAnnotation != null) {
39 | // 执行认证
40 | String token = request.getHeader("token"); // 从 http 请求头中取出 token
41 | if (token == null) {
42 | throw new RuntimeException("无token,请重新登录");
43 | }
44 | int userId;
45 | try {
46 | userId = Integer.parseInt(JWT.decode(token).getAudience().get(0)); // 获取 token 中的 user id
47 | } catch (JWTDecodeException e) {
48 | throw new RuntimeException("token无效,请重新登录");
49 | }
50 | User user = userService.findById(userId);
51 | if (user == null) {
52 | throw new RuntimeException("用户不存在,请重新登录");
53 | }
54 | // 验证 token
55 | try {
56 | JWTVerifier verifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
57 | try {
58 | verifier.verify(token);
59 | } catch (JWTVerificationException e) {
60 | throw new RuntimeException("token无效,请重新登录");
61 | }
62 | } catch (UnsupportedEncodingException ignore) {}
63 | request.setAttribute("currentUser", user);
64 | return true;
65 | }
66 | return true;
67 | }
68 |
69 | public void postHandle(HttpServletRequest request,
70 | HttpServletResponse response, Object handler,
71 | ModelAndView modelAndView) throws Exception {
72 | }
73 |
74 | public void afterCompletion(HttpServletRequest request,
75 | HttpServletResponse response, Object handler, Exception ex)
76 | throws Exception {
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/main/java/com/hpm/blog/config/CurrentUserMethodArgumentResolver.java:
--------------------------------------------------------------------------------
1 | package com.hpm.blog.config;
2 |
3 | import com.hpm.blog.annotation.CurrentUser;
4 | import com.hpm.blog.model.User;
5 | import org.springframework.core.MethodParameter;
6 | import org.springframework.web.bind.support.WebDataBinderFactory;
7 | import org.springframework.web.context.request.NativeWebRequest;
8 | import org.springframework.web.context.request.RequestAttributes;
9 | import org.springframework.web.method.support.HandlerMethodArgumentResolver;
10 | import org.springframework.web.method.support.ModelAndViewContainer;
11 | import org.springframework.web.multipart.support.MissingServletRequestPartException;
12 |
13 | /**
14 | * 增加方法注入,将含有 @CurrentUser 注解的方法参数注入当前登录用户
15 | */
16 | public class CurrentUserMethodArgumentResolver implements HandlerMethodArgumentResolver {
17 | @Override
18 | public boolean supportsParameter(MethodParameter parameter) {
19 | return parameter.getParameterType().isAssignableFrom(User.class)
20 | && parameter.hasParameterAnnotation(CurrentUser.class);
21 | }
22 |
23 | @Override
24 | public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
25 | User user = (User) webRequest.getAttribute("currentUser", RequestAttributes.SCOPE_REQUEST);
26 | if (user != null) {
27 | return user;
28 | }
29 | throw new MissingServletRequestPartException("currentUser");
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/hpm/blog/config/FastJsonHttpMessageConverterEx.java:
--------------------------------------------------------------------------------
1 | package com.hpm.blog.config;
2 |
3 | import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
4 |
5 | public class FastJsonHttpMessageConverterEx extends FastJsonHttpMessageConverter {
6 | public FastJsonHttpMessageConverterEx() {
7 | }
8 |
9 | @Override
10 | protected boolean supports(Class> clazz) {
11 | return super.supports(clazz);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/hpm/blog/config/GlobalExceptionHandler.java:
--------------------------------------------------------------------------------
1 | package com.hpm.blog.config;
2 |
3 |
4 | import com.alibaba.fastjson.JSONObject;
5 | import org.apache.commons.lang.exception.ExceptionUtils;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 | import org.springframework.web.bind.annotation.ControllerAdvice;
9 | import org.springframework.web.bind.annotation.ExceptionHandler;
10 | import org.springframework.web.bind.annotation.ResponseBody;
11 |
12 | import javax.servlet.ServletRequest;
13 |
14 |
15 | @ControllerAdvice
16 | public class GlobalExceptionHandler {
17 | private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
18 | @ResponseBody
19 | @ExceptionHandler(Exception.class)
20 | public Object handleException(Exception e) {
21 | logger.error(ExceptionUtils.getFullStackTrace(e)); // 记录错误信息
22 | String msg = e.getMessage();
23 | if (msg == null || msg.equals("")) {
24 | msg = "服务器出错";
25 | }
26 | JSONObject jsonObject = new JSONObject();
27 | jsonObject.put("error", msg);
28 | return jsonObject;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/com/hpm/blog/config/WebMvcConfigurer.java:
--------------------------------------------------------------------------------
1 | package com.hpm.blog.config;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.context.annotation.Configuration;
5 | import org.springframework.http.converter.HttpMessageConverter;
6 | import org.springframework.web.method.support.HandlerMethodArgumentResolver;
7 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
8 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
9 |
10 | import java.util.List;
11 |
12 | @Configuration
13 | public class WebMvcConfigurer extends WebMvcConfigurerAdapter {
14 | @Override
15 | public void addInterceptors(InterceptorRegistry registry) {
16 | registry.addInterceptor(authenticationInterceptor())
17 | .addPathPatterns("/**"); // 拦截所有请求,通过判断是否有 @LoginRequired 注解 决定是否需要登录
18 | super.addInterceptors(registry);
19 | }
20 |
21 | @Override
22 | public void addArgumentResolvers(List argumentResolvers) {
23 | argumentResolvers.add(currentUserMethodArgumentResolver());
24 | super.addArgumentResolvers(argumentResolvers);
25 | }
26 |
27 | @Override
28 | public void configureMessageConverters(List> converters) {
29 | converters.add(fastJsonHttpMessageConverterEx());
30 | super.configureMessageConverters(converters);
31 | }
32 |
33 | @Bean
34 | public FastJsonHttpMessageConverterEx fastJsonHttpMessageConverterEx() {
35 | return new FastJsonHttpMessageConverterEx();
36 | }
37 |
38 | @Bean
39 | public CurrentUserMethodArgumentResolver currentUserMethodArgumentResolver() {
40 | return new CurrentUserMethodArgumentResolver();
41 | }
42 |
43 | @Bean
44 | public AuthenticationInterceptor authenticationInterceptor() {
45 | return new AuthenticationInterceptor();
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/com/hpm/blog/mapper/PostMapper.java:
--------------------------------------------------------------------------------
1 | package com.hpm.blog.mapper;
2 |
3 | import com.hpm.blog.model.Post;
4 |
5 | import java.util.List;
6 |
7 | public interface PostMapper {
8 | int add(Post post);
9 |
10 | Post findOne(Post param);
11 |
12 | List all();
13 |
14 | void update(Post post);
15 |
16 | void delete(int id);
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/hpm/blog/mapper/UserMapper.java:
--------------------------------------------------------------------------------
1 | package com.hpm.blog.mapper;
2 |
3 | import com.hpm.blog.model.User;
4 |
5 | public interface UserMapper {
6 | int add(User user);
7 |
8 | User findOne(User user);
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/com/hpm/blog/model/Post.java:
--------------------------------------------------------------------------------
1 | package com.hpm.blog.model;
2 |
3 | import java.util.Date;
4 |
5 | /**
6 | * 文章类
7 | */
8 | public class Post {
9 | private Integer id;
10 | private User author;
11 | private Integer authorId; // 作者的 id
12 | private String title; // 文章标题
13 | private String content; // 文章内容
14 | private Date createTime;
15 |
16 | public Integer getId() {
17 | return id;
18 | }
19 |
20 | public void setId(Integer id) {
21 | this.id = id;
22 | }
23 |
24 | public User getAuthor() {
25 | return author;
26 | }
27 |
28 | public void setAuthor(User author) {
29 | this.author = author;
30 | }
31 |
32 | public Integer getAuthorId() {
33 | return authorId;
34 | }
35 |
36 | public void setAuthorId(Integer authorId) {
37 | this.authorId = authorId;
38 | }
39 |
40 | public String getTitle() {
41 | return title;
42 | }
43 |
44 | public void setTitle(String title) {
45 | this.title = title;
46 | }
47 |
48 | public String getContent() {
49 | return content;
50 | }
51 |
52 | public void setContent(String content) {
53 | this.content = content;
54 | }
55 |
56 | public Date getCreateTime() {
57 | return createTime;
58 | }
59 |
60 | public void setCreateTime(Date createTime) {
61 | this.createTime = createTime;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/com/hpm/blog/model/User.java:
--------------------------------------------------------------------------------
1 | package com.hpm.blog.model;
2 |
3 | public class User {
4 | private Integer id; // 这里不用 int, 应为 int 自动初始化为0,mybatis mapper 文件 就不能使用 了
5 | private String name;
6 | private String password;
7 |
8 | public Integer getId() {
9 | return id;
10 | }
11 |
12 | public void setId(Integer id) {
13 | this.id = id;
14 | }
15 |
16 | public String getName() {
17 | return name;
18 | }
19 |
20 | public void setName(String name) {
21 | this.name = name;
22 | }
23 |
24 | public String getPassword() {
25 | return password;
26 | }
27 |
28 | public void setPassword(String password) {
29 | this.password = password;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/hpm/blog/service/AuthenticationService.java:
--------------------------------------------------------------------------------
1 | package com.hpm.blog.service;
2 |
3 | import com.auth0.jwt.JWT;
4 | import com.auth0.jwt.algorithms.Algorithm;
5 | import com.hpm.blog.model.User;
6 | import org.springframework.stereotype.Service;
7 |
8 | import java.io.UnsupportedEncodingException;
9 |
10 | @Service
11 | public class AuthenticationService {
12 | public String getToken(User user) {
13 | String token = "";
14 | try {
15 | token = JWT.create()
16 | .withAudience(user.getId().toString()) // 将 user id 保存到 token 里面
17 | .sign(Algorithm.HMAC256(user.getPassword())); // 以 password 作为 token 的密钥
18 | } catch (UnsupportedEncodingException ignore) {
19 | }
20 | return token;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/com/hpm/blog/service/PostService.java:
--------------------------------------------------------------------------------
1 | package com.hpm.blog.service;
2 |
3 | import com.hpm.blog.mapper.PostMapper;
4 | import com.hpm.blog.model.Post;
5 | import com.hpm.blog.model.User;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.stereotype.Service;
8 | import org.springframework.transaction.annotation.Transactional;
9 |
10 | import java.util.List;
11 |
12 | import static com.google.common.base.Preconditions.checkNotNull;
13 |
14 | @Service
15 | public class PostService {
16 | private PostMapper postMapper;
17 |
18 | @Autowired
19 | public PostService(PostMapper postMapper) {
20 | this.postMapper = postMapper;
21 | }
22 |
23 | @Transactional
24 | public Post add(Post post) {
25 | postMapper.add(post);
26 | return findById(post.getId());
27 | }
28 |
29 | public Post findById(Integer id) {
30 | Post param = new Post();
31 | param.setId(id);
32 | Post post = postMapper.findOne(param);
33 | checkNotNull(post, "文章不存在");
34 | return post;
35 | }
36 |
37 | public List all() {
38 | return postMapper.all();
39 | }
40 |
41 | public Post update(Post post, User currentUser) {
42 | checkNotNull(post.getId(), "id不能为空");
43 | checkOwner(post.getId(), currentUser);
44 | postMapper.update(post);
45 | return findById(post.getId());
46 | }
47 |
48 | private void checkOwner(Integer id, User currentUser) {
49 | Post post = findById(id);
50 | if (!post.getAuthorId().equals(currentUser.getId())) {
51 | throw new RuntimeException("不能删除或修改别人的文章");
52 | }
53 | }
54 |
55 | public void delete(int id, User currentUser) {
56 | checkOwner(id, currentUser);
57 | postMapper.delete(id);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/com/hpm/blog/service/UserService.java:
--------------------------------------------------------------------------------
1 | package com.hpm.blog.service;
2 |
3 |
4 | import com.hpm.blog.mapper.UserMapper;
5 | import com.hpm.blog.model.User;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.stereotype.Service;
8 |
9 | import java.security.MessageDigest;
10 | import java.security.NoSuchAlgorithmException;
11 |
12 | @Service
13 | public class UserService {
14 | private UserMapper userMapper;
15 |
16 | @Autowired
17 | public UserService(UserMapper userMapper) {
18 | this.userMapper = userMapper;
19 | }
20 |
21 | public User add(User user) {
22 | String passwordHash = passwordToHash(user.getPassword());
23 | user.setPassword(passwordHash);
24 | userMapper.add(user);
25 | return findById(user.getId());
26 | }
27 |
28 | private String passwordToHash(String password) {
29 | try {
30 | MessageDigest digest = MessageDigest.getInstance("SHA-256");
31 | digest.update(password.getBytes());
32 | byte[] src = digest.digest();
33 | StringBuilder stringBuilder = new StringBuilder();
34 | // 字节数组转16进制字符串
35 | // https://my.oschina.net/u/347386/blog/182717
36 | for (byte aSrc : src) {
37 | String s = Integer.toHexString(aSrc & 0xFF);
38 | if (s.length() < 2) {
39 | stringBuilder.append('0');
40 | }
41 | stringBuilder.append(s);
42 | }
43 | return stringBuilder.toString();
44 | } catch (NoSuchAlgorithmException ignore) {
45 | }
46 | return null;
47 | }
48 |
49 | public User findById(int id) {
50 | User user = new User();
51 | user.setId(id);
52 | return userMapper.findOne(user);
53 | }
54 |
55 | public User findByName(String name) {
56 | User param = new User();
57 | param.setName(name);
58 | return userMapper.findOne(param);
59 | }
60 |
61 | public boolean comparePassword(User user, User userInDataBase) {
62 | return passwordToHash(user.getPassword()) // 将用户提交的密码转换为 hash
63 | .equals(userInDataBase.getPassword()); // 数据库中的 password 已经是 hash,不用转换
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | datasource:
3 | url: jdbc:mysql://localhost:3306/spring-blog?useUnicode=true&characterEncoding=utf8&autoReconnect=true
4 | username: root
5 | password: root
6 | debug: true
7 | mybatis:
8 | type-aliases-package: com.hpm.blog.model
9 | config-location: classpath:mybatis.xml
10 |
--------------------------------------------------------------------------------
/src/main/resources/com/hpm/blog/mapper/PostMapper.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
9 |
10 |
11 |
12 |
13 |
14 | INSERT INTO post (author_id, title, content) VALUES (#{authorId}, #{title}, #{content});
15 |
16 |
17 |
18 | UPDATE post
19 |
20 |
21 | title=#{title},
22 |
23 |
24 | content=#{content},
25 |
26 |
27 | where post.id=#{id}
28 |
29 |
30 |
31 | DELETE FROM post WHERE post.id=#{id}
32 |
33 |
34 |
42 |
43 |
46 |
47 |
48 | SELECT
49 | post.id,
50 | post.author_id ,
51 | post.title ,
52 | post.content ,
53 | post.create_time ,
54 | post.update_time,
55 |
56 | `user`.id as author__id,
57 | `user`.`name` as author__name
58 | FROM post
59 | LEFT JOIN `user` ON `user`.id=post.author_id
60 |
61 |
--------------------------------------------------------------------------------
/src/main/resources/com/hpm/blog/mapper/UserMapper.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | insert into user(name, password) values (#{name},#{password})
6 |
7 |
8 |
20 |
--------------------------------------------------------------------------------
/src/main/resources/logback-spring.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | [%-5level] %d{HH:mm:ss} %logger{36} %line: %msg %n
9 | UTF-8
10 |
11 |
12 |
13 |
14 |
15 | log/spring-boot-blog.log
16 |
17 | log/spring-boot-blog.%d{yyyy-MM-dd}.log
18 | 30
19 |
20 |
21 | %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{35} : %n %msg %n
22 | UTF-8
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/src/main/resources/mybatis.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/main/resources/static/css/common.css:
--------------------------------------------------------------------------------
1 | .content {
2 | word-break: break-word;
3 | }
4 |
5 | .main {
6 | max-width: 800px;
7 | margin: 0 auto;
8 | }
9 |
10 | .post img {
11 | margin: 0 auto;
12 | display: block;
13 | }
14 |
15 | .fade-enter-active, .fade-leave-active {
16 | transition: all 1s
17 | }
18 | .fade-enter, .fade-leave-active {
19 | opacity: 0
20 | }
--------------------------------------------------------------------------------
/src/main/resources/static/css/font-awesome.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Font Awesome 4.2.0 by @davegandy - http://fontawesome.io - @fontawesome
3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
4 | */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.2.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.2.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff?v=4.2.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.2.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.2.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}
--------------------------------------------------------------------------------
/src/main/resources/static/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyrijk/spring-boot-blog/34160e7cd92c7171d7fe3da2748e8933e47222f4/src/main/resources/static/fonts/FontAwesome.otf
--------------------------------------------------------------------------------
/src/main/resources/static/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyrijk/spring-boot-blog/34160e7cd92c7171d7fe3da2748e8933e47222f4/src/main/resources/static/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/src/main/resources/static/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyrijk/spring-boot-blog/34160e7cd92c7171d7fe3da2748e8933e47222f4/src/main/resources/static/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/src/main/resources/static/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyrijk/spring-boot-blog/34160e7cd92c7171d7fe3da2748e8933e47222f4/src/main/resources/static/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/src/main/resources/static/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyrijk/spring-boot-blog/34160e7cd92c7171d7fe3da2748e8933e47222f4/src/main/resources/static/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/src/main/resources/static/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Spring Boot blog demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
32 |
33 |
34 |
62 |
63 |
64 |
75 |
76 |
77 |
86 |
87 |
88 |
103 |
104 |
105 |
124 |
125 |
126 |
135 |
136 |
137 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
--------------------------------------------------------------------------------
/src/main/resources/static/js/axios.min.js:
--------------------------------------------------------------------------------
1 | /* axios v0.15.3 | (c) 2016 by Matt Zabriskie */
2 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.axios=t():e.axios=t()}(this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){e.exports=n(1)},function(e,t,n){"use strict";function r(e){var t=new i(e),n=s(i.prototype.request,t);return o.extend(n,i.prototype,t),o.extend(n,t),n}var o=n(2),s=n(3),i=n(4),a=n(5),u=r(a);u.Axios=i,u.create=function(e){return r(o.merge(a,e))},u.Cancel=n(22),u.CancelToken=n(23),u.isCancel=n(19),u.all=function(e){return Promise.all(e)},u.spread=n(24),e.exports=u,e.exports.default=u},function(e,t,n){"use strict";function r(e){return"[object Array]"===C.call(e)}function o(e){return"[object ArrayBuffer]"===C.call(e)}function s(e){return"undefined"!=typeof FormData&&e instanceof FormData}function i(e){var t;return t="undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&e.buffer instanceof ArrayBuffer}function a(e){return"string"==typeof e}function u(e){return"number"==typeof e}function c(e){return"undefined"==typeof e}function f(e){return null!==e&&"object"==typeof e}function p(e){return"[object Date]"===C.call(e)}function d(e){return"[object File]"===C.call(e)}function l(e){return"[object Blob]"===C.call(e)}function h(e){return"[object Function]"===C.call(e)}function m(e){return f(e)&&h(e.pipe)}function y(e){return"undefined"!=typeof URLSearchParams&&e instanceof URLSearchParams}function w(e){return e.replace(/^\s*/,"").replace(/\s*$/,"")}function g(){return"undefined"!=typeof window&&"undefined"!=typeof document&&"function"==typeof document.createElement}function v(e,t){if(null!==e&&"undefined"!=typeof e)if("object"==typeof e||r(e)||(e=[e]),r(e))for(var n=0,o=e.length;n=200&&e<300}};c.headers={common:{Accept:"application/json, text/plain, */*"}},s.forEach(["delete","get","head"],function(e){c.headers[e]={}}),s.forEach(["post","put","patch"],function(e){c.headers[e]=s.merge(u)}),e.exports=c},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t){r.forEach(e,function(n,r){r!==t&&r.toUpperCase()===t.toUpperCase()&&(e[t]=n,delete e[r])})}},function(e,t,n){"use strict";var r=n(2),o=n(8),s=n(11),i=n(12),a=n(13),u=n(9),c="undefined"!=typeof window&&window.btoa&&window.btoa.bind(window)||n(14);e.exports=function(e){return new Promise(function(t,f){var p=e.data,d=e.headers;r.isFormData(p)&&delete d["Content-Type"];var l=new XMLHttpRequest,h="onreadystatechange",m=!1;if("undefined"==typeof window||!window.XDomainRequest||"withCredentials"in l||a(e.url)||(l=new window.XDomainRequest,h="onload",m=!0,l.onprogress=function(){},l.ontimeout=function(){}),e.auth){var y=e.auth.username||"",w=e.auth.password||"";d.Authorization="Basic "+c(y+":"+w)}if(l.open(e.method.toUpperCase(),s(e.url,e.params,e.paramsSerializer),!0),l.timeout=e.timeout,l[h]=function(){if(l&&(4===l.readyState||m)&&(0!==l.status||l.responseURL&&0===l.responseURL.indexOf("file:"))){var n="getAllResponseHeaders"in l?i(l.getAllResponseHeaders()):null,r=e.responseType&&"text"!==e.responseType?l.response:l.responseText,s={data:r,status:1223===l.status?204:l.status,statusText:1223===l.status?"No Content":l.statusText,headers:n,config:e,request:l};o(t,f,s),l=null}},l.onerror=function(){f(u("Network Error",e)),l=null},l.ontimeout=function(){f(u("timeout of "+e.timeout+"ms exceeded",e,"ECONNABORTED")),l=null},r.isStandardBrowserEnv()){var g=n(15),v=(e.withCredentials||a(e.url))&&e.xsrfCookieName?g.read(e.xsrfCookieName):void 0;v&&(d[e.xsrfHeaderName]=v)}if("setRequestHeader"in l&&r.forEach(d,function(e,t){"undefined"==typeof p&&"content-type"===t.toLowerCase()?delete d[t]:l.setRequestHeader(t,e)}),e.withCredentials&&(l.withCredentials=!0),e.responseType)try{l.responseType=e.responseType}catch(e){if("json"!==l.responseType)throw e}"function"==typeof e.onDownloadProgress&&l.addEventListener("progress",e.onDownloadProgress),"function"==typeof e.onUploadProgress&&l.upload&&l.upload.addEventListener("progress",e.onUploadProgress),e.cancelToken&&e.cancelToken.promise.then(function(e){l&&(l.abort(),f(e),l=null)}),void 0===p&&(p=null),l.send(p)})}},function(e,t,n){"use strict";var r=n(9);e.exports=function(e,t,n){var o=n.config.validateStatus;n.status&&o&&!o(n.status)?t(r("Request failed with status code "+n.status,n.config,null,n)):e(n)}},function(e,t,n){"use strict";var r=n(10);e.exports=function(e,t,n,o){var s=new Error(e);return r(s,t,n,o)}},function(e,t){"use strict";e.exports=function(e,t,n,r){return e.config=t,n&&(e.code=n),e.response=r,e}},function(e,t,n){"use strict";function r(e){return encodeURIComponent(e).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}var o=n(2);e.exports=function(e,t,n){if(!t)return e;var s;if(n)s=n(t);else if(o.isURLSearchParams(t))s=t.toString();else{var i=[];o.forEach(t,function(e,t){null!==e&&"undefined"!=typeof e&&(o.isArray(e)&&(t+="[]"),o.isArray(e)||(e=[e]),o.forEach(e,function(e){o.isDate(e)?e=e.toISOString():o.isObject(e)&&(e=JSON.stringify(e)),i.push(r(t)+"="+r(e))}))}),s=i.join("&")}return s&&(e+=(e.indexOf("?")===-1?"?":"&")+s),e}},function(e,t,n){"use strict";var r=n(2);e.exports=function(e){var t,n,o,s={};return e?(r.forEach(e.split("\n"),function(e){o=e.indexOf(":"),t=r.trim(e.substr(0,o)).toLowerCase(),n=r.trim(e.substr(o+1)),t&&(s[t]=s[t]?s[t]+", "+n:n)}),s):s}},function(e,t,n){"use strict";var r=n(2);e.exports=r.isStandardBrowserEnv()?function(){function e(e){var t=e;return n&&(o.setAttribute("href",t),t=o.href),o.setAttribute("href",t),{href:o.href,protocol:o.protocol?o.protocol.replace(/:$/,""):"",host:o.host,search:o.search?o.search.replace(/^\?/,""):"",hash:o.hash?o.hash.replace(/^#/,""):"",hostname:o.hostname,port:o.port,pathname:"/"===o.pathname.charAt(0)?o.pathname:"/"+o.pathname}}var t,n=/(msie|trident)/i.test(navigator.userAgent),o=document.createElement("a");return t=e(window.location.href),function(n){var o=r.isString(n)?e(n):n;return o.protocol===t.protocol&&o.host===t.host}}():function(){return function(){return!0}}()},function(e,t){"use strict";function n(){this.message="String contains an invalid character"}function r(e){for(var t,r,s=String(e),i="",a=0,u=o;s.charAt(0|a)||(u="=",a%1);i+=u.charAt(63&t>>8-a%1*8)){if(r=s.charCodeAt(a+=.75),r>255)throw new n;t=t<<8|r}return i}var o="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";n.prototype=new Error,n.prototype.code=5,n.prototype.name="InvalidCharacterError",e.exports=r},function(e,t,n){"use strict";var r=n(2);e.exports=r.isStandardBrowserEnv()?function(){return{write:function(e,t,n,o,s,i){var a=[];a.push(e+"="+encodeURIComponent(t)),r.isNumber(n)&&a.push("expires="+new Date(n).toGMTString()),r.isString(o)&&a.push("path="+o),r.isString(s)&&a.push("domain="+s),i===!0&&a.push("secure"),document.cookie=a.join("; ")},read:function(e){var t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove:function(e){this.write(e,"",Date.now()-864e5)}}}():function(){return{write:function(){},read:function(){return null},remove:function(){}}}()},function(e,t,n){"use strict";function r(){this.handlers=[]}var o=n(2);r.prototype.use=function(e,t){return this.handlers.push({fulfilled:e,rejected:t}),this.handlers.length-1},r.prototype.eject=function(e){this.handlers[e]&&(this.handlers[e]=null)},r.prototype.forEach=function(e){o.forEach(this.handlers,function(t){null!==t&&e(t)})},e.exports=r},function(e,t,n){"use strict";function r(e){e.cancelToken&&e.cancelToken.throwIfRequested()}var o=n(2),s=n(18),i=n(19),a=n(5);e.exports=function(e){r(e),e.headers=e.headers||{},e.data=s(e.data,e.headers,e.transformRequest),e.headers=o.merge(e.headers.common||{},e.headers[e.method]||{},e.headers||{}),o.forEach(["delete","get","head","post","put","patch","common"],function(t){delete e.headers[t]});var t=e.adapter||a.adapter;return t(e).then(function(t){return r(e),t.data=s(t.data,t.headers,e.transformResponse),t},function(t){return i(t)||(r(e),t&&t.response&&(t.response.data=s(t.response.data,t.response.headers,e.transformResponse))),Promise.reject(t)})}},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t,n){return r.forEach(n,function(n){e=n(e,t)}),e}},function(e,t){"use strict";e.exports=function(e){return!(!e||!e.__CANCEL__)}},function(e,t){"use strict";e.exports=function(e){return/^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(e)}},function(e,t){"use strict";e.exports=function(e,t){return e.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,"")}},function(e,t){"use strict";function n(e){this.message=e}n.prototype.toString=function(){return"Cancel"+(this.message?": "+this.message:"")},n.prototype.__CANCEL__=!0,e.exports=n},function(e,t,n){"use strict";function r(e){if("function"!=typeof e)throw new TypeError("executor must be a function.");var t;this.promise=new Promise(function(e){t=e});var n=this;e(function(e){n.reason||(n.reason=new o(e),t(n.reason))})}var o=n(22);r.prototype.throwIfRequested=function(){if(this.reason)throw this.reason},r.source=function(){var e,t=new r(function(t){e=t});return{token:t,cancel:e}},e.exports=r},function(e,t){"use strict";e.exports=function(e){return function(t){return e.apply(null,t)}}}])});
3 | //# sourceMappingURL=axios.min.map
--------------------------------------------------------------------------------
/src/main/resources/static/js/index.js:
--------------------------------------------------------------------------------
1 | var bus = new Vue()
2 |
3 | function emitError(message) {
4 | bus.$emit('error', message)
5 | }
6 |
7 | function onError(fn) {
8 | bus.$on('error', fn)
9 | }
10 |
11 | function emitInfo(message) {
12 | bus.$emit('info', message)
13 | }
14 |
15 | function onInfo(fn) {
16 | bus.$on('info', fn)
17 | }
18 |
19 | function emitLogin() {
20 | bus.$emit('login')
21 | }
22 |
23 | function onLogin(fn) {
24 | bus.$on('login', fn)
25 | }
26 |
27 | function handleError(errorMessage) {
28 | emitError(errorMessage)
29 | }
30 |
31 | var Alert = {
32 | template: '#alert',
33 | data: function () {
34 | return {
35 | classObject: {
36 | 'is-danger': false,
37 | 'is-info': false
38 | },
39 | message: '',
40 | visible: false
41 | }
42 | },
43 | mounted: function () {
44 | onError(function (errorMessage) {
45 | this.classObject['is-danger'] = true;
46 | this.message = errorMessage
47 | this.visible = true
48 | setTimeout(this.reset.bind(this), 2000)
49 | }.bind(this))
50 |
51 | onInfo(function (message) {
52 | this.classObject['is-info'] = true;
53 | this.message = message
54 | this.visible = true
55 | setTimeout(this.reset.bind(this), 2000)
56 | }.bind(this))
57 | },
58 | methods: {
59 | reset: function () {
60 | this.visible = false
61 | this.classObject = {}
62 | }
63 | }
64 | }
65 |
66 | var Header = {
67 | template: '#header',
68 | data: function () {
69 | return {
70 | currentUser: JSON.parse(sessionStorage.getItem('currentUser'))
71 | }
72 | },
73 | mounted: function () {
74 | onLogin(function () {
75 | this.currentUser = JSON.parse(sessionStorage.getItem('currentUser'));
76 | }.bind(this))
77 | },
78 | methods: {
79 | logout: function () {
80 | this.currentUser = null
81 | sessionStorage.removeItem('token')
82 | sessionStorage.removeItem('currentUser')
83 | this.$router.push('/')
84 | }
85 | }
86 | }
87 |
88 | var PostList = {
89 | template: '#posts',
90 | data: function () {
91 | return {
92 | posts: []
93 | }
94 | },
95 | filters: {
96 | longToDate: function (long) {
97 | var date = new Date(long)
98 | return date.toLocaleString()
99 | }
100 | },
101 | mounted: function () {
102 | axios.get('/api/post').then(function (res) {
103 | if (res.data.error) {
104 | handleError(res.data.error)
105 | } else {
106 | var posts = res.data
107 | this.posts = posts.sort(function (a, b) {
108 | return b.createTime - a.createTime
109 | })
110 | }
111 | }.bind(this))
112 | },
113 | methods: {
114 | showDetail: {}
115 | }
116 | }
117 |
118 | var LoginForm = {
119 | template: '#login-form',
120 | data: function () {
121 | return {
122 | name: '',
123 | password: ''
124 | }
125 | },
126 | methods: {
127 | handleSubmit: function (e) {
128 | e.preventDefault()
129 | if (this.name === '') {
130 | handleError('用户名不能为空')
131 | return false
132 | }
133 | if (this.password === '') {
134 | handleError('密码不能为空')
135 | return false
136 | }
137 | axios.post('/api/authentication', {
138 | name: this.name,
139 | password: this.password
140 | }).then(function (res) {
141 | if (res.data.error) {
142 | handleError(res.data.error)
143 | } else {
144 | sessionStorage.setItem('token', res.data.token)
145 | sessionStorage.setItem('currentUser', JSON.stringify(res.data.user));
146 | emitLogin()
147 | this.$router.push('/')
148 | }
149 | }.bind(this))
150 | }
151 | }
152 | }
153 |
154 | var SignupForm = {
155 | template: '#signup-form',
156 | data: function () {
157 | return {
158 | name: '',
159 | password: '',
160 | passwordAgain: ''
161 | }
162 | },
163 | methods: {
164 | handleSubmit: function (e) {
165 | e.preventDefault()
166 | if (this.name === '') {
167 | handleError('用户名不能为空')
168 | return false
169 | }
170 | if (this.password === '') {
171 | handleError('密码不能为空')
172 | return false
173 | }
174 | if (this.password !== this.passwordAgain) {
175 | handleError('两次输入的密码不一致')
176 | return false
177 | }
178 | axios.post('/api/user', {
179 | name: this.name,
180 | password: this.password
181 | }).then(function (res) {
182 | if (res.data.error) {
183 | handleError(res.data.error)
184 | } else {
185 | emitInfo('注册成功,请登录')
186 | this.$router.push('/login')
187 | }
188 | }.bind(this))
189 | }
190 | }
191 | }
192 |
193 |
194 | marked.setOptions({
195 | renderer: new marked.Renderer(),
196 | gfm: true,
197 | tables: true,
198 | breaks: true,
199 | pedantic: false,
200 | sanitize: true,
201 | smartLists: true,
202 | smartypants: true
203 | });
204 |
205 | var PostDetail = {
206 | template: '#post-detail',
207 | data: function () {
208 | return {
209 | post: {
210 | content: '',
211 | author: {}
212 | }
213 | }
214 | },
215 | mounted: function () {
216 | axios.get('/api/post/' + this.$route.params.id).then(function (res) {
217 | if (res.data.error) {
218 | handleError(res.data.error)
219 | return
220 | } else {
221 | this.post = res.data
222 | }
223 | }.bind(this))
224 | },
225 | filters: {
226 | longToDate: PostList.filters.longToDate
227 | },
228 | computed: {
229 | compiledMarkdown: function () {
230 | return marked(this.post.content)
231 | }
232 | }
233 | }
234 |
235 | var NewPost = {
236 | template: '#new-post',
237 | data: function () {
238 | return {
239 | title: null,
240 | content: null
241 | }
242 |
243 | },
244 | beforeRouteEnter: function (to, from, next) {
245 | if (sessionStorage.getItem('token') === null) {
246 | next({path: 'login'})
247 | emitInfo('请先登录')
248 | } else {
249 | next()
250 | }
251 | },
252 | methods: {
253 | handleSubmit: function (e) {
254 | if (this.title === null || this.title === '') {
255 | handleError('标题不能为空')
256 | return false
257 | }
258 | if (this.content === null || this.content === '') {
259 | handleError('内容不能为空')
260 | return false
261 | }
262 | axios.post('/api/post', {
263 | title: this.title,
264 | content: this.content,
265 | }, {
266 | headers: {token: sessionStorage.getItem('token')}
267 | }
268 | ).then(function (res) {
269 | if (res.data.error) {
270 | handleError(res.data.error)
271 | return
272 | }
273 | this.$router.push('/posts/' + res.data.id)
274 | }.bind(this))
275 | }
276 | }
277 | }
278 |
279 | var routes = [
280 | {path: '/', component: PostList},
281 | {path: '/posts', component: PostList},
282 | {path: '/login', component: LoginForm},
283 | {path: '/signup', component: SignupForm},
284 | {path: '/posts/new', component: NewPost},
285 | {path: '/posts/:id', component: PostDetail},
286 | ]
287 |
288 | var router = new VueRouter({
289 | routes: routes
290 | })
291 |
292 | new Vue({
293 | router: router,
294 | components: {
295 | 'x-header': Header,
296 | 'x-alert': Alert,
297 | }
298 | }).$mount('#app')
--------------------------------------------------------------------------------
/src/main/resources/static/js/marked.min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * marked - a markdown parser
3 | * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
4 | * https://github.com/chjj/marked
5 | */
6 | (function(){var block={newline:/^\n+/,code:/^( {4}[^\n]+\n*)+/,fences:noop,hr:/^( *[-*_]){3,} *(?:\n+|$)/,heading:/^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,nptable:noop,lheading:/^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,blockquote:/^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,list:/^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:/^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,def:/^ *\[([^\]]+)\]: *([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,table:noop,paragraph:/^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,text:/^[^\n]+/};block.bullet=/(?:[*+-]|\d+\.)/;block.item=/^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;block.item=replace(block.item,"gm")(/bull/g,block.bullet)();block.list=replace(block.list)(/bull/g,block.bullet)("hr","\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))")("def","\\n+(?="+block.def.source+")")();block.blockquote=replace(block.blockquote)("def",block.def)();block._tag="(?!(?:"+"a|em|strong|small|s|cite|q|dfn|abbr|data|time|code"+"|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo"+"|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b";block.html=replace(block.html)("comment",//)("closed",/<(tag)[\s\S]+?<\/\1>/)("closing",/])*?>/)(/tag/g,block._tag)();block.paragraph=replace(block.paragraph)("hr",block.hr)("heading",block.heading)("lheading",block.lheading)("blockquote",block.blockquote)("tag","<"+block._tag)("def",block.def)();block.normal=merge({},block);block.gfm=merge({},block.normal,{fences:/^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,paragraph:/^/,heading:/^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/});block.gfm.paragraph=replace(block.paragraph)("(?!","(?!"+block.gfm.fences.source.replace("\\1","\\2")+"|"+block.list.source.replace("\\1","\\3")+"|")();block.tables=merge({},block.gfm,{nptable:/^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,table:/^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/});function Lexer(options){this.tokens=[];this.tokens.links={};this.options=options||marked.defaults;this.rules=block.normal;if(this.options.gfm){if(this.options.tables){this.rules=block.tables}else{this.rules=block.gfm}}}Lexer.rules=block;Lexer.lex=function(src,options){var lexer=new Lexer(options);return lexer.lex(src)};Lexer.prototype.lex=function(src){src=src.replace(/\r\n|\r/g,"\n").replace(/\t/g," ").replace(/\u00a0/g," ").replace(/\u2424/g,"\n");return this.token(src,true)};Lexer.prototype.token=function(src,top,bq){var src=src.replace(/^ +$/gm,""),next,loose,cap,bull,b,item,space,i,l;while(src){if(cap=this.rules.newline.exec(src)){src=src.substring(cap[0].length);if(cap[0].length>1){this.tokens.push({type:"space"})}}if(cap=this.rules.code.exec(src)){src=src.substring(cap[0].length);cap=cap[0].replace(/^ {4}/gm,"");this.tokens.push({type:"code",text:!this.options.pedantic?cap.replace(/\n+$/,""):cap});continue}if(cap=this.rules.fences.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"code",lang:cap[2],text:cap[3]||""});continue}if(cap=this.rules.heading.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"heading",depth:cap[1].length,text:cap[2]});continue}if(top&&(cap=this.rules.nptable.exec(src))){src=src.substring(cap[0].length);item={type:"table",header:cap[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:cap[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:cap[3].replace(/\n$/,"").split("\n")};for(i=0;i ?/gm,"");this.token(cap,top,true);this.tokens.push({type:"blockquote_end"});continue}if(cap=this.rules.list.exec(src)){src=src.substring(cap[0].length);bull=cap[2];this.tokens.push({type:"list_start",ordered:bull.length>1});cap=cap[0].match(this.rules.item);next=false;l=cap.length;i=0;for(;i1&&b.length>1)){src=cap.slice(i+1).join("\n")+src;i=l-1}}loose=next||/\n\n(?!\s*$)/.test(item);if(i!==l-1){next=item.charAt(item.length-1)==="\n";if(!loose)loose=next}this.tokens.push({type:loose?"loose_item_start":"list_item_start"});this.token(item,false,bq);this.tokens.push({type:"list_item_end"})}this.tokens.push({type:"list_end"});continue}if(cap=this.rules.html.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:this.options.sanitize?"paragraph":"html",pre:!this.options.sanitizer&&(cap[1]==="pre"||cap[1]==="script"||cap[1]==="style"),text:cap[0]});continue}if(!bq&&top&&(cap=this.rules.def.exec(src))){src=src.substring(cap[0].length);this.tokens.links[cap[1].toLowerCase()]={href:cap[2],title:cap[3]};continue}if(top&&(cap=this.rules.table.exec(src))){src=src.substring(cap[0].length);item={type:"table",header:cap[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:cap[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:cap[3].replace(/(?: *\| *)?\n$/,"").split("\n")};for(i=0;i])/,autolink:/^<([^ >]+(@|:\/)[^ >]+)>/,url:noop,tag:/^|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,link:/^!?\[(inside)\]\(href\)/,reflink:/^!?\[(inside)\]\s*\[([^\]]*)\]/,nolink:/^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,strong:/^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,em:/^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,code:/^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,br:/^ {2,}\n(?!\s*$)/,del:noop,text:/^[\s\S]+?(?=[\\?(?:\s+['"]([\s\S]*?)['"])?\s*/;inline.link=replace(inline.link)("inside",inline._inside)("href",inline._href)();inline.reflink=replace(inline.reflink)("inside",inline._inside)();inline.normal=merge({},inline);inline.pedantic=merge({},inline.normal,{strong:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,em:/^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/});inline.gfm=merge({},inline.normal,{escape:replace(inline.escape)("])","~|])")(),url:/^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,del:/^~~(?=\S)([\s\S]*?\S)~~/,text:replace(inline.text)("]|","~]|")("|","|https?://|")()});inline.breaks=merge({},inline.gfm,{br:replace(inline.br)("{2,}","*")(),text:replace(inline.gfm.text)("{2,}","*")()});function InlineLexer(links,options){this.options=options||marked.defaults;this.links=links;this.rules=inline.normal;this.renderer=this.options.renderer||new Renderer;this.renderer.options=this.options;if(!this.links){throw new Error("Tokens array requires a `links` property.")}if(this.options.gfm){if(this.options.breaks){this.rules=inline.breaks}else{this.rules=inline.gfm}}else if(this.options.pedantic){this.rules=inline.pedantic}}InlineLexer.rules=inline;InlineLexer.output=function(src,links,options){var inline=new InlineLexer(links,options);return inline.output(src)};InlineLexer.prototype.output=function(src){var out="",link,text,href,cap;while(src){if(cap=this.rules.escape.exec(src)){src=src.substring(cap[0].length);out+=cap[1];continue}if(cap=this.rules.autolink.exec(src)){src=src.substring(cap[0].length);if(cap[2]==="@"){text=cap[1].charAt(6)===":"?this.mangle(cap[1].substring(7)):this.mangle(cap[1]);href=this.mangle("mailto:")+text}else{text=escape(cap[1]);href=text}out+=this.renderer.link(href,null,text);continue}if(!this.inLink&&(cap=this.rules.url.exec(src))){src=src.substring(cap[0].length);text=escape(cap[1]);href=text;out+=this.renderer.link(href,null,text);continue}if(cap=this.rules.tag.exec(src)){if(!this.inLink&&/^/i.test(cap[0])){this.inLink=false}src=src.substring(cap[0].length);out+=this.options.sanitize?this.options.sanitizer?this.options.sanitizer(cap[0]):escape(cap[0]):cap[0];continue}if(cap=this.rules.link.exec(src)){src=src.substring(cap[0].length);this.inLink=true;out+=this.outputLink(cap,{href:cap[2],title:cap[3]});this.inLink=false;continue}if((cap=this.rules.reflink.exec(src))||(cap=this.rules.nolink.exec(src))){src=src.substring(cap[0].length);link=(cap[2]||cap[1]).replace(/\s+/g," ");link=this.links[link.toLowerCase()];if(!link||!link.href){out+=cap[0].charAt(0);src=cap[0].substring(1)+src;continue}this.inLink=true;out+=this.outputLink(cap,link);this.inLink=false;continue}if(cap=this.rules.strong.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.strong(this.output(cap[2]||cap[1]));continue}if(cap=this.rules.em.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.em(this.output(cap[2]||cap[1]));continue}if(cap=this.rules.code.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.codespan(escape(cap[2],true));continue}if(cap=this.rules.br.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.br();continue}if(cap=this.rules.del.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.del(this.output(cap[1]));continue}if(cap=this.rules.text.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.text(escape(this.smartypants(cap[0])));continue}if(src){throw new Error("Infinite loop on byte: "+src.charCodeAt(0))}}return out};InlineLexer.prototype.outputLink=function(cap,link){var href=escape(link.href),title=link.title?escape(link.title):null;return cap[0].charAt(0)!=="!"?this.renderer.link(href,title,this.output(cap[1])):this.renderer.image(href,title,escape(cap[1]))};InlineLexer.prototype.smartypants=function(text){if(!this.options.smartypants)return text;return text.replace(/---/g,"—").replace(/--/g,"–").replace(/(^|[-\u2014/(\[{"\s])'/g,"$1‘").replace(/'/g,"’").replace(/(^|[-\u2014/(\[{\u2018\s])"/g,"$1“").replace(/"/g,"”").replace(/\.{3}/g,"…")};InlineLexer.prototype.mangle=function(text){if(!this.options.mangle)return text;var out="",l=text.length,i=0,ch;for(;i.5){ch="x"+ch.toString(16)}out+=""+ch+";"}return out};function Renderer(options){this.options=options||{}}Renderer.prototype.code=function(code,lang,escaped){if(this.options.highlight){var out=this.options.highlight(code,lang);if(out!=null&&out!==code){escaped=true;code=out}}if(!lang){return""+(escaped?code:escape(code,true))+"\n
"}return''+(escaped?code:escape(code,true))+"\n
\n"};Renderer.prototype.blockquote=function(quote){return"\n"+quote+"
\n"};Renderer.prototype.html=function(html){return html};Renderer.prototype.heading=function(text,level,raw){return"\n"};Renderer.prototype.hr=function(){return this.options.xhtml?"
\n":"
\n"};Renderer.prototype.list=function(body,ordered){var type=ordered?"ol":"ul";return"<"+type+">\n"+body+""+type+">\n"};Renderer.prototype.listitem=function(text){return""+text+"\n"};Renderer.prototype.paragraph=function(text){return""+text+"
\n"};Renderer.prototype.table=function(header,body){return"\n"+"\n"+header+"\n"+"\n"+body+"\n"+"
\n"};Renderer.prototype.tablerow=function(content){return"\n"+content+"
\n"};Renderer.prototype.tablecell=function(content,flags){var type=flags.header?"th":"td";var tag=flags.align?"<"+type+' style="text-align:'+flags.align+'">':"<"+type+">";return tag+content+""+type+">\n"};Renderer.prototype.strong=function(text){return""+text+""};Renderer.prototype.em=function(text){return""+text+""};Renderer.prototype.codespan=function(text){return""+text+"
"};Renderer.prototype.br=function(){return this.options.xhtml?"
":"
"};Renderer.prototype.del=function(text){return""+text+""};Renderer.prototype.link=function(href,title,text){if(this.options.sanitize){try{var prot=decodeURIComponent(unescape(href)).replace(/[^\w:]/g,"").toLowerCase()}catch(e){return""}if(prot.indexOf("javascript:")===0||prot.indexOf("vbscript:")===0){return""}}var out='"+text+"";return out};Renderer.prototype.image=function(href,title,text){var out='
":">";return out};Renderer.prototype.text=function(text){return text};function Parser(options){this.tokens=[];this.token=null;this.options=options||marked.defaults;this.options.renderer=this.options.renderer||new Renderer;this.renderer=this.options.renderer;this.renderer.options=this.options}Parser.parse=function(src,options,renderer){var parser=new Parser(options,renderer);return parser.parse(src)};Parser.prototype.parse=function(src){this.inline=new InlineLexer(src.links,this.options,this.renderer);this.tokens=src.reverse();var out="";while(this.next()){out+=this.tok()}return out};Parser.prototype.next=function(){return this.token=this.tokens.pop()};Parser.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0};Parser.prototype.parseText=function(){var body=this.token.text;while(this.peek().type==="text"){body+="\n"+this.next().text}return this.inline.output(body)};Parser.prototype.tok=function(){switch(this.token.type){case"space":{return""}case"hr":{return this.renderer.hr()}case"heading":{return this.renderer.heading(this.inline.output(this.token.text),this.token.depth,this.token.text)}case"code":{return this.renderer.code(this.token.text,this.token.lang,this.token.escaped)}case"table":{var header="",body="",i,row,cell,flags,j;cell="";for(i=0;i/g,">").replace(/"/g,""").replace(/'/g,"'")}function unescape(html){return html.replace(/&([#\w]+);/g,function(_,n){n=n.toLowerCase();if(n==="colon")return":";if(n.charAt(0)==="#"){return n.charAt(1)==="x"?String.fromCharCode(parseInt(n.substring(2),16)):String.fromCharCode(+n.substring(1))}return""})}function replace(regex,opt){regex=regex.source;opt=opt||"";return function self(name,val){if(!name)return new RegExp(regex,opt);val=val.source||val;val=val.replace(/(^|[^\[])\^/g,"$1");regex=regex.replace(name,val);return self}}function noop(){}noop.exec=noop;function merge(obj){var i=1,target,key;for(;iAn error occured:
"+escape(e.message+"",true)+"
"}throw e}}marked.options=marked.setOptions=function(opt){merge(marked.defaults,opt);return marked};marked.defaults={gfm:true,tables:true,breaks:false,pedantic:false,sanitize:false,sanitizer:null,mangle:true,smartLists:false,silent:false,highlight:null,langPrefix:"lang-",smartypants:false,headerPrefix:"",renderer:new Renderer,xhtml:false};marked.Parser=Parser;marked.parser=Parser.parse;marked.Renderer=Renderer;marked.Lexer=Lexer;marked.lexer=Lexer.lex;marked.InlineLexer=InlineLexer;marked.inlineLexer=InlineLexer.output;marked.parse=marked;if(typeof module!=="undefined"&&typeof exports==="object"){module.exports=marked}else if(typeof define==="function"&&define.amd){define(function(){return marked})}else{this.marked=marked}}).call(function(){return this||(typeof window!=="undefined"?window:global)}());
--------------------------------------------------------------------------------
/src/test/java/com/hpm/blog/SpringBootBlogApplicationTests.java:
--------------------------------------------------------------------------------
1 | package com.hpm.blog;
2 |
3 | import org.junit.Test;
4 | import org.junit.runner.RunWith;
5 | import org.springframework.boot.test.context.SpringBootTest;
6 | import org.springframework.test.context.junit4.SpringRunner;
7 |
8 | @RunWith(SpringRunner.class)
9 | @SpringBootTest
10 | public class SpringBootBlogApplicationTests {
11 |
12 | @Test
13 | public void contextLoads() {
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------