├── .gitignore
├── .mvn
└── wrapper
│ ├── maven-wrapper.jar
│ └── maven-wrapper.properties
├── LICENSE
├── README.md
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
├── main
├── java
│ └── com
│ │ └── example
│ │ └── demo
│ │ ├── Application.java
│ │ ├── controller
│ │ ├── AdminController.java
│ │ ├── HelloController.java
│ │ ├── MemoController.java
│ │ └── UserController.java
│ │ ├── entity
│ │ ├── Memo.java
│ │ └── User.java
│ │ ├── repository
│ │ ├── MemoRepository.java
│ │ └── UserRepository.java
│ │ ├── security
│ │ ├── SecurityConfig.java
│ │ ├── SimpleAccessDeniedHandler.java
│ │ ├── SimpleAuthenticationEntryPoint.java
│ │ ├── SimpleAuthenticationFailureHandler.java
│ │ ├── SimpleAuthenticationSuccessHandler.java
│ │ ├── SimpleLoginUser.java
│ │ ├── SimpleTokenFilter.java
│ │ └── SimpleUserDetailsService.java
│ │ └── service
│ │ ├── MemoService.java
│ │ ├── UserService.java
│ │ └── impl
│ │ ├── MemoServiceImpl.java
│ │ └── UserServiceImpl.java
└── resources
│ ├── application.yml
│ ├── import.sql
│ └── scripts
│ └── sql
│ ├── create_database.sql
│ ├── create_memo_table.sql
│ ├── create_user.sql
│ ├── create_user_table.sql
│ ├── insert_memo_data.sql
│ └── insert_user_data.sql
└── test
├── java
└── com
│ └── example
│ └── demo
│ ├── ApplicationTests.java
│ ├── JwtTests.java
│ ├── controller
│ ├── AdminControllerTests.java
│ ├── HelloControllerIntegrationTests.java
│ ├── HelloControllerTests.java
│ ├── MemoControllerIntegrationTests.java
│ ├── MemoControllerTests.java
│ ├── UserControllerIntegrationTests.java
│ └── UserControllerTests.java
│ └── repository
│ └── UserRepositoryTests.java
└── resources
└── .gitkeep
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 |
4 | ### STS ###
5 | .apt_generated
6 | .classpath
7 | .factorypath
8 | .project
9 | .settings
10 | .springBeans
11 | .sts4-cache
12 |
13 | ### IntelliJ IDEA ###
14 | .idea
15 | *.iws
16 | *.iml
17 | *.ipr
18 |
19 | ### NetBeans ###
20 | nbproject/private/
21 | build/
22 | nbbuild/
23 | dist/
24 | nbdist/
25 | .nb-gradle/
26 |
27 | /tmp
28 | /out
29 | *.gz
30 | *.log
31 |
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubytomato/demo-security-jwt-spring2/8a2956a7be129ce93b592fe12bd1a9fefa533825/.mvn/wrapper/maven-wrapper.jar
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Watanabe Shin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Spring Security & JWT with Spring Boot 2.0 Rest API application
2 |
3 | Development environment
4 |
5 | * Java 1.8.0
6 | * Spring Boot 2.0.6
7 | * H2
8 | * java-jwt 3.4.0
9 | * Maven 3.5.4
10 |
11 | ## Build & Run
12 |
13 | using an embedded database H2.
14 |
15 | ```text
16 | mvn clean package
17 | ```
18 |
19 | ```text
20 | java -jar .\target\demo.jar
21 | ```
22 |
23 | ## test user
24 |
25 | |email |password |admin |
26 | |:----------------------|:----------------|:------|
27 | |kkamimura@example.com |iWKw06pvj |true |
28 | |rsakuma@example.com |sk10ZIaiq |false |
29 | |tyukinaga@example.com |me02yFufL |false |
30 | |zsawatari@example.com |FjqU39aia |false |
31 | |ehiyama@example.com |ruFOep18r |false |
32 |
33 |
34 | ## API
35 |
36 | ### login API
37 |
38 | 認証が成功するとトークンが取得できます。このトークンは認証が必要なAPIで使用します。
39 |
40 | ```text
41 | curl -i -X POST "http://localhost:9000/app/login" -d "email=kkamimura@example.com" -d "pass=iWKw06pvj"
42 | ```
43 |
44 | For example, the response is as follows
45 |
46 | ```text
47 | HTTP/1.1 200
48 | Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI1IiwibmJmIjoxNTMzMDA2NDc0LCJleHAiOjE1MzMwMDcwNzQsImlhdCI6MTUzMzAwNjQ3NH0.HHrg8dGkexwgw3z06n5NGa69DLzJn--PWzlBYNQ8r3KXDLsXwJKNt7qwJK3xGHc2CP0zBi2fcdjAWV-2zs68wA
49 | X-Content-Type-Options: nosniff
50 | X-XSS-Protection: 1; mode=block
51 | Cache-Control: no-cache, no-store, max-age=0, must-revalidate
52 | Pragma: no-cache
53 | Expires: 0
54 | X-Frame-Options: DENY
55 | Content-Length: 0
56 | Date: Tue, 31 Jul 2018 03:07:54 GMT
57 | ```
58 |
59 | ### No authentication required API
60 |
61 | ```text
62 | curl -i "http://localhost:9000/app/hello"
63 | ```
64 |
65 | ```text
66 | curl -i "http://localhost:9000/app/hello/{message}"
67 | ```
68 |
69 | ```text
70 | curl -i -X POST "http://localhost:9000/app/hello" -d "message=world"
71 | ```
72 |
73 | ### These APIs do not need roles
74 |
75 | But the user needs to be authenticated.
76 |
77 | ```text
78 | curl -i -H "Authorization: Bearer {TOKEN}" "http://localhost:9000/app/memo/1"
79 | ```
80 |
81 | ```text
82 | curl -i -H "Authorization: Bearer {TOKEN}" "http://localhost:9000/app/memo/list"
83 | ```
84 |
85 | ### These APIs requiring authentication and USER role
86 |
87 | ```text
88 | curl -i -H "Authorization: Bearer {TOKEN}" "http://localhost:9000/app/user"
89 | ```
90 |
91 | ```text
92 | curl -i -H "Authorization: Bearer {TOKEN}" "http://localhost:9000/app/user/echo/{message}"
93 | ```
94 |
95 | ```text
96 | curl -i -H "Authorization: Bearer {TOKEN}" -H "Content-Type:application/json" -X POST "http://localhost:9000/app/user/echo" -d "{\"message\": \"hello world\"}"
97 | ```
98 |
99 | ### These APIs requiring authentication and ADMIN role
100 |
101 | ```text
102 | curl -i -H "Authorization: Bearer {TOKEN}" "http://localhost:9000/app/admin"
103 | ```
104 |
105 | ```text
106 | curl -i -H "Authorization: Bearer {TOKEN}" "http://localhost:9000/app/admin/echo/{message}"
107 | ```
108 |
109 | ```text
110 | curl -i -H "Authorization: Bearer {TOKEN}" -H "Content-Type:application/json" -X POST "http://localhost:9000/app/admin/echo" -d "{\"message\": \"hello world\"}"
111 | ```
112 |
--------------------------------------------------------------------------------
/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 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
59 | if [ -z "$JAVA_HOME" ]; then
60 | if [ -x "/usr/libexec/java_home" ]; then
61 | export JAVA_HOME="`/usr/libexec/java_home`"
62 | else
63 | export JAVA_HOME="/Library/Java/Home"
64 | fi
65 | fi
66 | ;;
67 | esac
68 |
69 | if [ -z "$JAVA_HOME" ] ; then
70 | if [ -r /etc/gentoo-release ] ; then
71 | JAVA_HOME=`java-config --jre-home`
72 | fi
73 | fi
74 |
75 | if [ -z "$M2_HOME" ] ; then
76 | ## resolve links - $0 may be a link to maven's home
77 | PRG="$0"
78 |
79 | # need this for relative symlinks
80 | while [ -h "$PRG" ] ; do
81 | ls=`ls -ld "$PRG"`
82 | link=`expr "$ls" : '.*-> \(.*\)$'`
83 | if expr "$link" : '/.*' > /dev/null; then
84 | PRG="$link"
85 | else
86 | PRG="`dirname "$PRG"`/$link"
87 | fi
88 | done
89 |
90 | saveddir=`pwd`
91 |
92 | M2_HOME=`dirname "$PRG"`/..
93 |
94 | # make it fully qualified
95 | M2_HOME=`cd "$M2_HOME" && pwd`
96 |
97 | cd "$saveddir"
98 | # echo Using m2 at $M2_HOME
99 | fi
100 |
101 | # For Cygwin, ensure paths are in UNIX format before anything is touched
102 | if $cygwin ; then
103 | [ -n "$M2_HOME" ] &&
104 | M2_HOME=`cygpath --unix "$M2_HOME"`
105 | [ -n "$JAVA_HOME" ] &&
106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
107 | [ -n "$CLASSPATH" ] &&
108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
109 | fi
110 |
111 | # For Migwn, ensure paths are in UNIX format before anything is touched
112 | if $mingw ; then
113 | [ -n "$M2_HOME" ] &&
114 | M2_HOME="`(cd "$M2_HOME"; pwd)`"
115 | [ -n "$JAVA_HOME" ] &&
116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
117 | # TODO classpath?
118 | fi
119 |
120 | if [ -z "$JAVA_HOME" ]; then
121 | javaExecutable="`which javac`"
122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
123 | # readlink(1) is not available as standard on Solaris 10.
124 | readLink=`which readlink`
125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
126 | if $darwin ; then
127 | javaHome="`dirname \"$javaExecutable\"`"
128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
129 | else
130 | javaExecutable="`readlink -f \"$javaExecutable\"`"
131 | fi
132 | javaHome="`dirname \"$javaExecutable\"`"
133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'`
134 | JAVA_HOME="$javaHome"
135 | export JAVA_HOME
136 | fi
137 | fi
138 | fi
139 |
140 | if [ -z "$JAVACMD" ] ; then
141 | if [ -n "$JAVA_HOME" ] ; then
142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
143 | # IBM's JDK on AIX uses strange locations for the executables
144 | JAVACMD="$JAVA_HOME/jre/sh/java"
145 | else
146 | JAVACMD="$JAVA_HOME/bin/java"
147 | fi
148 | else
149 | JAVACMD="`which java`"
150 | fi
151 | fi
152 |
153 | if [ ! -x "$JAVACMD" ] ; then
154 | echo "Error: JAVA_HOME is not defined correctly." >&2
155 | echo " We cannot execute $JAVACMD" >&2
156 | exit 1
157 | fi
158 |
159 | if [ -z "$JAVA_HOME" ] ; then
160 | echo "Warning: JAVA_HOME environment variable is not set."
161 | fi
162 |
163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
164 |
165 | # traverses directory structure from process work directory to filesystem root
166 | # first directory with .mvn subdirectory is considered project base directory
167 | find_maven_basedir() {
168 |
169 | if [ -z "$1" ]
170 | then
171 | echo "Path not specified to find_maven_basedir"
172 | return 1
173 | fi
174 |
175 | basedir="$1"
176 | wdir="$1"
177 | while [ "$wdir" != '/' ] ; do
178 | if [ -d "$wdir"/.mvn ] ; then
179 | basedir=$wdir
180 | break
181 | fi
182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc)
183 | if [ -d "${wdir}" ]; then
184 | wdir=`cd "$wdir/.."; pwd`
185 | fi
186 | # end of workaround
187 | done
188 | echo "${basedir}"
189 | }
190 |
191 | # concatenates all lines of a file
192 | concat_lines() {
193 | if [ -f "$1" ]; then
194 | echo "$(tr -s '\n' ' ' < "$1")"
195 | fi
196 | }
197 |
198 | BASE_DIR=`find_maven_basedir "$(pwd)"`
199 | if [ -z "$BASE_DIR" ]; then
200 | exit 1;
201 | fi
202 |
203 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
204 | echo $MAVEN_PROJECTBASEDIR
205 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
206 |
207 | # For Cygwin, switch paths to Windows format before running java
208 | if $cygwin; then
209 | [ -n "$M2_HOME" ] &&
210 | M2_HOME=`cygpath --path --windows "$M2_HOME"`
211 | [ -n "$JAVA_HOME" ] &&
212 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
213 | [ -n "$CLASSPATH" ] &&
214 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
215 | [ -n "$MAVEN_PROJECTBASEDIR" ] &&
216 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
217 | fi
218 |
219 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
220 |
221 | exec "$JAVACMD" \
222 | $MAVEN_OPTS \
223 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
224 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
225 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
226 |
--------------------------------------------------------------------------------
/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 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
84 | @REM Fallback to current working directory if not found.
85 |
86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
88 |
89 | set EXEC_DIR=%CD%
90 | set WDIR=%EXEC_DIR%
91 | :findBaseDir
92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound
93 | cd ..
94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound
95 | set WDIR=%CD%
96 | goto findBaseDir
97 |
98 | :baseDirFound
99 | set MAVEN_PROJECTBASEDIR=%WDIR%
100 | cd "%EXEC_DIR%"
101 | goto endDetectBaseDir
102 |
103 | :baseDirNotFound
104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
105 | cd "%EXEC_DIR%"
106 |
107 | :endDetectBaseDir
108 |
109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
110 |
111 | @setlocal EnableExtensions EnableDelayedExpansion
112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
114 |
115 | :endReadAdditionalConfig
116 |
117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
118 |
119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
121 |
122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
123 | if ERRORLEVEL 1 goto error
124 | goto end
125 |
126 | :error
127 | set ERROR_CODE=1
128 |
129 | :end
130 | @endlocal & set ERROR_CODE=%ERROR_CODE%
131 |
132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending
134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
136 | :skipRcPost
137 |
138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause
140 |
141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
142 |
143 | exit /B %ERROR_CODE%
144 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | com.example
7 | demo-security-jwt-spring2
8 | 0.0.5-SNAPSHOT
9 | jar
10 |
11 | demo
12 | Demo project for Spring Boot
13 |
14 |
15 | org.springframework.boot
16 | spring-boot-starter-parent
17 | 2.0.6.RELEASE
18 |
19 |
20 |
21 |
22 | UTF-8
23 | UTF-8
24 | 1.8
25 |
26 |
27 |
28 |
29 | org.springframework.boot
30 | spring-boot-starter-web
31 |
32 |
33 | org.springframework.boot
34 | spring-boot-starter-security
35 |
36 |
37 |
38 | com.auth0
39 | java-jwt
40 | 3.4.0
41 |
42 |
43 | org.springframework.boot
44 | spring-boot-starter-data-jpa
45 |
46 |
47 | com.h2database
48 | h2
49 | runtime
50 |
51 |
58 |
59 | org.springframework.boot
60 | spring-boot-configuration-processor
61 | true
62 |
63 |
70 |
71 | org.projectlombok
72 | lombok
73 | true
74 |
75 |
76 |
77 | org.springframework.boot
78 | spring-boot-starter-test
79 | test
80 |
81 |
82 | org.springframework.security
83 | spring-security-test
84 | test
85 |
86 |
87 |
88 |
89 | demo
90 |
91 |
92 | org.apache.maven.plugins
93 | maven-compiler-plugin
94 |
95 |
96 | -Xlint:all
97 |
98 | true
99 |
100 |
101 |
102 | org.springframework.boot
103 | spring-boot-maven-plugin
104 |
105 |
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/src/main/java/com/example/demo/Application.java:
--------------------------------------------------------------------------------
1 | package com.example.demo;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class Application {
8 | public static void main(String[] args) {
9 | SpringApplication.run(Application.class, args);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/example/demo/controller/AdminController.java:
--------------------------------------------------------------------------------
1 | package com.example.demo.controller;
2 |
3 | import com.example.demo.entity.User;
4 | import com.example.demo.service.UserService;
5 | import lombok.extern.slf4j.Slf4j;
6 | import org.springframework.http.MediaType;
7 | import org.springframework.security.core.annotation.AuthenticationPrincipal;
8 | import org.springframework.web.bind.annotation.GetMapping;
9 | import org.springframework.web.bind.annotation.PathVariable;
10 | import org.springframework.web.bind.annotation.PostMapping;
11 | import org.springframework.web.bind.annotation.RequestBody;
12 | import org.springframework.web.bind.annotation.RequestMapping;
13 | import org.springframework.web.bind.annotation.RestController;
14 |
15 | import java.util.Map;
16 |
17 | @RestController
18 | @RequestMapping(path = "admin")
19 | @Slf4j
20 | public class AdminController {
21 |
22 | private final UserService userService;
23 |
24 | public AdminController(UserService userService) {
25 | this.userService = userService;
26 | }
27 |
28 | @GetMapping
29 | public String greeting(@AuthenticationPrincipal(expression = "user") User user) {
30 | log.info("access user : {}", user.toString());
31 | return "hello admin " + user.getName();
32 | }
33 |
34 | @GetMapping(path = "{name}")
35 | public String greeting(@AuthenticationPrincipal(expression = "user") User user, @PathVariable(name = "name") String name) {
36 | log.info("access user : {}", user.toString());
37 | return userService.findByName(name).map(u -> "hello " + u.getName()).orElse("unknown user");
38 | }
39 |
40 | @GetMapping(path = "echo/{message}")
41 | public String getEcho(@PathVariable(name = "message") String message) {
42 | return message.toUpperCase();
43 | }
44 |
45 | @PostMapping(path = "echo", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
46 | public String postEcho(@RequestBody Map message) {
47 | return message.toString();
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/com/example/demo/controller/HelloController.java:
--------------------------------------------------------------------------------
1 | package com.example.demo.controller;
2 |
3 | import lombok.extern.slf4j.Slf4j;
4 | import org.springframework.web.bind.annotation.GetMapping;
5 | import org.springframework.web.bind.annotation.PathVariable;
6 | import org.springframework.web.bind.annotation.PostMapping;
7 | import org.springframework.web.bind.annotation.RequestMapping;
8 | import org.springframework.web.bind.annotation.RequestParam;
9 | import org.springframework.web.bind.annotation.RestController;
10 |
11 | @RestController
12 | @RequestMapping(path = "hello")
13 | @Slf4j
14 | public class HelloController {
15 |
16 | @GetMapping
17 | public String greeting() {
18 | return "hello world";
19 | }
20 |
21 | @GetMapping(path = "{message}")
22 | public String greeting(@PathVariable(name = "message") String message) {
23 | return "hello " + message;
24 | }
25 |
26 | @PostMapping
27 | public String postGreeting(@RequestParam(name = "message") String message) {
28 | return "hello " + message;
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/example/demo/controller/MemoController.java:
--------------------------------------------------------------------------------
1 | package com.example.demo.controller;
2 |
3 | import com.example.demo.entity.Memo;
4 | import com.example.demo.service.MemoService;
5 | import lombok.extern.slf4j.Slf4j;
6 | import org.springframework.data.domain.Page;
7 | import org.springframework.data.domain.Pageable;
8 | import org.springframework.http.MediaType;
9 | import org.springframework.http.ResponseEntity;
10 | import org.springframework.web.bind.annotation.DeleteMapping;
11 | import org.springframework.web.bind.annotation.GetMapping;
12 | import org.springframework.web.bind.annotation.PathVariable;
13 | import org.springframework.web.bind.annotation.PostMapping;
14 | import org.springframework.web.bind.annotation.RequestBody;
15 | import org.springframework.web.bind.annotation.RequestMapping;
16 | import org.springframework.web.bind.annotation.RestController;
17 |
18 | import java.util.List;
19 | import java.util.Optional;
20 |
21 | @RestController
22 | @RequestMapping(path = "memo")
23 | @Slf4j
24 | public class MemoController {
25 |
26 | private final MemoService service;
27 |
28 | public MemoController(MemoService service) {
29 | this.service = service;
30 | }
31 |
32 | @GetMapping(path = "{id}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
33 | public ResponseEntity id(@PathVariable(value = "id") Long id) {
34 | Optional memo = service.findById(id);
35 | return memo.map(ResponseEntity::ok)
36 | .orElseGet(() -> ResponseEntity.notFound().build());
37 | }
38 |
39 | @GetMapping(path = "list", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
40 | public ResponseEntity> list(Pageable page) {
41 | Page memos = service.findAll(page);
42 | return ResponseEntity.ok(memos.getContent());
43 | }
44 |
45 | @PostMapping(produces = MediaType.TEXT_PLAIN_VALUE, consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
46 | public String store(@RequestBody Memo memo) {
47 | service.store(memo);
48 | return "success";
49 | }
50 |
51 | @DeleteMapping(path = "{id}", produces = MediaType.TEXT_PLAIN_VALUE, consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
52 | public String update(@PathVariable(value = "id") Long id, @RequestBody Memo memo) {
53 | service.updateById(id, memo);
54 | return "success";
55 | }
56 |
57 | @DeleteMapping(path = "{id}", produces = MediaType.TEXT_PLAIN_VALUE)
58 | public String remove(@PathVariable(value = "id") Long id) {
59 | service.removeById(id);
60 | return "success";
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/com/example/demo/controller/UserController.java:
--------------------------------------------------------------------------------
1 | package com.example.demo.controller;
2 |
3 | import com.example.demo.entity.User;
4 | import lombok.extern.slf4j.Slf4j;
5 | import org.springframework.http.MediaType;
6 | import org.springframework.security.core.annotation.AuthenticationPrincipal;
7 | import org.springframework.security.web.csrf.CsrfToken;
8 | import org.springframework.web.bind.annotation.GetMapping;
9 | import org.springframework.web.bind.annotation.PathVariable;
10 | import org.springframework.web.bind.annotation.PostMapping;
11 | import org.springframework.web.bind.annotation.RequestBody;
12 | import org.springframework.web.bind.annotation.RequestMapping;
13 | import org.springframework.web.bind.annotation.RestController;
14 |
15 | import java.util.Map;
16 |
17 | @RestController
18 | @RequestMapping(path = "user")
19 | @Slf4j
20 | public class UserController {
21 |
22 | @GetMapping
23 | public String greeting(@AuthenticationPrincipal(expression = "user") User user) {
24 | log.info("access user : {}", user.toString());
25 | return "hello " + user.getName();
26 | }
27 |
28 | @GetMapping(path = "echo/{message}")
29 | public String getEcho(@PathVariable(name = "message") String message) {
30 | return message.toUpperCase();
31 | }
32 |
33 | @PostMapping(path = "echo", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
34 | public String postEcho(@RequestBody Map message) {
35 | return message.toString();
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/com/example/demo/entity/Memo.java:
--------------------------------------------------------------------------------
1 | package com.example.demo.entity;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Builder;
5 | import lombok.Data;
6 | import lombok.NoArgsConstructor;
7 |
8 | import javax.persistence.Column;
9 | import javax.persistence.Entity;
10 | import javax.persistence.GeneratedValue;
11 | import javax.persistence.GenerationType;
12 | import javax.persistence.Id;
13 | import javax.persistence.PrePersist;
14 | import javax.persistence.PreUpdate;
15 | import javax.persistence.Table;
16 | import java.io.Serializable;
17 | import java.time.LocalDateTime;
18 |
19 | @Entity
20 | @Table(name = "memo")
21 | @Data
22 | @Builder
23 | @AllArgsConstructor
24 | @NoArgsConstructor
25 | public class Memo implements Serializable {
26 |
27 | private static final long serialVersionUID = 8836618159506901418L;
28 |
29 | @Id
30 | @GeneratedValue(strategy = GenerationType.IDENTITY)
31 | private Long id;
32 | @Column(name = "title", nullable = false, length = 255)
33 | private String title;
34 | @Column(name = "description", nullable = false)
35 | private String description;
36 | @Column(name = "done", nullable = false)
37 | private Boolean done;
38 | @Column(name = "updated", nullable = false)
39 | private LocalDateTime updated;
40 |
41 | public static Memo of(String title, String description) {
42 | return Memo.of(null, title, description);
43 | }
44 |
45 | public static Memo of(Long id, String title, String description) {
46 | return Memo.builder()
47 | .id(id)
48 | .title(title)
49 | .description(description)
50 | .done(false)
51 | .updated(LocalDateTime.now())
52 | .build();
53 | }
54 |
55 | public void merge(Memo memo) {
56 | if (memo.title != null && memo.title.length() > 0) {
57 | title = memo.title;
58 | }
59 | if (memo.description != null && memo.description.length() > 0) {
60 | description = memo.description;
61 | }
62 | if (memo.done != null) {
63 | done = memo.done;
64 | }
65 | }
66 |
67 | @PrePersist
68 | private void prePersist() {
69 | done = false;
70 | updated = LocalDateTime.now();
71 | }
72 |
73 | @PreUpdate
74 | private void preUpdate() {
75 | updated = LocalDateTime.now();
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/src/main/java/com/example/demo/entity/User.java:
--------------------------------------------------------------------------------
1 | package com.example.demo.entity;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Builder;
5 | import lombok.Data;
6 | import lombok.NoArgsConstructor;
7 |
8 | import javax.persistence.Column;
9 | import javax.persistence.Entity;
10 | import javax.persistence.GeneratedValue;
11 | import javax.persistence.GenerationType;
12 | import javax.persistence.Id;
13 | import javax.persistence.Table;
14 | import java.io.Serializable;
15 |
16 | @Entity
17 | @Table(name = "user")
18 | @Data
19 | @Builder
20 | @AllArgsConstructor
21 | @NoArgsConstructor
22 | public class User implements Serializable {
23 |
24 | private static final long serialVersionUID = -2315659388348422700L;
25 |
26 | @Id
27 | @GeneratedValue(strategy = GenerationType.IDENTITY)
28 | private Long id;
29 | @Column(name = "name", nullable = false, length = 128)
30 | private String name;
31 | @Column(name = "password", nullable = false, length = 255)
32 | private String password;
33 | @Column(name = "email", nullable = false, unique = true, length = 255)
34 | private String email;
35 | @Column(name = "admin_flag", nullable = false)
36 | private Boolean admin;
37 |
38 | public static User of(String name, String password, String email) {
39 | return User.builder().name(name).password(password).email(email).admin(false).build();
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/com/example/demo/repository/MemoRepository.java:
--------------------------------------------------------------------------------
1 | package com.example.demo.repository;
2 |
3 | import com.example.demo.entity.Memo;
4 | import org.springframework.data.jpa.repository.JpaRepository;
5 |
6 | public interface MemoRepository extends JpaRepository {
7 | }
--------------------------------------------------------------------------------
/src/main/java/com/example/demo/repository/UserRepository.java:
--------------------------------------------------------------------------------
1 | package com.example.demo.repository;
2 |
3 | import com.example.demo.entity.User;
4 | import org.springframework.data.jpa.repository.JpaRepository;
5 |
6 | import java.util.Optional;
7 |
8 | public interface UserRepository extends JpaRepository {
9 | Optional findByEmail(String email);
10 |
11 | Optional findFirstByName(String name);
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/example/demo/security/SecurityConfig.java:
--------------------------------------------------------------------------------
1 | package com.example.demo.security;
2 |
3 | import com.example.demo.repository.UserRepository;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.beans.factory.annotation.Value;
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
8 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
9 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
10 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
11 | import org.springframework.security.config.http.SessionCreationPolicy;
12 | import org.springframework.security.core.userdetails.UserDetailsService;
13 | import org.springframework.security.crypto.factory.PasswordEncoderFactories;
14 | import org.springframework.security.crypto.password.PasswordEncoder;
15 | import org.springframework.security.web.AuthenticationEntryPoint;
16 | import org.springframework.security.web.access.AccessDeniedHandler;
17 | import org.springframework.security.web.authentication.AuthenticationFailureHandler;
18 | import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
19 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
20 | import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler;
21 | import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
22 | import org.springframework.web.filter.GenericFilterBean;
23 |
24 | @EnableWebSecurity
25 | public class SecurityConfig extends WebSecurityConfigurerAdapter {
26 |
27 | @Autowired
28 | private UserRepository userRepository;
29 | @Value("${security.secret-key:secret}")
30 | private String secretKey = "secret";
31 |
32 | @Override
33 | protected void configure(HttpSecurity http) throws Exception {
34 | // @formatter:off
35 | http
36 | // AUTHORIZE
37 | .authorizeRequests()
38 | .mvcMatchers("/hello/**")
39 | .permitAll()
40 | .mvcMatchers("/user/**")
41 | .hasRole("USER")
42 | .mvcMatchers("/admin/**")
43 | .hasRole("ADMIN")
44 | .anyRequest()
45 | .authenticated()
46 | .and()
47 | // EXCEPTION
48 | .exceptionHandling()
49 | .authenticationEntryPoint(authenticationEntryPoint())
50 | .accessDeniedHandler(accessDeniedHandler())
51 | .and()
52 | // LOGIN
53 | .formLogin()
54 | .loginProcessingUrl("/login").permitAll()
55 | .usernameParameter("email")
56 | .passwordParameter("pass")
57 | .successHandler(authenticationSuccessHandler())
58 | .failureHandler(authenticationFailureHandler())
59 | .and()
60 | // LOGOUT
61 | .logout()
62 | .logoutUrl("/logout")
63 | .logoutSuccessHandler(logoutSuccessHandler())
64 | .and()
65 | // CSRF
66 | .csrf()
67 | .disable()
68 | // AUTHORIZE
69 | .addFilterBefore(tokenFilter(), UsernamePasswordAuthenticationFilter.class)
70 | // SESSION
71 | .sessionManagement()
72 | .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
73 | ;
74 | // @formatter:on
75 | }
76 |
77 | @Autowired
78 | public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
79 | auth.eraseCredentials(true)
80 | .userDetailsService(simpleUserDetailsService())
81 | .passwordEncoder(passwordEncoder());
82 | }
83 |
84 | @Bean("simpleUserDetailsService")
85 | UserDetailsService simpleUserDetailsService() {
86 | return new SimpleUserDetailsService(userRepository);
87 | }
88 |
89 | @Bean
90 | PasswordEncoder passwordEncoder() {
91 | return PasswordEncoderFactories.createDelegatingPasswordEncoder();
92 | }
93 |
94 | GenericFilterBean tokenFilter() {
95 | return new SimpleTokenFilter(userRepository, secretKey);
96 | }
97 |
98 | AuthenticationEntryPoint authenticationEntryPoint() {
99 | return new SimpleAuthenticationEntryPoint();
100 | }
101 |
102 | AccessDeniedHandler accessDeniedHandler() {
103 | return new SimpleAccessDeniedHandler();
104 | }
105 |
106 | AuthenticationSuccessHandler authenticationSuccessHandler() {
107 | return new SimpleAuthenticationSuccessHandler(secretKey);
108 | }
109 |
110 | AuthenticationFailureHandler authenticationFailureHandler() {
111 | return new SimpleAuthenticationFailureHandler();
112 | }
113 |
114 | LogoutSuccessHandler logoutSuccessHandler() {
115 | return new HttpStatusReturningLogoutSuccessHandler();
116 | }
117 |
118 | }
119 |
--------------------------------------------------------------------------------
/src/main/java/com/example/demo/security/SimpleAccessDeniedHandler.java:
--------------------------------------------------------------------------------
1 | package com.example.demo.security;
2 |
3 | import lombok.extern.slf4j.Slf4j;
4 | import org.springframework.http.HttpStatus;
5 | import org.springframework.security.access.AccessDeniedException;
6 | import org.springframework.security.access.AuthorizationServiceException;
7 | import org.springframework.security.web.access.AccessDeniedHandler;
8 | import org.springframework.security.web.csrf.CsrfException;
9 |
10 | import javax.servlet.http.HttpServletRequest;
11 | import javax.servlet.http.HttpServletResponse;
12 | import java.io.IOException;
13 |
14 | /**
15 | * アクセスするリソースの認可に失敗した時の処理
16 | */
17 | @Slf4j
18 | public class SimpleAccessDeniedHandler implements AccessDeniedHandler {
19 |
20 | public SimpleAccessDeniedHandler() {
21 | }
22 |
23 | @Override
24 | public void handle(HttpServletRequest request,
25 | HttpServletResponse response,
26 | AccessDeniedException exception) throws IOException {
27 | if (response.isCommitted()) {
28 | log.info("Response has already been committed.");
29 | return;
30 | }
31 | dump(exception);
32 | response.sendError(HttpStatus.FORBIDDEN.value(), HttpStatus.FORBIDDEN.getReasonPhrase());
33 | }
34 |
35 | private void dump(AccessDeniedException e) {
36 | if (e instanceof AuthorizationServiceException) {
37 | log.debug("AuthorizationServiceException : {}", e.getMessage());
38 | } else if (e instanceof CsrfException) {
39 | log.debug("org.springframework.security.web.csrf.CsrfException : {}", e.getMessage());
40 | } else if (e instanceof org.springframework.security.web.server.csrf.CsrfException) {
41 | log.debug("org.springframework.security.web.server.csrf.CsrfException : {}", e.getMessage());
42 | } else {
43 | log.debug("AccessDeniedException : {}", e.getMessage());
44 | }
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/com/example/demo/security/SimpleAuthenticationEntryPoint.java:
--------------------------------------------------------------------------------
1 | package com.example.demo.security;
2 |
3 | import lombok.extern.slf4j.Slf4j;
4 | import org.springframework.http.HttpStatus;
5 | import org.springframework.security.authentication.AccountExpiredException;
6 | import org.springframework.security.authentication.BadCredentialsException;
7 | import org.springframework.security.authentication.CredentialsExpiredException;
8 | import org.springframework.security.authentication.DisabledException;
9 | import org.springframework.security.authentication.LockedException;
10 | import org.springframework.security.core.AuthenticationException;
11 | import org.springframework.security.web.AuthenticationEntryPoint;
12 | import org.springframework.security.web.authentication.session.SessionAuthenticationException;
13 |
14 | import javax.servlet.http.HttpServletRequest;
15 | import javax.servlet.http.HttpServletResponse;
16 | import java.io.IOException;
17 |
18 | /**
19 | * 認証が必要なリソースに未認証でアクセスしたときの処理
20 | */
21 | @Slf4j
22 | public class SimpleAuthenticationEntryPoint implements AuthenticationEntryPoint {
23 |
24 | public SimpleAuthenticationEntryPoint() {
25 | }
26 |
27 | @Override
28 | public void commence(HttpServletRequest request,
29 | HttpServletResponse response,
30 | AuthenticationException exception) throws IOException {
31 | if (response.isCommitted()) {
32 | log.info("Response has already been committed.");
33 | return;
34 | }
35 | dump(exception);
36 | response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
37 | }
38 |
39 | private void dump(AuthenticationException e) {
40 | if (e instanceof BadCredentialsException) {
41 | log.debug("BadCredentialsException : {}", e.getMessage());
42 | } else if (e instanceof LockedException) {
43 | log.debug("LockedException : {}", e.getMessage());
44 | } else if (e instanceof DisabledException) {
45 | log.debug("DisabledException : {}", e.getMessage());
46 | } else if (e instanceof AccountExpiredException) {
47 | log.debug("AccountExpiredException : {}", e.getMessage());
48 | } else if (e instanceof CredentialsExpiredException) {
49 | log.debug("CredentialsExpiredException : {}", e.getMessage());
50 | } else if (e instanceof SessionAuthenticationException) {
51 | log.debug("SessionAuthenticationException : {}", e.getMessage());
52 | } else {
53 | log.debug("AuthenticationException : {}", e.getMessage());
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/com/example/demo/security/SimpleAuthenticationFailureHandler.java:
--------------------------------------------------------------------------------
1 | package com.example.demo.security;
2 |
3 | import lombok.extern.slf4j.Slf4j;
4 | import org.springframework.http.HttpStatus;
5 | import org.springframework.security.core.AuthenticationException;
6 | import org.springframework.security.web.authentication.AuthenticationFailureHandler;
7 |
8 | import javax.servlet.ServletException;
9 | import javax.servlet.http.HttpServletRequest;
10 | import javax.servlet.http.HttpServletResponse;
11 | import java.io.IOException;
12 |
13 | /**
14 | * 認証が失敗した時の処理
15 | */
16 | @Slf4j
17 | public class SimpleAuthenticationFailureHandler implements AuthenticationFailureHandler {
18 |
19 | public SimpleAuthenticationFailureHandler() {
20 | }
21 |
22 | @Override
23 | public void onAuthenticationFailure(HttpServletRequest request,
24 | HttpServletResponse response,
25 | AuthenticationException exception) throws IOException, ServletException {
26 | if (response.isCommitted()) {
27 | log.info("Response has already been committed.");
28 | return;
29 | }
30 | response.sendError(HttpStatus.FORBIDDEN.value(), HttpStatus.FORBIDDEN.getReasonPhrase());
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/com/example/demo/security/SimpleAuthenticationSuccessHandler.java:
--------------------------------------------------------------------------------
1 | package com.example.demo.security;
2 |
3 | import com.auth0.jwt.JWT;
4 | import com.auth0.jwt.algorithms.Algorithm;
5 | import lombok.extern.slf4j.Slf4j;
6 | import org.springframework.http.HttpStatus;
7 | import org.springframework.security.core.Authentication;
8 | import org.springframework.security.web.WebAttributes;
9 | import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
10 |
11 | import javax.servlet.http.HttpServletRequest;
12 | import javax.servlet.http.HttpServletResponse;
13 | import javax.servlet.http.HttpSession;
14 | import java.util.Date;
15 | import java.util.Objects;
16 | import java.util.concurrent.TimeUnit;
17 |
18 | /**
19 | * 認証が成功した時の処理
20 | */
21 | @Slf4j
22 | public class SimpleAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
23 |
24 | final private Algorithm algorithm;
25 |
26 | public SimpleAuthenticationSuccessHandler(String secretKey) {
27 | Objects.requireNonNull(secretKey, "secret key must be not null");
28 | this.algorithm = Algorithm.HMAC512(secretKey);
29 | }
30 |
31 | private static final Long EXPIRATION_TIME = TimeUnit.MINUTES.toMillis(10L);
32 |
33 | @Override
34 | public void onAuthenticationSuccess(HttpServletRequest request,
35 | HttpServletResponse response,
36 | Authentication auth) {
37 | if (response.isCommitted()) {
38 | log.info("Response has already been committed.");
39 | return;
40 | }
41 | setToken(response, generateToken(auth));
42 | response.setStatus(HttpStatus.OK.value());
43 | clearAuthenticationAttributes(request);
44 | }
45 |
46 | private String generateToken(Authentication auth) {
47 | SimpleLoginUser loginUser = (SimpleLoginUser) auth.getPrincipal();
48 | Date issuedAt = new Date();
49 | Date notBefore = new Date(issuedAt.getTime());
50 | Date expiresAt = new Date(issuedAt.getTime() + EXPIRATION_TIME);
51 | String token = JWT.create()
52 | .withIssuedAt(issuedAt)
53 | .withNotBefore(notBefore)
54 | .withExpiresAt(expiresAt)
55 | .withSubject(loginUser.getUser().getId().toString())
56 | .sign(this.algorithm);
57 | log.debug("generate token : {}", token);
58 | return token;
59 | }
60 |
61 | private void setToken(HttpServletResponse response, String token) {
62 | response.setHeader("Authorization", String.format("Bearer %s", token));
63 | }
64 |
65 | /**
66 | * Removes temporary authentication-related data which may have been stored in the
67 | * session during the authentication process.
68 | */
69 | private void clearAuthenticationAttributes(HttpServletRequest request) {
70 | HttpSession session = request.getSession(false);
71 | if (session == null) {
72 | return;
73 | }
74 | session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/src/main/java/com/example/demo/security/SimpleLoginUser.java:
--------------------------------------------------------------------------------
1 | package com.example.demo.security;
2 |
3 | import com.example.demo.entity.User;
4 | import org.springframework.security.core.GrantedAuthority;
5 | import org.springframework.security.core.authority.AuthorityUtils;
6 |
7 | import java.util.List;
8 |
9 | public class SimpleLoginUser extends org.springframework.security.core.userdetails.User {
10 |
11 | // Userエンティティ
12 | private com.example.demo.entity.User user;
13 |
14 | public User getUser() {
15 | return user;
16 | }
17 |
18 | public SimpleLoginUser(User user) {
19 | super(user.getName(), user.getPassword(), determineRoles(user.getAdmin()));
20 | this.user = user;
21 | }
22 |
23 | private static final List USER_ROLES = AuthorityUtils.createAuthorityList("ROLE_USER");
24 | private static final List ADMIN_ROLES = AuthorityUtils.createAuthorityList("ROLE_ADMIN", "ROLE_USER");
25 |
26 | private static List determineRoles(boolean isAdmin) {
27 | return isAdmin ? ADMIN_ROLES : USER_ROLES;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/com/example/demo/security/SimpleTokenFilter.java:
--------------------------------------------------------------------------------
1 | package com.example.demo.security;
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.JWTVerificationException;
7 | import com.auth0.jwt.interfaces.DecodedJWT;
8 | import com.example.demo.repository.UserRepository;
9 | import lombok.extern.slf4j.Slf4j;
10 | import org.springframework.http.HttpStatus;
11 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
12 | import org.springframework.security.core.context.SecurityContextHolder;
13 | import org.springframework.web.filter.GenericFilterBean;
14 |
15 | import javax.servlet.FilterChain;
16 | import javax.servlet.ServletException;
17 | import javax.servlet.ServletRequest;
18 | import javax.servlet.ServletResponse;
19 | import javax.servlet.http.HttpServletRequest;
20 | import javax.servlet.http.HttpServletResponse;
21 | import java.io.IOException;
22 | import java.util.Objects;
23 |
24 | @Slf4j
25 | public class SimpleTokenFilter extends GenericFilterBean {
26 |
27 | final private UserRepository userRepository;
28 | final private Algorithm algorithm;
29 |
30 | public SimpleTokenFilter(UserRepository userRepository, String secretKey) {
31 | Objects.requireNonNull(secretKey, "secret key must be not null");
32 | this.userRepository = userRepository;
33 | this.algorithm = Algorithm.HMAC512(secretKey);
34 | }
35 |
36 | @Override
37 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
38 | String token = resolveToken(request);
39 | if (token == null) {
40 | filterChain.doFilter(request, response);
41 | return;
42 | }
43 |
44 | try {
45 | authentication(verifyToken(token));
46 | } catch (JWTVerificationException e) {
47 | log.error("verify token error", e);
48 | SecurityContextHolder.clearContext();
49 | ((HttpServletResponse) response).sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
50 | }
51 | filterChain.doFilter(request, response);
52 | }
53 |
54 | private String resolveToken(ServletRequest request) {
55 | String token = ((HttpServletRequest) request).getHeader("Authorization");
56 | if (token == null || !token.startsWith("Bearer ")) {
57 | return null;
58 | }
59 | // remove "Bearer "
60 | return token.substring(7);
61 | }
62 |
63 | private DecodedJWT verifyToken(String token) {
64 | JWTVerifier verifier = JWT.require(algorithm).build();
65 | return verifier.verify(token);
66 | }
67 |
68 | private void authentication(DecodedJWT jwt) {
69 | Long userId = Long.valueOf(jwt.getSubject());
70 | userRepository.findById(userId).ifPresent(user -> {
71 | SimpleLoginUser simpleLoginUser = new SimpleLoginUser(user);
72 | SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(simpleLoginUser, null, simpleLoginUser.getAuthorities()));
73 | });
74 | }
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/src/main/java/com/example/demo/security/SimpleUserDetailsService.java:
--------------------------------------------------------------------------------
1 | package com.example.demo.security;
2 |
3 | import com.example.demo.repository.UserRepository;
4 | import org.springframework.security.core.userdetails.UserDetails;
5 | import org.springframework.security.core.userdetails.UserDetailsService;
6 | import org.springframework.security.core.userdetails.UsernameNotFoundException;
7 |
8 | /**
9 | * Userエンティティ
10 | */
11 | public class SimpleUserDetailsService implements UserDetailsService {
12 |
13 | private final UserRepository userRepository;
14 |
15 | SimpleUserDetailsService(UserRepository userRepository) {
16 | this.userRepository = userRepository;
17 | }
18 |
19 | @Override
20 | public UserDetails loadUserByUsername(final String email) {
21 | // emailでデータベースからユーザーエンティティを検索する
22 | return userRepository.findByEmail(email)
23 | .map(SimpleLoginUser::new)
24 | .orElseThrow(() -> new UsernameNotFoundException("user not found"));
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/com/example/demo/service/MemoService.java:
--------------------------------------------------------------------------------
1 | package com.example.demo.service;
2 |
3 | import com.example.demo.entity.Memo;
4 | import org.springframework.data.domain.Page;
5 | import org.springframework.data.domain.Pageable;
6 |
7 | import java.util.Optional;
8 |
9 | public interface MemoService {
10 | Optional findById(Long id);
11 |
12 | Page findAll(Pageable page);
13 |
14 | void store(Memo memo);
15 |
16 | void updateById(Long id, Memo memo);
17 |
18 | void removeById(Long id);
19 | }
--------------------------------------------------------------------------------
/src/main/java/com/example/demo/service/UserService.java:
--------------------------------------------------------------------------------
1 | package com.example.demo.service;
2 |
3 | import com.example.demo.entity.User;
4 |
5 | import java.util.Optional;
6 |
7 | public interface UserService {
8 | Optional findByName(String name);
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/com/example/demo/service/impl/MemoServiceImpl.java:
--------------------------------------------------------------------------------
1 | package com.example.demo.service.impl;
2 |
3 | import com.example.demo.entity.Memo;
4 | import com.example.demo.repository.MemoRepository;
5 | import com.example.demo.service.MemoService;
6 | import lombok.extern.slf4j.Slf4j;
7 | import org.springframework.data.domain.Page;
8 | import org.springframework.data.domain.Pageable;
9 | import org.springframework.stereotype.Service;
10 | import org.springframework.transaction.annotation.Transactional;
11 |
12 | import java.util.Optional;
13 |
14 | @Service
15 | @Slf4j
16 | public class MemoServiceImpl implements MemoService {
17 |
18 | private final MemoRepository repository;
19 |
20 | public MemoServiceImpl(MemoRepository repository) {
21 | this.repository = repository;
22 | }
23 |
24 | @Transactional(readOnly = true)
25 | @Override
26 | public Optional findById(Long id) {
27 | return repository.findById(id);
28 | }
29 |
30 | @Transactional(readOnly = true)
31 | @Override
32 | public Page findAll(Pageable page) {
33 | return repository.findAll(page);
34 | }
35 |
36 | @Transactional(timeout = 10)
37 | @Override
38 | public void store(Memo memo) {
39 | repository.save(memo);
40 | }
41 |
42 | @Transactional(timeout = 10)
43 | @Override
44 | public void updateById(Long id, Memo memo) {
45 | repository.findById(id).ifPresent(targetMemo -> targetMemo.merge(memo));
46 | }
47 |
48 | @Transactional(timeout = 10)
49 | @Override
50 | public void removeById(Long id) {
51 | repository.deleteById(id);
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/com/example/demo/service/impl/UserServiceImpl.java:
--------------------------------------------------------------------------------
1 | package com.example.demo.service.impl;
2 |
3 | import com.example.demo.entity.User;
4 | import com.example.demo.repository.UserRepository;
5 | import com.example.demo.service.UserService;
6 | import lombok.extern.slf4j.Slf4j;
7 | import org.springframework.stereotype.Service;
8 |
9 | import java.util.Objects;
10 | import java.util.Optional;
11 |
12 | @Service
13 | @Slf4j
14 | public class UserServiceImpl implements UserService {
15 |
16 | private final UserRepository userRepository;
17 |
18 | public UserServiceImpl(UserRepository userRepository) {
19 | this.userRepository = userRepository;
20 | }
21 |
22 | @Override
23 | public Optional findByName(String name) {
24 | Objects.requireNonNull(name, "name must be not null");
25 | return userRepository.findFirstByName(name);
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | application:
3 | name: Spring Security & JWT with spring-boot 2.0
4 | # OUTPUT
5 | output:
6 | ansi:
7 | enabled: detect
8 | # DATASOURCE
9 | datasource:
10 | url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;
11 | username: sa
12 | password:
13 | # url: jdbc:mysql://localhost:3306/demo3_db?useSSL=false
14 | # username: demo_user
15 | # password: demo_pass
16 | # driver-class-name: com.mysql.jdbc.Driver
17 | # JPA
18 | jpa:
19 | open-in-view: true
20 | generate-ddl: true
21 | hibernate:
22 | ddl-auto: create-drop
23 | properties:
24 | hibernate:
25 | show-sql: true
26 | format_sql: true
27 | use_sql_comments: true
28 | generate_statistics: false
29 | # JACKSON
30 | jackson:
31 | serialization:
32 | indent-output: true
33 | write-dates-as-timestamps: false
34 | write-durations-as-timestamps: true
35 | # DEVTOOLS
36 | # devtools:
37 | # livereload:
38 | # enabled: false
39 | # restart:
40 | # log-condition-evaluation-delta: true
41 | # enabled: true
42 | security:
43 | secret-key: GkD0wnqHaN6y5peyf0
44 |
45 | server:
46 | port: 9000
47 | servlet:
48 | context-path: /app
49 |
50 | logging.file: demo.log
51 | logging:
52 | file:
53 | max-size: 50MB
54 | max-history: 10
55 | level:
56 | root: info
57 | org.springframework: info
58 | org.springframework.security: debug
59 | org.hibernate: info
60 | org.hibernate.SQL: debug
61 | org.hibernate.type.descriptor.sql.BasicBinder: trace
62 | com.example.demo: debug
63 |
--------------------------------------------------------------------------------
/src/main/resources/import.sql:
--------------------------------------------------------------------------------
1 | INSERT INTO memo (id, title, description, done, updated) VALUES (1, 'memo shopping', 'memo1 description', false, '2018-01-04 12:01:00');
2 | INSERT INTO memo (id, title, description, done, updated) VALUES (2, 'memo job', 'memo2 description', false, '2018-01-04 13:02:10');
3 | INSERT INTO memo (id, title, description, done, updated) VALUES (3, 'memo private', 'memo3 description', false, '2018-01-04 14:03:21');
4 | INSERT INTO memo (id, title, description, done, updated) VALUES (4, 'memo job', 'memo4 description', false, '2018-01-04 15:04:32');
5 | INSERT INTO memo (id, title, description, done, updated) VALUES (5, 'memo private', 'memo5 description', false, '2018-01-04 16:05:43');
6 | INSERT INTO memo (id, title, description, done, updated) VALUES (6, 'memo travel', 'memo6 description', false, '2018-01-04 17:06:54');
7 | INSERT INTO memo (id, title, description, done, updated) VALUES (7, 'memo travel', 'memo7 description', false, '2018-01-04 18:07:05');
8 | INSERT INTO memo (id, title, description, done, updated) VALUES (8, 'memo shopping', 'memo8 description', false, '2018-01-04 19:08:16');
9 | INSERT INTO memo (id, title, description, done, updated) VALUES (9, 'memo private', 'memo9 description', false, '2018-01-04 20:09:27');
10 | INSERT INTO memo (id, title, description, done, updated) VALUES (10,'memo hospital', 'memoA description', false, '2018-01-04 21:10:38');
11 |
12 | INSERT INTO `user` (id, `name`, password, email, admin_flag) VALUES (1, 'kamimura', '{bcrypt}$2a$10$yiIGwxNPWwJ3CZ0SGAq3i.atLYrQNhzTyep1ALi6dbax1b1R2Y.cG', 'kkamimura@example.com', TRUE);
13 | INSERT INTO `user` (id, `name`, password, email, admin_flag) VALUES (2, 'sakuma', '{bcrypt}$2a$10$9jo/FSVljst5xJjuw9eyoumx2iVCUA.uBkUKeBo748bUIaPjypbte', 'rsakuma@example.com', FALSE);
14 | INSERT INTO `user` (id, `name`, password, email, admin_flag) VALUES (3, 'yukinaga', '{bcrypt}$2a$10$1OXUbgiuuIi3SOO3t.jyZOEY66ELL03dRcGpAKWql8HBXOag4YZ8q', 'tyukinaga@example.com', FALSE);
15 | INSERT INTO `user` (id, `name`, password, email, admin_flag) VALUES (4, 'sawatari', '{pbkdf2}0963ebe5b7508e9f0de55e7480ee7b87c623754ea18a94f25a20cc213a6341695d6ad38d18ff8f25', 'zsawatari@example.com', TRUE);
16 | INSERT INTO `user` (id, `name`, password, email, admin_flag) VALUES (5, 'hiyama', '{pbkdf2}998f1e8af4662f9c7e44bad5af69e916f0ab6cf6af1a1a38b0e667f5f7b9f5bb0d3700e2eacfcf72', 'ehiyama@example.com', FALSE);
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/main/resources/scripts/sql/create_database.sql:
--------------------------------------------------------------------------------
1 | CREATE DATABASE IF NOT EXISTS demo3_db
2 | CHARACTER SET = utf8mb4
3 | COLLATE = utf8mb4_general_ci;
4 |
--------------------------------------------------------------------------------
/src/main/resources/scripts/sql/create_memo_table.sql:
--------------------------------------------------------------------------------
1 | DROP TABLE IF EXISTS memo;
2 |
3 | CREATE TABLE IF NOT EXISTS memo (
4 | id BIGINT AUTO_INCREMENT,
5 | title VARCHAR(255) NOT NULL,
6 | description TEXT NOT NULL,
7 | done BOOLEAN NOT NULL DEFAULT FALSE,
8 | updated TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
9 | PRIMARY KEY (id)
10 | )
11 | ENGINE = INNODB,
12 | CHARACTER SET = utf8mb4,
13 | COLLATE utf8mb4_general_ci;
14 |
--------------------------------------------------------------------------------
/src/main/resources/scripts/sql/create_user.sql:
--------------------------------------------------------------------------------
1 | CREATE USER IF NOT EXISTS 'demo_user'@'localhost'
2 | IDENTIFIED BY 'demo_pass'
3 | PASSWORD EXPIRE NEVER;
4 |
5 | GRANT ALL ON demo3_db.* TO 'demo_user'@'localhost';
--------------------------------------------------------------------------------
/src/main/resources/scripts/sql/create_user_table.sql:
--------------------------------------------------------------------------------
1 | DROP TABLE IF EXISTS `user`;
2 |
3 | CREATE TABLE IF NOT EXISTS `user` (
4 | id BIGINT AUTO_INCREMENT,
5 | `name` VARCHAR(128) NOT NULL,
6 | password VARCHAR(255) NOT NULL,
7 | email VARCHAR(255) NOT NULL,
8 | admin_flag BOOLEAN NOT NULL DEFAULT FALSE,
9 | PRIMARY KEY (id),
10 | UNIQUE KEY (email)
11 | )
12 | ENGINE = INNODB,
13 | CHARACTER SET = utf8mb4,
14 | COLLATE utf8mb4_general_ci;
15 |
--------------------------------------------------------------------------------
/src/main/resources/scripts/sql/insert_memo_data.sql:
--------------------------------------------------------------------------------
1 | INSERT INTO memo (id, title, description, done, updated) VALUES
2 | (1, 'memo shopping', 'memo1 description', false, '2018-01-04 12:01:00'),
3 | (2, 'memo job', 'memo2 description', false, '2018-01-04 13:02:10'),
4 | (3, 'memo private', 'memo3 description', false, '2018-01-04 14:03:21'),
5 | (4, 'memo job', 'memo4 description', false, '2018-01-04 15:04:32'),
6 | (5, 'memo private', 'memo5 description', false, '2018-01-04 16:05:43'),
7 | (6, 'memo travel', 'memo6 description', false, '2018-01-04 17:06:54'),
8 | (7, 'memo travel', 'memo7 description', false, '2018-01-04 18:07:05'),
9 | (8, 'memo shopping', 'memo8 description', false, '2018-01-04 19:08:16'),
10 | (9, 'memo private', 'memo9 description', false, '2018-01-04 20:09:27'),
11 | (10,'memo hospital', 'memoA description', false, '2018-01-04 21:10:38')
12 | ;
13 |
--------------------------------------------------------------------------------
/src/main/resources/scripts/sql/insert_user_data.sql:
--------------------------------------------------------------------------------
1 | INSERT INTO `user` (id, `name`, password, email, admin_flag) VALUES
2 | (1, 'kamimura', '{bcrypt}$2a$10$yiIGwxNPWwJ3CZ0SGAq3i.atLYrQNhzTyep1ALi6dbax1b1R2Y.cG', 'kkamimura@example.com', TRUE),
3 | (2, 'sakuma', '{bcrypt}$2a$10$9jo/FSVljst5xJjuw9eyoumx2iVCUA.uBkUKeBo748bUIaPjypbte', 'rsakuma@example.com', FALSE),
4 | (3, 'yukinaga', '{bcrypt}$2a$10$1OXUbgiuuIi3SOO3t.jyZOEY66ELL03dRcGpAKWql8HBXOag4YZ8q', 'tyukinaga@example.com', FALSE)
5 | ;
6 |
7 | INSERT INTO `user` (id, `name`, password, email, admin_flag) VALUES
8 | (4, 'sawatari', '{pbkdf2}0963ebe5b7508e9f0de55e7480ee7b87c623754ea18a94f25a20cc213a6341695d6ad38d18ff8f25', 'zsawatari@example.com', TRUE),
9 | (5, 'hiyama', '{pbkdf2}998f1e8af4662f9c7e44bad5af69e916f0ab6cf6af1a1a38b0e667f5f7b9f5bb0d3700e2eacfcf72', 'ehiyama@example.com', FALSE)
10 | ;
11 |
--------------------------------------------------------------------------------
/src/test/java/com/example/demo/ApplicationTests.java:
--------------------------------------------------------------------------------
1 | package com.example.demo;
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 ApplicationTests {
11 |
12 | @Test
13 | public void contextLoads() {
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/test/java/com/example/demo/JwtTests.java:
--------------------------------------------------------------------------------
1 | package com.example.demo;
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.JWTVerificationException;
7 | import com.auth0.jwt.interfaces.DecodedJWT;
8 | import org.junit.Ignore;
9 | import org.junit.Test;
10 |
11 | import java.util.Date;
12 |
13 | public class JwtTests {
14 |
15 | private static final Long EXPIRATION_TIME = 1000L * 60L * 10L;
16 |
17 | @Ignore
18 | @Test
19 | public void build() {
20 | String secretKey = "secret";
21 | Date issuedAt = new Date(); // "iat" < TODAY
22 | Date notBefore = new Date(issuedAt.getTime()); // "nbf" > TODAY
23 | Date expiresAt = new Date(issuedAt.getTime() + EXPIRATION_TIME); // "exp" > TODAY
24 |
25 | Algorithm algorithm = Algorithm.HMAC512(secretKey);
26 | String token = JWT.create()
27 | //.withJWTId("jwtId") //JWT ID | Use of this claim is OPTIONAL. JWTの一意の識別子
28 | //.withAudience("audience") //Audience | Use of this claim is OPTIONAL. JWTの利用者
29 | //.withIssuer("issuer") //Issuer | Use of this claim is OPTIONAL. JWTの発行者
30 | .withSubject("test") //Subject | Use of this claim is OPTIONAL. JWTのプリンシパル JWTの発行者のコンテキスト内でユニークまたはグローバルユニーク
31 | .withIssuedAt(issuedAt) //Issued At | Use of this claim is OPTIONAL. JWTの発行時間
32 | .withNotBefore(notBefore) //Not Before | Use of this claim is OPTIONAL. JWTの有効期限の開始時間 to account for clock skew. 通常は数分
33 | .withExpiresAt(expiresAt) //Expiration Time | Use of this claim is OPTIONAL. JWTの有効期限の終了時間 to account for clock skew. 通常は数分
34 | //private claims
35 | .withClaim("X-AUTHORITIES", "aaa")
36 | .withClaim("X-USERNAME", "bbb")
37 | .sign(algorithm);
38 | System.out.println("generate token : " + token);
39 | }
40 |
41 | @Ignore
42 | @Test
43 | public void verify() {
44 | String secretKey = "secret";
45 | // set generate token
46 | String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0ZXN0IiwibmJmIjoxNTMzMDA1MzAxLCJYLUFVVEhPUklUSUVTIjoiYWFhIiwiZXhwIjoxNTMzMDA1OTAxLCJpYXQiOjE1MzMwMDUzMDEsIlgtVVNFUk5BTUUiOiJiYmIifQ.jUnvcfW33wv9Fs7VK52BZ_sWGG2GM3yYLKicqsm4AuK_qhN7iF46T9rAGPOg7Gle7kArh7hbacxwe_pQbclPCw";
47 | try {
48 | Algorithm algorithm = Algorithm.HMAC512(secretKey);
49 | JWTVerifier verifier = JWT.require(algorithm).build();
50 | DecodedJWT jwt = verifier.verify(token);
51 | String subject = jwt.getSubject();
52 | Date issuedAt = jwt.getIssuedAt();
53 | Date notBefore = jwt.getNotBefore();
54 | Date expiresAt = jwt.getExpiresAt();
55 | System.out.println("subject : [" + subject + "] issuedAt : [" + issuedAt.toString() + "] notBefore : [" + notBefore.toString() + "] expiresAt : [" + expiresAt.toString() + "]");
56 | String authorities = jwt.getClaim("X-AUTHORITIES").asString();
57 | String username = jwt.getClaim("X-USERNAME").asString();
58 | System.out.println("private claim X-AUTHORITIES : [" + authorities + "] X-USERNAME : [" + username + "]");
59 | } catch (JWTVerificationException e) {
60 | e.printStackTrace();
61 | }
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/src/test/java/com/example/demo/controller/AdminControllerTests.java:
--------------------------------------------------------------------------------
1 | package com.example.demo.controller;
2 |
3 | import com.example.demo.entity.User;
4 | import com.example.demo.repository.UserRepository;
5 | import com.example.demo.security.SecurityConfig;
6 | import com.example.demo.security.SimpleLoginUser;
7 | import com.example.demo.service.UserService;
8 | import com.fasterxml.jackson.databind.ObjectMapper;
9 | import org.junit.Test;
10 | import org.junit.runner.RunWith;
11 | import org.mockito.Mockito;
12 | import org.springframework.beans.factory.annotation.Autowired;
13 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
14 | import org.springframework.boot.test.mock.mockito.MockBean;
15 | import org.springframework.context.annotation.Import;
16 | import org.springframework.http.MediaType;
17 | import org.springframework.test.context.junit4.SpringRunner;
18 | import org.springframework.test.web.servlet.MockMvc;
19 | import org.springframework.test.web.servlet.MvcResult;
20 | import org.springframework.test.web.servlet.RequestBuilder;
21 | import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
22 |
23 | import java.nio.charset.Charset;
24 | import java.util.Optional;
25 |
26 | import static org.assertj.core.api.Assertions.assertThat;
27 | import static org.mockito.ArgumentMatchers.anyString;
28 | import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
29 | import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
30 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
31 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
32 |
33 | @RunWith(SpringRunner.class)
34 | @WebMvcTest(value = AdminController.class)
35 | @Import(value = {SecurityConfig.class})
36 | public class AdminControllerTests {
37 |
38 | @Autowired
39 | private MockMvc mvc;
40 | @Autowired
41 | private ObjectMapper objectMapper;
42 |
43 | @MockBean
44 | private UserService userService;
45 | @MockBean
46 | private UserRepository userRepository;
47 |
48 | final private MediaType contentTypeText = new MediaType(MediaType.TEXT_PLAIN.getType(),
49 | MediaType.TEXT_PLAIN.getSubtype(), Charset.forName("utf8"));
50 |
51 | @Test
52 | public void greeting() throws Exception {
53 | User user = new User(1L, "aaaa", "pass", "aaa.aaa@example.com", true);
54 | SimpleLoginUser loginUser = new SimpleLoginUser(user);
55 | RequestBuilder builder = MockMvcRequestBuilders.get("/admin")
56 | .with(user(loginUser))
57 | .accept(MediaType.TEXT_PLAIN);
58 |
59 | MvcResult result = mvc.perform(builder)
60 | .andExpect(status().isOk())
61 | .andExpect(content().contentType(contentTypeText))
62 | .andExpect(content().string("hello admin aaaa"))
63 | .andDo(print())
64 | .andReturn();
65 |
66 | assertThat(result.getResponse().getContentAsString()).isEqualTo("hello admin aaaa");
67 | }
68 |
69 | @Test
70 | public void greetingWithName() throws Exception {
71 | Mockito.when(userService.findByName(anyString())).thenReturn(Optional.of(new User(2L, "bbbb", "pass", "bbb.bbb@example.com", false)));
72 |
73 | User user = new User(1L, "aaaa", "pass", "aaa.aaa@example.com", true);
74 | SimpleLoginUser loginUser = new SimpleLoginUser(user);
75 | RequestBuilder builder = MockMvcRequestBuilders.get("/admin/{name}", "bbbb")
76 | .with(user(loginUser))
77 | .accept(MediaType.TEXT_PLAIN);
78 |
79 | MvcResult result = mvc.perform(builder)
80 | .andExpect(status().isOk())
81 | .andExpect(content().contentType(contentTypeText))
82 | .andExpect(content().string("hello bbbb"))
83 | .andDo(print())
84 | .andReturn();
85 |
86 | assertThat(result.getResponse().getContentAsString()).isEqualTo("hello bbbb");
87 | }
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/src/test/java/com/example/demo/controller/HelloControllerIntegrationTests.java:
--------------------------------------------------------------------------------
1 | package com.example.demo.controller;
2 |
3 | import org.junit.Before;
4 | import org.junit.Test;
5 | import org.junit.runner.RunWith;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.boot.test.context.SpringBootTest;
8 | import org.springframework.http.MediaType;
9 | import org.springframework.test.context.junit4.SpringRunner;
10 | import org.springframework.test.web.servlet.MockMvc;
11 | import org.springframework.test.web.servlet.MvcResult;
12 | import org.springframework.test.web.servlet.RequestBuilder;
13 | import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
14 | import org.springframework.test.web.servlet.setup.MockMvcBuilders;
15 | import org.springframework.web.context.WebApplicationContext;
16 |
17 | import java.nio.charset.Charset;
18 |
19 | import static org.assertj.core.api.Assertions.assertThat;
20 | import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
21 | import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
22 | import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
23 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
24 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
25 |
26 | @RunWith(SpringRunner.class)
27 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
28 | public class HelloControllerIntegrationTests {
29 |
30 | @Autowired
31 | private WebApplicationContext context;
32 |
33 | private MockMvc mvc;
34 |
35 | final private MediaType contentTypeText = new MediaType(MediaType.TEXT_PLAIN.getType(),
36 | MediaType.TEXT_PLAIN.getSubtype(), Charset.forName("utf8"));
37 |
38 | @Before
39 | public void setup() {
40 | mvc = MockMvcBuilders
41 | .webAppContextSetup(context)
42 | .apply(springSecurity())
43 | .build();
44 | }
45 |
46 | @Test
47 | public void greeting() throws Exception {
48 | RequestBuilder builder = MockMvcRequestBuilders.get("/hello")
49 | .accept(MediaType.TEXT_PLAIN);
50 |
51 | MvcResult result = mvc.perform(builder)
52 | .andExpect(status().isOk())
53 | .andExpect(content().contentType(contentTypeText))
54 | .andExpect(content().string("hello world"))
55 | .andDo(print())
56 | .andReturn();
57 |
58 | assertThat(result.getResponse().getContentAsString()).isEqualTo("hello world");
59 | }
60 |
61 | @Test
62 | public void greetingWithMessage() throws Exception {
63 | RequestBuilder builder = MockMvcRequestBuilders.get("/hello/{message}", "WORLD")
64 | .accept(MediaType.TEXT_PLAIN);
65 |
66 | MvcResult result = mvc.perform(builder)
67 | .andExpect(status().isOk())
68 | .andExpect(content().contentType(contentTypeText))
69 | .andExpect(content().string("hello WORLD"))
70 | .andDo(print())
71 | .andReturn();
72 |
73 | assertThat(result.getResponse().getContentAsString()).isEqualTo("hello WORLD");
74 | }
75 |
76 | @Test
77 | public void postGreeting() throws Exception {
78 | RequestBuilder builder = MockMvcRequestBuilders.post("/hello")
79 | .param("message", "WORLD")
80 | .accept(MediaType.TEXT_PLAIN);
81 |
82 | MvcResult result = mvc.perform(builder)
83 | .andExpect(status().isOk())
84 | .andExpect(content().contentType(contentTypeText))
85 | .andExpect(content().string("hello WORLD"))
86 | .andDo(print())
87 | .andReturn();
88 |
89 | assertThat(result.getResponse().getContentAsString()).isEqualTo("hello WORLD");
90 | }
91 |
92 | }
93 |
--------------------------------------------------------------------------------
/src/test/java/com/example/demo/controller/HelloControllerTests.java:
--------------------------------------------------------------------------------
1 | package com.example.demo.controller;
2 |
3 | import org.junit.Test;
4 | import org.junit.runner.RunWith;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
7 | import org.springframework.http.MediaType;
8 | import org.springframework.test.context.junit4.SpringRunner;
9 | import org.springframework.test.web.servlet.MockMvc;
10 | import org.springframework.test.web.servlet.MvcResult;
11 | import org.springframework.test.web.servlet.RequestBuilder;
12 | import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
13 |
14 | import java.nio.charset.Charset;
15 |
16 | import static org.assertj.core.api.Assertions.assertThat;
17 | import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
18 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
19 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
20 |
21 | @RunWith(SpringRunner.class)
22 | @WebMvcTest(value = HelloController.class, secure = false)
23 | public class HelloControllerTests {
24 |
25 | @Autowired
26 | private MockMvc mvc;
27 |
28 | final private MediaType contentTypeText = new MediaType(MediaType.TEXT_PLAIN.getType(),
29 | MediaType.TEXT_PLAIN.getSubtype(), Charset.forName("utf8"));
30 |
31 | @Test
32 | public void greeting() throws Exception {
33 | RequestBuilder builder = MockMvcRequestBuilders.get("/hello")
34 | .accept(MediaType.TEXT_PLAIN);
35 |
36 | MvcResult result = mvc.perform(builder)
37 | .andExpect(status().isOk())
38 | .andExpect(content().contentType(contentTypeText))
39 | .andExpect(content().string("hello world"))
40 | .andDo(print())
41 | .andReturn();
42 |
43 | assertThat(result.getResponse().getContentAsString()).isEqualTo("hello world");
44 | }
45 |
46 | @Test
47 | public void greetingWithName() throws Exception {
48 | RequestBuilder builder = MockMvcRequestBuilders.get("/hello/{message}", "WORLD")
49 | .accept(MediaType.TEXT_PLAIN);
50 |
51 | MvcResult result = mvc.perform(builder)
52 | .andExpect(status().isOk())
53 | .andExpect(content().contentType(contentTypeText))
54 | .andExpect(content().string("hello WORLD"))
55 | .andDo(print())
56 | .andReturn();
57 |
58 | assertThat(result.getResponse().getContentAsString()).isEqualTo("hello WORLD");
59 | }
60 |
61 | @Test
62 | public void postGreeting() throws Exception {
63 | RequestBuilder builder = MockMvcRequestBuilders.post("/hello")
64 | .param("message", "WORLD")
65 | .accept(MediaType.TEXT_PLAIN);
66 |
67 | MvcResult result = mvc.perform(builder)
68 | .andExpect(status().isOk())
69 | .andExpect(content().contentType(contentTypeText))
70 | .andExpect(content().string("hello WORLD"))
71 | .andDo(print())
72 | .andReturn();
73 |
74 | assertThat(result.getResponse().getContentAsString()).isEqualTo("hello WORLD");
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/src/test/java/com/example/demo/controller/MemoControllerIntegrationTests.java:
--------------------------------------------------------------------------------
1 | package com.example.demo.controller;
2 |
3 | import com.example.demo.entity.Memo;
4 | import com.example.demo.entity.User;
5 | import com.example.demo.security.SimpleLoginUser;
6 | import com.fasterxml.jackson.databind.ObjectMapper;
7 | import org.junit.Before;
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 | import org.springframework.beans.factory.annotation.Autowired;
11 | import org.springframework.boot.test.context.SpringBootTest;
12 | import org.springframework.http.MediaType;
13 | import org.springframework.test.context.junit4.SpringRunner;
14 | import org.springframework.test.web.servlet.MockMvc;
15 | import org.springframework.test.web.servlet.MvcResult;
16 | import org.springframework.test.web.servlet.RequestBuilder;
17 | import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
18 | import org.springframework.test.web.servlet.setup.MockMvcBuilders;
19 | import org.springframework.web.context.WebApplicationContext;
20 |
21 | import java.nio.charset.Charset;
22 | import java.time.LocalDateTime;
23 |
24 | import static org.assertj.core.api.Assertions.assertThat;
25 | import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
26 | import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
27 | import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
28 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
29 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
30 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
31 |
32 | @RunWith(SpringRunner.class)
33 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
34 | public class MemoControllerIntegrationTests {
35 |
36 | @Autowired
37 | private WebApplicationContext context;
38 | @Autowired
39 | private ObjectMapper objectMapper;
40 |
41 | final private MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
42 | MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
43 |
44 | private MockMvc mvc;
45 |
46 | @Before
47 | public void setup() {
48 | mvc = MockMvcBuilders
49 | .webAppContextSetup(context)
50 | .apply(springSecurity())
51 | .build();
52 | }
53 |
54 | @Test
55 | public void getOne() throws Exception {
56 | Memo expected = new Memo(1L, "memo shopping", "memo1 description", false, LocalDateTime.of(2018, 1, 4, 12, 1, 0));
57 | String expectedJson = objectMapper.writeValueAsString(expected);
58 |
59 | User user = new User(1L, "aaaa", "pass", "aaa.aaa@example.com", true);
60 | SimpleLoginUser loginUser = new SimpleLoginUser(user);
61 |
62 | RequestBuilder builder = MockMvcRequestBuilders.get("/memo/{id}", expected.getId())
63 | .with(user(loginUser))
64 | //.header("Authorization", "Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJra2FtaW11cmFAZXhhbXBsZS5jb20iLCJpYXQiOjE1MjM0NjQ5NzcsImV4cCI6MTUyMzQ2NTU3NywiWC1BVVRIT1JJVElFUyI6IlJPTEVfQURNSU4sUk9MRV9VU0VSIiwiWC1VU0VSTkFNRSI6ImthbWltdXJhIn0.qMVKdGd8c1UIYryR1y7WOosdh6k-jsVGxc3XrUU2Fg8")
65 | .accept(MediaType.APPLICATION_JSON_UTF8);
66 |
67 | MvcResult result = mvc.perform(builder)
68 | .andExpect(status().isOk())
69 | .andExpect(content().contentType(contentType))
70 | .andExpect(jsonPath("$").isNotEmpty())
71 | .andExpect(jsonPath("$.title").value(expected.getTitle()))
72 | .andExpect(jsonPath("$.description").value(expected.getDescription()))
73 | .andExpect(jsonPath("$.done").value(expected.getDone()))
74 | .andDo(print())
75 | .andReturn();
76 |
77 | assertThat(result.getResponse().getContentAsString()).isEqualTo(expectedJson);
78 | }
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/src/test/java/com/example/demo/controller/MemoControllerTests.java:
--------------------------------------------------------------------------------
1 | package com.example.demo.controller;
2 |
3 | import com.example.demo.entity.Memo;
4 | import com.example.demo.service.MemoService;
5 | import com.fasterxml.jackson.databind.ObjectMapper;
6 | import org.junit.Test;
7 | import org.junit.runner.RunWith;
8 | import org.mockito.Mockito;
9 | import org.springframework.beans.factory.annotation.Autowired;
10 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
11 | import org.springframework.boot.test.mock.mockito.MockBean;
12 | import org.springframework.http.MediaType;
13 | import org.springframework.security.test.context.support.WithMockUser;
14 | import org.springframework.test.context.junit4.SpringRunner;
15 | import org.springframework.test.web.servlet.MockMvc;
16 | import org.springframework.test.web.servlet.MvcResult;
17 | import org.springframework.test.web.servlet.RequestBuilder;
18 | import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
19 |
20 | import java.nio.charset.Charset;
21 | import java.time.LocalDateTime;
22 | import java.util.Optional;
23 |
24 | import static org.assertj.core.api.Assertions.assertThat;
25 | import static org.mockito.ArgumentMatchers.anyLong;
26 | import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
27 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
28 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
29 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
30 |
31 | @RunWith(SpringRunner.class)
32 | @WebMvcTest(value = MemoController.class)
33 | public class MemoControllerTests {
34 |
35 | @Autowired
36 | private MockMvc mvc;
37 | @Autowired
38 | private ObjectMapper objectMapper;
39 |
40 | @MockBean
41 | private MemoService memoService;
42 |
43 | final private MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
44 | MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
45 |
46 | @WithMockUser
47 | @Test
48 | public void getOne() throws Exception {
49 | Memo expected = new Memo(1L, "test title", "test description", false, LocalDateTime.of(2018, 3, 19, 0, 34, 49));
50 | String expectedJson = objectMapper.writeValueAsString(expected);
51 | Mockito.when(memoService.findById(anyLong())).thenReturn(Optional.ofNullable(expected));
52 |
53 | RequestBuilder builder = MockMvcRequestBuilders.get("/memo/{id}", 1L)
54 | .accept(MediaType.APPLICATION_JSON_UTF8);
55 |
56 | MvcResult result = mvc.perform(builder)
57 | .andExpect(status().isOk())
58 | .andExpect(content().contentType(contentType))
59 | .andExpect(jsonPath("$").isNotEmpty())
60 | .andExpect(jsonPath("$.title").value(expected.getTitle()))
61 | .andExpect(jsonPath("$.description").value(expected.getDescription()))
62 | .andExpect(jsonPath("$.done").value(expected.getDone()))
63 | .andDo(print())
64 | .andReturn();
65 |
66 | assertThat(result.getResponse().getContentAsString()).isEqualTo(expectedJson);
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/src/test/java/com/example/demo/controller/UserControllerIntegrationTests.java:
--------------------------------------------------------------------------------
1 | package com.example.demo.controller;
2 |
3 | import com.example.demo.entity.User;
4 | import com.example.demo.security.SimpleLoginUser;
5 | import org.junit.Before;
6 | import org.junit.Test;
7 | import org.junit.runner.RunWith;
8 | import org.springframework.beans.factory.annotation.Autowired;
9 | import org.springframework.boot.test.context.SpringBootTest;
10 | import org.springframework.http.HttpStatus;
11 | import org.springframework.http.MediaType;
12 | import org.springframework.security.test.context.support.WithMockUser;
13 | import org.springframework.test.context.junit4.SpringRunner;
14 | import org.springframework.test.web.servlet.MockMvc;
15 | import org.springframework.test.web.servlet.MvcResult;
16 | import org.springframework.test.web.servlet.RequestBuilder;
17 | import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
18 | import org.springframework.test.web.servlet.setup.MockMvcBuilders;
19 | import org.springframework.web.context.WebApplicationContext;
20 |
21 | import java.nio.charset.Charset;
22 |
23 | import static org.assertj.core.api.Assertions.assertThat;
24 | import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
25 | import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
26 | import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
27 | import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
28 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
29 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl;
30 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
31 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
32 |
33 | @RunWith(SpringRunner.class)
34 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
35 | public class UserControllerIntegrationTests {
36 |
37 | @Autowired
38 | private WebApplicationContext context;
39 |
40 | private MockMvc mvc;
41 |
42 | final private MediaType contentTypeText = new MediaType(MediaType.TEXT_PLAIN.getType(),
43 | MediaType.TEXT_PLAIN.getSubtype(), Charset.forName("utf8"));
44 | final private MediaType contentTypeJson = new MediaType(MediaType.APPLICATION_JSON.getType(),
45 | MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
46 |
47 | @Before
48 | public void setup() {
49 | mvc = MockMvcBuilders
50 | .webAppContextSetup(context)
51 | .apply(springSecurity())
52 | .build();
53 | }
54 |
55 | @Test
56 | public void greeting() throws Exception {
57 | User user = new User(1L, "test_user", "pass", "aaa.aaa@example.com", true);
58 | SimpleLoginUser loginUser = new SimpleLoginUser(user);
59 |
60 | RequestBuilder builder = MockMvcRequestBuilders.get("/user")
61 | .with(user(loginUser))
62 | .accept(MediaType.TEXT_PLAIN);
63 |
64 | MvcResult result = mvc.perform(builder)
65 | .andExpect(authenticated().withUsername("test_user").withRoles("ADMIN", "USER"))
66 | .andExpect(content().contentType(contentTypeText))
67 | .andExpect(content().string("hello test_user"))
68 | .andExpect(forwardedUrl(null))
69 | .andExpect(redirectedUrl(null))
70 | .andDo(print())
71 | .andReturn();
72 |
73 | assertThat(result.getResponse().getContentAsString()).isEqualTo("hello test_user");
74 | }
75 |
76 | @WithMockUser(roles = "USER")
77 | @Test
78 | public void getEcho() throws Exception {
79 | RequestBuilder builder = MockMvcRequestBuilders.get("/user/echo/{message}", "abc")
80 | .accept(MediaType.TEXT_PLAIN);
81 |
82 | MvcResult result = mvc.perform(builder)
83 | .andExpect(status().isOk())
84 | .andExpect(content().contentType(contentTypeText))
85 | .andExpect(content().string("ABC"))
86 | .andDo(print())
87 | .andReturn();
88 |
89 | assertThat(result.getResponse().getContentAsString()).isEqualTo("ABC");
90 | }
91 |
92 | @WithMockUser(roles = "ADMIN")
93 | @Test
94 | public void getEcho_403() throws Exception {
95 | RequestBuilder builder = MockMvcRequestBuilders.get("/user/echo/{message}", "abc");
96 |
97 | mvc.perform(builder)
98 | .andExpect(status().is(HttpStatus.FORBIDDEN.value()))
99 | .andDo(print());
100 | }
101 |
102 | @Test
103 | public void getEcho_401() throws Exception {
104 | RequestBuilder builder = MockMvcRequestBuilders.get("/user/echo/{message}", "abc");
105 |
106 | mvc.perform(builder)
107 | .andExpect(status().is(HttpStatus.UNAUTHORIZED.value()))
108 | .andDo(print());
109 | }
110 |
111 | @WithMockUser(roles = "USER")
112 | @Test
113 | public void postEcho() throws Exception {
114 | RequestBuilder builder = MockMvcRequestBuilders.post("/user/echo")
115 | .contentType(MediaType.APPLICATION_JSON_UTF8)
116 | .content("{\"message\": \"hello world\"}")
117 | .accept(MediaType.APPLICATION_JSON_UTF8_VALUE);
118 |
119 | MvcResult result = mvc.perform(builder)
120 | .andExpect(status().isOk())
121 | .andExpect(content().contentType(contentTypeJson))
122 | .andExpect(content().string("{message=hello world}"))
123 | .andDo(print())
124 | .andReturn();
125 |
126 | assertThat(result.getResponse().getContentAsString()).isEqualTo("{message=hello world}");
127 | }
128 |
129 | }
130 |
--------------------------------------------------------------------------------
/src/test/java/com/example/demo/controller/UserControllerTests.java:
--------------------------------------------------------------------------------
1 | package com.example.demo.controller;
2 |
3 | import com.example.demo.entity.User;
4 | import com.example.demo.repository.UserRepository;
5 | import com.example.demo.security.SecurityConfig;
6 | import com.example.demo.security.SimpleLoginUser;
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 | import org.springframework.beans.factory.annotation.Autowired;
10 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
11 | import org.springframework.boot.test.mock.mockito.MockBean;
12 | import org.springframework.context.annotation.Import;
13 | import org.springframework.http.HttpStatus;
14 | import org.springframework.http.MediaType;
15 | import org.springframework.security.test.context.support.WithMockUser;
16 | import org.springframework.test.context.junit4.SpringRunner;
17 | import org.springframework.test.web.servlet.MockMvc;
18 | import org.springframework.test.web.servlet.MvcResult;
19 | import org.springframework.test.web.servlet.RequestBuilder;
20 | import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
21 |
22 | import java.nio.charset.Charset;
23 |
24 | import static org.assertj.core.api.Assertions.assertThat;
25 | import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
26 | import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
27 | import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
28 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
29 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl;
30 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
31 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
32 |
33 | @RunWith(SpringRunner.class)
34 | @WebMvcTest(value = UserController.class)
35 | @Import(value = {SecurityConfig.class})
36 | public class UserControllerTests {
37 |
38 | @Autowired
39 | private MockMvc mvc;
40 |
41 | @MockBean
42 | private UserRepository userRepository;
43 |
44 | final private MediaType contentTypeText = new MediaType(MediaType.TEXT_PLAIN.getType(),
45 | MediaType.TEXT_PLAIN.getSubtype(), Charset.forName("utf8"));
46 | final private MediaType contentTypeJson = new MediaType(MediaType.APPLICATION_JSON.getType(),
47 | MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
48 |
49 | @Test
50 | public void greeting() throws Exception {
51 | User user = new User(1L, "test_user", "pass", "aaa.aaa@example.com", true);
52 | SimpleLoginUser loginUser = new SimpleLoginUser(user);
53 | RequestBuilder builder = MockMvcRequestBuilders.get("/user")
54 | .with(user(loginUser))
55 | .accept(MediaType.TEXT_PLAIN);
56 |
57 | MvcResult result = mvc.perform(builder)
58 | .andExpect(status().isOk())
59 | .andExpect(authenticated().withUsername("test_user").withRoles("USER", "ADMIN"))
60 | .andExpect(content().contentType(contentTypeText))
61 | .andExpect(content().string("hello test_user"))
62 | .andExpect(forwardedUrl(null))
63 | .andExpect(redirectedUrl(null))
64 | .andDo(print())
65 | .andReturn();
66 |
67 | assertThat(result.getResponse().getContentAsString()).isEqualTo("hello test_user");
68 | }
69 |
70 | @WithMockUser(roles = "USER")
71 | @Test
72 | public void getEcho() throws Exception {
73 | RequestBuilder builder = MockMvcRequestBuilders.get("/user/echo/{message}", "abc")
74 | .accept(MediaType.TEXT_PLAIN);
75 |
76 | MvcResult result = mvc.perform(builder)
77 | .andExpect(status().isOk())
78 | .andExpect(content().contentType(contentTypeText))
79 | .andExpect(content().string("ABC"))
80 | .andDo(print())
81 | .andReturn();
82 |
83 | assertThat(result.getResponse().getContentAsString()).isEqualTo("ABC");
84 | }
85 |
86 | @WithMockUser(roles = "ADMIN")
87 | @Test
88 | public void getEcho_403() throws Exception {
89 | RequestBuilder builder = MockMvcRequestBuilders.get("/user/echo/{message}", "abc");
90 |
91 | mvc.perform(builder)
92 | .andExpect(status().is(HttpStatus.FORBIDDEN.value()))
93 | .andDo(print());
94 | }
95 |
96 | @Test
97 | public void getEcho_401() throws Exception {
98 | RequestBuilder builder = MockMvcRequestBuilders.get("/user/echo/{message}", "abc");
99 |
100 | mvc.perform(builder)
101 | .andExpect(status().is(HttpStatus.UNAUTHORIZED.value()))
102 | .andDo(print());
103 | }
104 |
105 | @WithMockUser(roles = "USER")
106 | @Test
107 | public void postEcho() throws Exception {
108 | RequestBuilder builder = MockMvcRequestBuilders.post("/user/echo")
109 | .contentType(MediaType.APPLICATION_JSON_UTF8)
110 | .content("{\"message\": \"hello world\"}")
111 | .accept(MediaType.APPLICATION_JSON_UTF8_VALUE);
112 |
113 | MvcResult result = mvc.perform(builder)
114 | .andExpect(status().isOk())
115 | .andExpect(content().contentType(contentTypeJson))
116 | .andExpect(content().string("{message=hello world}"))
117 | .andDo(print())
118 | .andReturn();
119 |
120 | assertThat(result.getResponse().getContentAsString()).isEqualTo("{message=hello world}");
121 | }
122 |
123 | }
124 |
--------------------------------------------------------------------------------
/src/test/java/com/example/demo/repository/UserRepositoryTests.java:
--------------------------------------------------------------------------------
1 | package com.example.demo.repository;
2 |
3 | import com.example.demo.entity.User;
4 | import org.junit.Test;
5 | import org.junit.runner.RunWith;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
8 | import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
9 | import org.springframework.test.context.junit4.SpringRunner;
10 |
11 | import java.util.Optional;
12 |
13 | import static org.assertj.core.api.Assertions.assertThat;
14 |
15 | @RunWith(SpringRunner.class)
16 | @DataJpaTest
17 | public class UserRepositoryTests {
18 | @Autowired
19 | private TestEntityManager testEntityManager;
20 | @Autowired
21 | private UserRepository userRepository;
22 |
23 | @Test
24 | public void findByEmail() {
25 | User expected = testEntityManager.persistFlushFind(User.of("user_a_name", "user_a_pass", "aaa@example.com"));
26 |
27 | Optional user = userRepository.findByEmail(expected.getEmail());
28 | User actual = user.orElseThrow(RuntimeException::new);
29 | assertThat(actual).isEqualTo(expected);
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/test/resources/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubytomato/demo-security-jwt-spring2/8a2956a7be129ce93b592fe12bd1a9fefa533825/src/test/resources/.gitkeep
--------------------------------------------------------------------------------