├── .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 --------------------------------------------------------------------------------