├── .gitignore
├── .mvn
└── wrapper
│ ├── maven-wrapper.jar
│ └── maven-wrapper.properties
├── README.md
├── mvnw
├── mvnw.cmd
├── pom.xml
├── src
├── main
│ ├── java
│ │ └── com
│ │ │ └── wheejuni
│ │ │ └── jwtdemo
│ │ │ ├── JwtdemoApplication.java
│ │ │ ├── controllers
│ │ │ └── ApiController.java
│ │ │ ├── domain
│ │ │ ├── Account.kt
│ │ │ ├── AccountRepository.java
│ │ │ ├── SocialProviders.java
│ │ │ └── UserRole.java
│ │ │ ├── dtos
│ │ │ ├── FormLoginDto.kt
│ │ │ ├── SocialLoginDto.kt
│ │ │ └── TokenDto.kt
│ │ │ └── security
│ │ │ ├── AccountContext.java
│ │ │ ├── AccountContextService.java
│ │ │ ├── InvalidJwtException.java
│ │ │ ├── SecurityConfig.java
│ │ │ ├── filters
│ │ │ ├── FilterSkipMatcher.java
│ │ │ ├── FormLoginFilter.java
│ │ │ ├── JwtAuthenticationFilter.java
│ │ │ └── SocialLoginFilter.java
│ │ │ ├── handlers
│ │ │ ├── FormLoginAuthenticationSuccessHandler.java
│ │ │ └── JwtAuthenticationFailureHandler.java
│ │ │ ├── jwt
│ │ │ ├── HeaderTokenExtractor.java
│ │ │ ├── JwtDecoder.java
│ │ │ └── JwtFactory.java
│ │ │ ├── providers
│ │ │ ├── FormLoginAuthenticationProvider.java
│ │ │ ├── JwtAuthenticationProvider.java
│ │ │ └── SocialLoginAuthenticationProvider.java
│ │ │ ├── services
│ │ │ ├── impl
│ │ │ │ ├── SocialFetchServiceProd.java
│ │ │ │ └── SocialFetchServiceTest.java
│ │ │ └── specification
│ │ │ │ └── SocialFetchService.java
│ │ │ ├── social
│ │ │ ├── KakaoUserProperty.kt
│ │ │ ├── NaverUserProperty.kt
│ │ │ └── SocialUserProperty.java
│ │ │ └── tokens
│ │ │ ├── JwtPostProcessingToken.java
│ │ │ ├── JwtPreProcessingToken.java
│ │ │ ├── PostAuthorizationToken.java
│ │ │ ├── PreAuthorizationToken.java
│ │ │ └── SocialPreAuthorizationToken.java
│ └── resources
│ │ └── application.yml
└── test
│ └── java
│ └── com
│ └── wheejuni
│ └── jwtdemo
│ ├── JwtdemoApplicationTests.java
│ └── security
│ ├── HeaderTokenExtractorTest.java
│ ├── JwtFactoryTest.java
│ └── services
│ └── specification
│ └── SocialFetchServiceTest.java
└── test-http
├── hello.http
├── loggedin-request.http
├── login-naver.http
├── login-request.http
└── login-social.http
/.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/
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wheejuni/spring-jwt/92f567eab41f830ecad5124bb59414aaeaec5b9f/.mvn/wrapper/maven-wrapper.jar
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.3/apache-maven-3.5.3-bin.zip
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### 봄이네집 스프링 - (1) Spring Security + Auth0 JWT Library
2 | ---
3 |
4 | #### 안녕하세요.
5 |
6 | 안녕하세요.
7 |
8 | 봄이네집 스프링의 첫 세션입니다.
9 |
10 | 이번 세션에서는 Spring Security의 기본 개념을 알아보고, 레퍼런스 문서를 함께 읽습니다. 또 내장된 기본 구현체들을 까 보는(?) 시간도 가질 예정입니다.
11 |
12 | 또한 Auth0 Java-JWT( https://github.com/auth0/java-jwt )를 사용해 JWT를 발급하고, 인증하는 방법을 알아봅니다. 소셜 공급자 연동도 간단히 맛봅니다.
13 |
14 | 감사합니다!
15 |
16 |
17 | **방송 일지**
18 |
19 | [제 1회차 방송](https://www.youtube.com/watch?v=SMZm2aqI_dQ&index=1&list=PLcsqrv8NxApXzHViDU2fB1ew7KoLoaB02)
20 |
21 | [제 2회차 방송](https://www.youtube.com/watch?v=x2i96t1aA3s&index=2&list=PLcsqrv8NxApXzHViDU2fB1ew7KoLoaB02&t=0s)
22 |
23 | [제 3회차 방송](https://www.youtube.com/watch?v=qCA3JB4W_cw)
24 |
25 | [제 4회차 방송](https://www.youtube.com/watch?v=jNNJnGiLMl8&list=PLcsqrv8NxApXzHViDU2fB1ew7KoLoaB02&index=4&t=0s)
26 |
27 | [제 5회차 방송](https://www.youtube.com/watch?v=qhCVfz1t68I&t=0s&index=6&list=PLcsqrv8NxApXzHViDU2fB1ew7KoLoaB02)
28 |
29 | [방송 바로가기](https://www.youtube.com/watch?v=SMZm2aqI_dQ)
30 |
31 | ---
32 | #### Account 객체 요구사항
33 |
34 | * 기본적인 유저 정보
35 |
36 | * 아이디, 비밀번호, 이름, 프사 링크(profileHref)
37 | * 서비스상에서 유저에게 부여하고싶은 권한
38 | * 소셜 로그인한 사용자의 경우, 소셜 서비스가 부여한 ID 코드 **(로그인 ID 아님)**
39 |
40 | * 유저 정보를 인증과정에서 처리하는 방식
41 |
42 | * 유저 모델을 그대로 사용
43 | * 유저디테일즈 구현체를 사용
44 |
45 | ---
46 | ###오늘의 할일
47 |
48 | * 카카오 로그인을 구현한다.
49 |
50 | * 유저의 카카오 토큰으로 유저 정보를 얻어오는 로직 (서비스) 작성.
51 | * 추후 다른 소셜 공급자와도 연동할 수 있게 확장성있게 구현.
52 | * 최대한 객체지향적으로 설계.
53 | * DTO 작성.
54 |
55 | * 인증 후 JWT를 발급해야 한다.
56 | * 데이터베이스에서 검색되지 않는 사용자는 회원가입 처리 후 발급.
57 | * 데이트베이스에 있는 사용자는 바로 발급.
58 |
59 | #### 소셜 공급자에서 빼올 데이터.
60 | - email
61 | - 고유번호('id')
62 | - 프사 href
63 | - 닉네임
64 |
65 | ### 인터페이스부터 구현합시다.
66 | * SocialUserProperty
67 |
68 | ---
69 | ###오늘의 할 일
70 |
71 | * 네이버 로그인을 구현한다.
72 |
73 |
--------------------------------------------------------------------------------
/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.wheejuni
7 | jwtdemo
8 | 0.0.1-SNAPSHOT
9 | jar
10 |
11 | jwtdemo
12 | Demo project for Spring Boot
13 |
14 |
15 | org.springframework.boot
16 | spring-boot-starter-parent
17 | 2.0.1.RELEASE
18 |
19 |
20 |
21 |
22 | UTF-8
23 | UTF-8
24 | 1.8
25 | 1.2.40
26 |
27 |
28 |
29 |
30 | org.springframework.boot
31 | spring-boot-starter-data-jpa
32 |
33 |
34 | org.springframework.boot
35 | spring-boot-starter-security
36 |
37 |
38 | org.springframework.boot
39 | spring-boot-starter-web
40 |
41 |
42 |
43 | org.springframework.boot
44 | spring-boot-devtools
45 | runtime
46 |
47 |
48 | org.projectlombok
49 | lombok
50 | true
51 |
52 |
53 | org.springframework.boot
54 | spring-boot-starter-test
55 | test
56 |
57 |
58 | org.springframework.security
59 | spring-security-test
60 | test
61 |
62 |
63 | com.auth0
64 | java-jwt
65 | 3.3.0
66 |
67 |
68 | org.jetbrains.kotlin
69 | kotlin-stdlib-jdk8
70 | ${kotlin.version}
71 |
72 |
73 | org.jetbrains.kotlin
74 | kotlin-test
75 | ${kotlin.version}
76 | test
77 |
78 |
79 | org.jetbrains.kotlin
80 | kotlin-reflect
81 | 1.1.2
82 |
83 |
84 | com.fasterxml.jackson.module
85 | jackson-module-kotlin
86 |
87 |
88 | com.h2database
89 | h2
90 | 1.4.196
91 | runtime
92 |
93 |
94 |
95 |
96 |
97 |
98 | org.springframework.boot
99 | spring-boot-maven-plugin
100 |
101 |
102 | org.jetbrains.kotlin
103 | kotlin-maven-plugin
104 | ${kotlin.version}
105 |
106 |
107 | compile
108 | process-sources
109 |
110 | compile
111 |
112 |
113 |
114 | test-compile
115 | test-compile
116 |
117 | test-compile
118 |
119 |
120 |
121 |
122 | 1.8
123 |
124 |
125 |
126 | org.apache.maven.plugins
127 | maven-compiler-plugin
128 |
129 |
130 | compile
131 | compile
132 |
133 | compile
134 |
135 |
136 |
137 | testCompile
138 | test-compile
139 |
140 | testCompile
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/JwtdemoApplication.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo;
2 |
3 | import com.wheejuni.jwtdemo.domain.Account;
4 | import com.wheejuni.jwtdemo.domain.AccountRepository;
5 | import org.springframework.boot.CommandLineRunner;
6 | import org.springframework.boot.SpringApplication;
7 | import org.springframework.boot.autoconfigure.SpringBootApplication;
8 | import org.springframework.context.annotation.Bean;
9 | import org.springframework.security.crypto.password.PasswordEncoder;
10 |
11 | @SpringBootApplication
12 | public class JwtdemoApplication {
13 |
14 | public static void main(String[] args) {
15 | SpringApplication.run(JwtdemoApplication.class, args);
16 | }
17 |
18 |
19 | @Bean
20 | CommandLineRunner bootstrapTestAccount(AccountRepository accountRepository, PasswordEncoder passwordEncoder) {
21 | return args -> {
22 | Account account = new Account();
23 | account.setPassword(passwordEncoder.encode("1234"));
24 |
25 | accountRepository.save(account);
26 | };
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/controllers/ApiController.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.controllers;
2 |
3 | import com.wheejuni.jwtdemo.security.tokens.PostAuthorizationToken;
4 | import org.springframework.security.access.prepost.PreAuthorize;
5 | import org.springframework.security.core.Authentication;
6 | import org.springframework.web.bind.annotation.GetMapping;
7 | import org.springframework.web.bind.annotation.RequestMapping;
8 | import org.springframework.web.bind.annotation.RestController;
9 |
10 | @RestController
11 | @RequestMapping("/api")
12 | public class ApiController {
13 |
14 |
15 | @GetMapping("/hello")
16 | @PreAuthorize("hasRole('ROLE_USER')")
17 | public String getUsername(Authentication authentication) {
18 |
19 | PostAuthorizationToken token = (PostAuthorizationToken)authentication;
20 |
21 | return token.getAccountContext().getUsername();
22 | }
23 |
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/domain/Account.kt:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.domain
2 |
3 | import javax.persistence.*
4 |
5 | @Entity
6 | @Table(name = "ACCOUNT")
7 | data class Account(
8 |
9 | @Id
10 | @GeneratedValue(strategy = GenerationType.AUTO)
11 | val id: Long? = null,
12 |
13 | @Column(name = "ACCOUNT_USERNAME")
14 | val username: String? = null,
15 |
16 | @Column(name = "ACCOUNT_LOGINID")
17 | val userId: String? = "emalyun@naver.com",
18 |
19 | @Column(name = "ACCOUNT_PASSWORD")
20 | var password: String? = "1234",
21 |
22 | @Column(name = "ACCOUNT_ROLE")
23 | @Enumerated(value = EnumType.STRING)
24 | var userRole: UserRole? = UserRole.USER,
25 |
26 | @Column(name = "ACCOUNT_SOCIAL_ID")
27 | var socialId: Long? = null,
28 |
29 | @Column(name = "ACCOUNT_SOCIAL_PROVIDER")
30 | @Enumerated(value = EnumType.STRING)
31 | var socialProvider: SocialProviders? = null,
32 |
33 | @Column(name = "ACCOUNT_SOCIAL_PROFILEPIC")
34 | var profileHref: String? = null) {
35 |
36 |
37 | }
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/domain/AccountRepository.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.domain;
2 |
3 | import org.springframework.data.repository.CrudRepository;
4 |
5 | import java.util.Optional;
6 |
7 | public interface AccountRepository extends CrudRepository {
8 |
9 | Optional findByUserId(String userId);
10 |
11 | Optional findBySocialId(long socialId);
12 |
13 | Optional findBySocialIdAndSocialProvider(long socialId, SocialProviders socialProvider);
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/domain/SocialProviders.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.domain;
2 |
3 | import com.fasterxml.jackson.annotation.JsonValue;
4 | import com.wheejuni.jwtdemo.security.social.KakaoUserProperty;
5 | import com.wheejuni.jwtdemo.security.social.NaverUserProperty;
6 | import com.wheejuni.jwtdemo.security.social.SocialUserProperty;
7 | import lombok.Getter;
8 |
9 | @Getter
10 | public enum SocialProviders {
11 |
12 | KAKAO("https://kapi.kakao.com/v1/user/me", KakaoUserProperty.class),
13 | NAVER("https://openapi.naver.com/v1/nid/me", NaverUserProperty.class);
14 |
15 | private String userinfoEndpoint;
16 | private Class extends SocialUserProperty> propertyMetaclass;
17 |
18 | SocialProviders(String userinfoEndpoint, Class extends SocialUserProperty> propertyMetaclass) {
19 | this.userinfoEndpoint = userinfoEndpoint;
20 | this.propertyMetaclass = propertyMetaclass;
21 | }
22 |
23 | @JsonValue
24 | public String getProviderName() {
25 | return this.name();
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/domain/UserRole.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.domain;
2 |
3 | import lombok.Getter;
4 | import org.springframework.security.core.GrantedAuthority;
5 |
6 | import java.util.Arrays;
7 | import java.util.NoSuchElementException;
8 |
9 | @Getter
10 | public enum UserRole {
11 |
12 | USER("ROLE_USER"), ADMIN("ROLE_ADMIN") ;
13 |
14 | private String roleName;
15 |
16 | UserRole(String roleName) {
17 | this.roleName = roleName;
18 | }
19 |
20 | public boolean isCorrectName(String name) {
21 | return name.equalsIgnoreCase(this.roleName);
22 | }
23 |
24 | public static UserRole getRoleByName(String roleName) {
25 | return Arrays.stream(UserRole.values()).filter(r -> r.isCorrectName(roleName)).findFirst().orElseThrow(() -> new NoSuchElementException("검색된 권한이 없습니다."));
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/dtos/FormLoginDto.kt:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.dtos
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty
4 |
5 | data class FormLoginDto(
6 |
7 | @field:JsonProperty("userid")
8 | val id: String? = null,
9 |
10 | @field:JsonProperty("password")
11 | val password: String? = null
12 | )
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/dtos/SocialLoginDto.kt:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.dtos
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty
4 | import com.wheejuni.jwtdemo.domain.SocialProviders
5 |
6 | data class SocialLoginDto(
7 | @field:JsonProperty("provider")
8 | val provider: SocialProviders? = null,
9 |
10 | @field:JsonProperty("token")
11 | val token: String? = null
12 | )
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/dtos/TokenDto.kt:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.dtos
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty
4 |
5 | data class TokenDto(
6 |
7 | @field:JsonProperty("token")
8 | val token: String? = null)
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/security/AccountContext.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.security;
2 |
3 | import com.wheejuni.jwtdemo.domain.Account;
4 | import com.wheejuni.jwtdemo.domain.UserRole;
5 | import com.wheejuni.jwtdemo.security.tokens.JwtPostProcessingToken;
6 | import org.springframework.security.core.GrantedAuthority;
7 | import org.springframework.security.core.authority.SimpleGrantedAuthority;
8 | import org.springframework.security.core.userdetails.User;
9 |
10 | import java.util.Arrays;
11 | import java.util.Collection;
12 | import java.util.List;
13 | import java.util.stream.Collectors;
14 |
15 | public class AccountContext extends User {
16 |
17 | private Account account;
18 |
19 | private AccountContext(Account account, String username, String password, Collection extends GrantedAuthority> authorities) {
20 | super(username, password, authorities);
21 | this.account = account;
22 | }
23 |
24 | public AccountContext(String username, String password, String role) {
25 | super(username, password, parseAuthorities(role));
26 | }
27 |
28 | public static AccountContext fromAccountModel(Account account) {
29 | return new AccountContext(account, account.getUserId(), account.getPassword(), parseAuthorities(account.getUserRole()));
30 | }
31 |
32 | public static AccountContext fromJwtPostToken(JwtPostProcessingToken token) {
33 | return new AccountContext(null, token.getUserid(), token.getPassword(), token.getAuthorities());
34 | }
35 |
36 | private static List parseAuthorities(UserRole role) {
37 | return Arrays.asList(role).stream().map(r -> new SimpleGrantedAuthority(r.getRoleName())).collect(Collectors.toList());
38 | }
39 |
40 | private static List parseAuthorities(String role) {
41 | return parseAuthorities(UserRole.getRoleByName(role));
42 | }
43 |
44 | public Account getAccount() {
45 | return account;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/security/AccountContextService.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.security;
2 |
3 | import com.wheejuni.jwtdemo.domain.Account;
4 | import com.wheejuni.jwtdemo.domain.AccountRepository;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.security.core.userdetails.UserDetails;
7 | import org.springframework.security.core.userdetails.UserDetailsService;
8 | import org.springframework.security.core.userdetails.UsernameNotFoundException;
9 | import org.springframework.stereotype.Component;
10 |
11 | import java.util.NoSuchElementException;
12 |
13 | @Component
14 | public class AccountContextService implements UserDetailsService {
15 |
16 | @Autowired
17 | private AccountRepository accountRepository;
18 |
19 |
20 | @Override
21 | public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException {
22 | Account account = accountRepository.findByUserId(userId).orElseThrow(() -> new NoSuchElementException("아이디에 맞는 계정이 없습니다."));
23 |
24 | return getAccountContext(account);
25 | }
26 |
27 | private AccountContext getAccountContext(Account account) {
28 | return AccountContext.fromAccountModel(account);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/security/InvalidJwtException.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.security;
2 |
3 | public class InvalidJwtException extends RuntimeException {
4 |
5 | public InvalidJwtException(String msg) {
6 | super(msg);
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/security/SecurityConfig.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.security;
2 |
3 | import com.fasterxml.jackson.databind.ObjectMapper;
4 | import com.wheejuni.jwtdemo.security.filters.FilterSkipMatcher;
5 | import com.wheejuni.jwtdemo.security.filters.FormLoginFilter;
6 | import com.wheejuni.jwtdemo.security.filters.JwtAuthenticationFilter;
7 | import com.wheejuni.jwtdemo.security.filters.SocialLoginFilter;
8 | import com.wheejuni.jwtdemo.security.handlers.FormLoginAuthenticationSuccessHandler;
9 | import com.wheejuni.jwtdemo.security.handlers.JwtAuthenticationFailureHandler;
10 | import com.wheejuni.jwtdemo.security.jwt.HeaderTokenExtractor;
11 | import com.wheejuni.jwtdemo.security.providers.FormLoginAuthenticationProvider;
12 | import com.wheejuni.jwtdemo.security.providers.JwtAuthenticationProvider;
13 | import com.wheejuni.jwtdemo.security.providers.SocialLoginAuthenticationProvider;
14 | import org.springframework.beans.factory.annotation.Autowired;
15 | import org.springframework.context.annotation.Bean;
16 | import org.springframework.context.annotation.Configuration;
17 | import org.springframework.security.authentication.AuthenticationManager;
18 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
19 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
20 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
21 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
22 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
23 | import org.springframework.security.config.http.SessionCreationPolicy;
24 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
25 | import org.springframework.security.crypto.password.PasswordEncoder;
26 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
27 |
28 | import java.util.Arrays;
29 |
30 | @Configuration
31 | @EnableWebSecurity
32 | @EnableGlobalMethodSecurity(prePostEnabled = true)
33 | public class SecurityConfig extends WebSecurityConfigurerAdapter {
34 |
35 | @Autowired
36 | private FormLoginAuthenticationSuccessHandler formLoginAuthenticationSuccessHandler;
37 |
38 | @Autowired
39 | private FormLoginAuthenticationProvider provider;
40 |
41 | @Autowired
42 | private JwtAuthenticationProvider jwtProvider;
43 |
44 | @Autowired
45 | private SocialLoginAuthenticationProvider socialProvider;
46 |
47 | @Autowired
48 | private JwtAuthenticationFailureHandler jwtFailureHandler;
49 |
50 | @Autowired
51 | private HeaderTokenExtractor headerTokenExtractor;
52 |
53 | @Bean
54 | public PasswordEncoder getPasswordEncoder() {
55 | return new BCryptPasswordEncoder();
56 | }
57 |
58 | @Bean
59 | public ObjectMapper getObjectMapper() {
60 | return new ObjectMapper();
61 | }
62 |
63 | @Bean
64 | public AuthenticationManager getAuthenticationManager() throws Exception {
65 | return super.authenticationManagerBean();
66 | }
67 |
68 | protected FormLoginFilter formLoginFilter() throws Exception {
69 | FormLoginFilter filter = new FormLoginFilter("/formlogin", formLoginAuthenticationSuccessHandler, null);
70 | filter.setAuthenticationManager(super.authenticationManagerBean());
71 |
72 | return filter;
73 | }
74 |
75 | protected JwtAuthenticationFilter jwtFilter() throws Exception {
76 | FilterSkipMatcher matcher = new FilterSkipMatcher(Arrays.asList("/formlogin", "/social"), "/api/**");
77 | JwtAuthenticationFilter filter = new JwtAuthenticationFilter(matcher, jwtFailureHandler, headerTokenExtractor);
78 | filter.setAuthenticationManager(super.authenticationManagerBean());
79 |
80 | return filter;
81 | }
82 |
83 | protected SocialLoginFilter socialFilter() throws Exception {
84 | SocialLoginFilter filter = new SocialLoginFilter("/social", formLoginAuthenticationSuccessHandler);
85 | filter.setAuthenticationManager(super.authenticationManagerBean());
86 |
87 | return filter;
88 | }
89 |
90 |
91 |
92 | @Override
93 | protected void configure(AuthenticationManagerBuilder auth) throws Exception {
94 | auth
95 | .authenticationProvider(this.provider)
96 | .authenticationProvider(this.socialProvider)
97 | .authenticationProvider(this.jwtProvider);
98 | }
99 |
100 | @Override
101 | protected void configure(HttpSecurity http) throws Exception {
102 |
103 | http
104 | .sessionManagement()
105 | .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
106 |
107 | http
108 | .csrf().disable();
109 |
110 | http
111 | .headers().frameOptions().disable();
112 |
113 | http
114 | .authorizeRequests()
115 | .antMatchers("/h2-console**").permitAll();
116 |
117 | http
118 | .addFilterBefore(formLoginFilter(), UsernamePasswordAuthenticationFilter.class)
119 | .addFilterBefore(socialFilter(), UsernamePasswordAuthenticationFilter.class)
120 | .addFilterBefore(jwtFilter(), UsernamePasswordAuthenticationFilter.class);
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/security/filters/FilterSkipMatcher.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.security.filters;
2 |
3 | import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
4 | import org.springframework.security.web.util.matcher.OrRequestMatcher;
5 | import org.springframework.security.web.util.matcher.RequestMatcher;
6 |
7 | import javax.servlet.http.HttpServletRequest;
8 | import java.util.List;
9 | import java.util.stream.Collectors;
10 |
11 | public class FilterSkipMatcher implements RequestMatcher {
12 |
13 | private OrRequestMatcher orRequestMatcher;
14 | private RequestMatcher processingMatcher;
15 |
16 | public FilterSkipMatcher(List pathToSkip, String processingPath) {
17 | this.orRequestMatcher = new OrRequestMatcher(pathToSkip.stream().map(p -> new AntPathRequestMatcher(p)).collect(Collectors.toList()));
18 | this.processingMatcher = new AntPathRequestMatcher(processingPath);
19 | }
20 |
21 | @Override
22 | public boolean matches(HttpServletRequest req) {
23 | return !orRequestMatcher.matches(req) && processingMatcher.matches(req);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/security/filters/FormLoginFilter.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.security.filters;
2 |
3 | import com.fasterxml.jackson.databind.ObjectMapper;
4 | import com.wheejuni.jwtdemo.dtos.FormLoginDto;
5 | import com.wheejuni.jwtdemo.security.handlers.FormLoginAuthenticationSuccessHandler;
6 | import com.wheejuni.jwtdemo.security.tokens.PreAuthorizationToken;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 | import org.springframework.security.core.Authentication;
10 | import org.springframework.security.core.AuthenticationException;
11 | import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
12 | import org.springframework.security.web.authentication.AuthenticationFailureHandler;
13 | import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
14 |
15 | import javax.servlet.FilterChain;
16 | import javax.servlet.ServletException;
17 | import javax.servlet.http.HttpServletRequest;
18 | import javax.servlet.http.HttpServletResponse;
19 | import java.io.IOException;
20 |
21 | public class FormLoginFilter extends AbstractAuthenticationProcessingFilter {
22 |
23 | private AuthenticationSuccessHandler authenticationSuccessHandler;
24 | private AuthenticationFailureHandler authenticationFailureHandler;
25 |
26 | public FormLoginFilter(String defaultUrl, AuthenticationSuccessHandler successHandler, AuthenticationFailureHandler failureHandler) {
27 | super(defaultUrl);
28 |
29 | this.authenticationSuccessHandler = successHandler;
30 | this.authenticationFailureHandler = failureHandler;
31 | }
32 |
33 |
34 | protected FormLoginFilter(String defaultFilterProcessesUrl) {
35 | super(defaultFilterProcessesUrl);
36 | }
37 |
38 | @Override
39 | public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res) throws AuthenticationException, IOException, ServletException {
40 |
41 | FormLoginDto dto = new ObjectMapper().readValue(req.getReader(), FormLoginDto.class);
42 | PreAuthorizationToken token = new PreAuthorizationToken(dto);
43 |
44 | return super.getAuthenticationManager().authenticate(token);
45 | }
46 |
47 | @Override
48 | protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
49 | this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, authResult);
50 | }
51 |
52 | @Override
53 | protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
54 | AuthenticationFailureHandler handler = (req, res, exception) -> {
55 | Logger log = LoggerFactory.getLogger("authentication_failure");
56 |
57 | log.error(exception.getMessage());
58 | };
59 |
60 | handler.onAuthenticationFailure(request, response, failed);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/security/filters/JwtAuthenticationFilter.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.security.filters;
2 |
3 | import com.wheejuni.jwtdemo.security.jwt.HeaderTokenExtractor;
4 | import com.wheejuni.jwtdemo.security.handlers.JwtAuthenticationFailureHandler;
5 | import com.wheejuni.jwtdemo.security.tokens.JwtPreProcessingToken;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 | import org.springframework.security.core.Authentication;
9 | import org.springframework.security.core.AuthenticationException;
10 | import org.springframework.security.core.context.SecurityContext;
11 | import org.springframework.security.core.context.SecurityContextHolder;
12 | import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
13 | import org.springframework.security.web.util.matcher.RequestMatcher;
14 |
15 | import javax.servlet.FilterChain;
16 | import javax.servlet.ServletException;
17 | import javax.servlet.http.HttpServletRequest;
18 | import javax.servlet.http.HttpServletResponse;
19 | import java.io.IOException;
20 |
21 | public class JwtAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
22 |
23 | private static final Logger log = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
24 |
25 | private JwtAuthenticationFailureHandler failureHandler;
26 | private HeaderTokenExtractor extractor;
27 |
28 | protected JwtAuthenticationFilter(RequestMatcher matcher) {
29 | super(matcher);
30 | }
31 |
32 | public JwtAuthenticationFilter(RequestMatcher matcher, JwtAuthenticationFailureHandler failureHandler, HeaderTokenExtractor extractor) {
33 | super(matcher);
34 |
35 | this.failureHandler = failureHandler;
36 | this.extractor = extractor;
37 | }
38 |
39 |
40 | @Override
41 | public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res) throws AuthenticationException, IOException, ServletException {
42 |
43 | String tokenPayload = req.getHeader("Authorization");
44 |
45 | JwtPreProcessingToken token = new JwtPreProcessingToken(this.extractor.extract(tokenPayload));
46 | return super.getAuthenticationManager().authenticate(token);
47 | }
48 |
49 | @Override
50 | protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
51 |
52 | SecurityContext context = SecurityContextHolder.createEmptyContext();
53 |
54 | context.setAuthentication(authResult);
55 | SecurityContextHolder.setContext(context);
56 |
57 | chain.doFilter(request, response);
58 | }
59 |
60 |
61 | @Override
62 | protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
63 | SecurityContextHolder.clearContext();
64 |
65 | this.unsuccessfulAuthentication(request, response, failed);
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/security/filters/SocialLoginFilter.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.security.filters;
2 |
3 | import com.fasterxml.jackson.databind.ObjectMapper;
4 | import com.wheejuni.jwtdemo.dtos.SocialLoginDto;
5 | import com.wheejuni.jwtdemo.security.tokens.PreAuthorizationToken;
6 | import com.wheejuni.jwtdemo.security.tokens.SocialPreAuthorizationToken;
7 | import lombok.extern.slf4j.Slf4j;
8 | import org.springframework.security.core.Authentication;
9 | import org.springframework.security.core.AuthenticationException;
10 | import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
11 | import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
12 |
13 | import javax.servlet.FilterChain;
14 | import javax.servlet.ServletException;
15 | import javax.servlet.http.HttpServletRequest;
16 | import javax.servlet.http.HttpServletResponse;
17 | import java.io.IOException;
18 |
19 | @Slf4j
20 | public class SocialLoginFilter extends AbstractAuthenticationProcessingFilter {
21 |
22 | private AuthenticationSuccessHandler successHandler;
23 |
24 | protected SocialLoginFilter(String defaultFilterProcessesUrl) {
25 | super(defaultFilterProcessesUrl);
26 | }
27 |
28 | public SocialLoginFilter(String url, AuthenticationSuccessHandler handler) {
29 | super(url);
30 | this.successHandler = handler;
31 | }
32 |
33 | @Override
34 | public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res) throws AuthenticationException, IOException, ServletException {
35 |
36 | ObjectMapper objectMapper = new ObjectMapper();
37 |
38 | SocialLoginDto dto = objectMapper.readValue(req.getReader(), SocialLoginDto.class);
39 | return super.getAuthenticationManager().authenticate(new SocialPreAuthorizationToken(dto));
40 | }
41 |
42 | @Override
43 | protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
44 | this.successHandler.onAuthenticationSuccess(request, response, authResult);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/security/handlers/FormLoginAuthenticationSuccessHandler.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.security.handlers;
2 |
3 | import com.fasterxml.jackson.core.JsonProcessingException;
4 | import com.fasterxml.jackson.databind.ObjectMapper;
5 | import com.wheejuni.jwtdemo.dtos.TokenDto;
6 | import com.wheejuni.jwtdemo.security.AccountContext;
7 | import com.wheejuni.jwtdemo.security.jwt.JwtFactory;
8 | import com.wheejuni.jwtdemo.security.tokens.PostAuthorizationToken;
9 | import org.springframework.beans.factory.annotation.Autowired;
10 | import org.springframework.http.HttpStatus;
11 | import org.springframework.http.MediaType;
12 | import org.springframework.security.core.Authentication;
13 | import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
14 | import org.springframework.stereotype.Component;
15 |
16 | import javax.servlet.ServletException;
17 | import javax.servlet.http.HttpServletRequest;
18 | import javax.servlet.http.HttpServletResponse;
19 | import java.io.IOException;
20 |
21 | @Component
22 | public class FormLoginAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
23 |
24 | @Autowired
25 | private JwtFactory factory;
26 |
27 | @Autowired
28 | private ObjectMapper objectMapper;
29 |
30 | @Override
31 | public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse res, Authentication auth) throws IOException, ServletException {
32 | AccountContext context = ((PostAuthorizationToken) auth).getAccountContext();
33 |
34 | String tokenString = factory.generateToken(context);
35 |
36 | processResponse(res, writeDto(tokenString));
37 | }
38 |
39 | private TokenDto writeDto(String token) {
40 | return new TokenDto(token);
41 | }
42 |
43 | private void processResponse(HttpServletResponse res, TokenDto dto) throws JsonProcessingException, IOException {
44 | res.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
45 | res.setStatus(HttpStatus.OK.value());
46 | res.getWriter().write(objectMapper.writeValueAsString(dto));
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/security/handlers/JwtAuthenticationFailureHandler.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.security.handlers;
2 |
3 | import com.wheejuni.jwtdemo.security.filters.JwtAuthenticationFilter;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 | import org.springframework.security.core.AuthenticationException;
7 | import org.springframework.security.web.authentication.AuthenticationFailureHandler;
8 | import org.springframework.stereotype.Component;
9 |
10 | import javax.servlet.ServletException;
11 | import javax.servlet.http.HttpServletRequest;
12 | import javax.servlet.http.HttpServletResponse;
13 | import java.io.IOException;
14 |
15 | @Component
16 | public class JwtAuthenticationFailureHandler implements AuthenticationFailureHandler {
17 |
18 | private static final Logger log = LoggerFactory.getLogger(JwtAuthenticationFailureHandler.class);
19 |
20 | @Override
21 | public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse res, AuthenticationException e) throws IOException, ServletException {
22 | log.error(e.getMessage());
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/security/jwt/HeaderTokenExtractor.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.security.jwt;
2 |
3 | import com.wheejuni.jwtdemo.security.InvalidJwtException;
4 | import org.springframework.stereotype.Component;
5 | import org.springframework.util.StringUtils;
6 |
7 | @Component
8 | public class HeaderTokenExtractor {
9 |
10 | public static final String HEADER_PREFIX = "Bearer ";
11 |
12 | public String extract(String header) {
13 |
14 | if(StringUtils.isEmpty(header) | header.length() < HEADER_PREFIX.length()) {
15 | throw new InvalidJwtException("올바른 토큰 정보가 아닙니다.");
16 | }
17 |
18 | return header.substring(HEADER_PREFIX.length(), header.length());
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/security/jwt/JwtDecoder.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.security.jwt;
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.interfaces.DecodedJWT;
7 | import com.wheejuni.jwtdemo.security.AccountContext;
8 | import com.wheejuni.jwtdemo.security.InvalidJwtException;
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 | import org.springframework.stereotype.Component;
12 |
13 | import java.util.Optional;
14 |
15 | @Component
16 | public class JwtDecoder {
17 |
18 | private static final Logger log = LoggerFactory.getLogger(JwtDecoder.class);
19 |
20 | public AccountContext decodeJwt(String token) {
21 | DecodedJWT decodedJWT = isValidToken(token).orElseThrow(() -> new InvalidJwtException("유효한 토큰아 아닙니다."));
22 |
23 | String username = decodedJWT.getClaim("USERNAME").asString();
24 | String role = decodedJWT.getClaim("USER_ROLE").asString();
25 |
26 | return new AccountContext(username, "1234", role);
27 | }
28 |
29 | private Optional isValidToken(String token) {
30 |
31 | DecodedJWT jwt = null;
32 |
33 | try {
34 | Algorithm algorithm = Algorithm.HMAC256("jwttest");
35 | JWTVerifier verifier = JWT.require(algorithm).build();
36 |
37 | jwt = verifier.verify(token);
38 | } catch (Exception e) {
39 | log.error(e.getMessage());
40 | }
41 |
42 | return Optional.ofNullable(jwt);
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/security/jwt/JwtFactory.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.security.jwt;
2 |
3 | import com.auth0.jwt.JWT;
4 | import com.auth0.jwt.algorithms.Algorithm;
5 | import com.wheejuni.jwtdemo.security.AccountContext;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 | import org.springframework.stereotype.Component;
9 |
10 | import java.io.UnsupportedEncodingException;
11 |
12 | @Component
13 | public class JwtFactory {
14 |
15 | private static final Logger log = LoggerFactory.getLogger(JwtFactory.class);
16 |
17 | private static String signingKey = "jwttest";
18 |
19 | public String generateToken(AccountContext context) {
20 |
21 | String token = null;
22 |
23 |
24 |
25 | try {
26 | token = JWT.create()
27 | .withIssuer("bomeehouse")
28 | .withClaim("USERNAME", context.getAccount().getUserId())
29 | .withClaim("USER_ROLE", context.getAccount().getUserRole().getRoleName())
30 | .sign(generateAlgorithm());
31 |
32 | } catch (Exception e) {
33 | log.error(e.getMessage());
34 | }
35 |
36 | return token;
37 | }
38 |
39 | private Algorithm generateAlgorithm() throws UnsupportedEncodingException {
40 | return Algorithm.HMAC256(signingKey);
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/security/providers/FormLoginAuthenticationProvider.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.security.providers;
2 |
3 | import com.wheejuni.jwtdemo.domain.Account;
4 | import com.wheejuni.jwtdemo.domain.AccountRepository;
5 | import com.wheejuni.jwtdemo.security.AccountContext;
6 | import com.wheejuni.jwtdemo.security.AccountContextService;
7 | import com.wheejuni.jwtdemo.security.tokens.PostAuthorizationToken;
8 | import com.wheejuni.jwtdemo.security.tokens.PreAuthorizationToken;
9 | import org.springframework.beans.factory.annotation.Autowired;
10 | import org.springframework.security.authentication.AuthenticationProvider;
11 | import org.springframework.security.core.Authentication;
12 | import org.springframework.security.core.AuthenticationException;
13 | import org.springframework.security.crypto.password.PasswordEncoder;
14 | import org.springframework.stereotype.Component;
15 |
16 | import java.util.NoSuchElementException;
17 |
18 | @Component
19 | public class FormLoginAuthenticationProvider implements AuthenticationProvider {
20 |
21 | @Autowired
22 | private PasswordEncoder passwordEncoder;
23 |
24 | @Autowired
25 | private AccountRepository accountRepository;
26 |
27 | @Override
28 | public Authentication authenticate(Authentication authentication) throws AuthenticationException {
29 |
30 | PreAuthorizationToken token = (PreAuthorizationToken)authentication;
31 |
32 | String username = token.getUsername();
33 | String password = token.getUserPassword();
34 |
35 | Account account = accountRepository.findByUserId(username).orElseThrow(() -> new NoSuchElementException("정보에 맞는 계정이 없습니다."));
36 |
37 | if(isCorrectPassword(password, account)) {
38 | return PostAuthorizationToken.getTokenFromAccountContext(AccountContext.fromAccountModel(account));
39 | }
40 |
41 | throw new NoSuchElementException("인증 정보가 정확하지 않습니다.");
42 | }
43 |
44 | @Override
45 | public boolean supports(Class> aClass) {
46 | return PreAuthorizationToken.class.isAssignableFrom(aClass);
47 | }
48 |
49 | private boolean isCorrectPassword(String password, Account account) {
50 | return passwordEncoder.matches(password, account.getPassword());
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/security/providers/JwtAuthenticationProvider.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.security.providers;
2 |
3 | import com.wheejuni.jwtdemo.security.AccountContext;
4 | import com.wheejuni.jwtdemo.security.jwt.JwtDecoder;
5 | import com.wheejuni.jwtdemo.security.tokens.JwtPreProcessingToken;
6 | import com.wheejuni.jwtdemo.security.tokens.PostAuthorizationToken;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.security.authentication.AuthenticationProvider;
9 | import org.springframework.security.core.Authentication;
10 | import org.springframework.security.core.AuthenticationException;
11 | import org.springframework.stereotype.Component;
12 |
13 | @Component
14 | public class JwtAuthenticationProvider implements AuthenticationProvider {
15 |
16 | @Autowired
17 | private JwtDecoder jwtDecoder;
18 |
19 | @Override
20 | public Authentication authenticate(Authentication authentication) throws AuthenticationException {
21 |
22 | String token = (String)authentication.getPrincipal();
23 | AccountContext context = jwtDecoder.decodeJwt(token);
24 |
25 | return PostAuthorizationToken.getTokenFromAccountContext(context);
26 | }
27 |
28 | @Override
29 | public boolean supports(Class> aClass) {
30 | return JwtPreProcessingToken.class.isAssignableFrom(aClass);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/security/providers/SocialLoginAuthenticationProvider.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.security.providers;
2 |
3 | import com.wheejuni.jwtdemo.domain.Account;
4 | import com.wheejuni.jwtdemo.domain.AccountRepository;
5 | import com.wheejuni.jwtdemo.domain.SocialProviders;
6 | import com.wheejuni.jwtdemo.domain.UserRole;
7 | import com.wheejuni.jwtdemo.dtos.SocialLoginDto;
8 | import com.wheejuni.jwtdemo.security.AccountContext;
9 | import com.wheejuni.jwtdemo.security.services.specification.SocialFetchService;
10 | import com.wheejuni.jwtdemo.security.social.SocialUserProperty;
11 | import com.wheejuni.jwtdemo.security.tokens.PostAuthorizationToken;
12 | import com.wheejuni.jwtdemo.security.tokens.SocialPreAuthorizationToken;
13 | import org.springframework.beans.factory.annotation.Autowired;
14 | import org.springframework.beans.factory.annotation.Qualifier;
15 | import org.springframework.security.authentication.AuthenticationProvider;
16 | import org.springframework.security.core.Authentication;
17 | import org.springframework.security.core.AuthenticationException;
18 | import org.springframework.stereotype.Component;
19 |
20 | import java.util.UUID;
21 |
22 | @Component
23 | public class SocialLoginAuthenticationProvider implements AuthenticationProvider {
24 |
25 | @Autowired
26 | private AccountRepository accountRepository;
27 |
28 | @Qualifier("socialFetchServiceProd")
29 | @Autowired
30 | private SocialFetchService service;
31 |
32 | @Override
33 | public Authentication authenticate(Authentication authentication) throws AuthenticationException {
34 | SocialPreAuthorizationToken token = (SocialPreAuthorizationToken)authentication;
35 | SocialLoginDto dto = token.getDto();
36 |
37 | return PostAuthorizationToken.getTokenFromAccountContext(AccountContext.fromAccountModel(getAccount(dto)));
38 | }
39 |
40 | @Override
41 | public boolean supports(Class> aClass) {
42 | return SocialPreAuthorizationToken.class.isAssignableFrom(aClass);
43 | }
44 |
45 | private Account getAccount(SocialLoginDto dto) {
46 | SocialUserProperty property = service.getSocialUserInfo(dto);
47 |
48 | String userId = property.getUserId();
49 | SocialProviders provider = dto.getProvider();
50 |
51 | return accountRepository.findBySocialIdAndSocialProvider(Long.valueOf(userId), provider)
52 | .orElseGet(() -> accountRepository.save(
53 | new Account(null, property.getUserNickname(), "SOCIAL_USER", String.valueOf(UUID.randomUUID().getMostSignificantBits()), UserRole.USER, Long.valueOf(property.getUserId()), provider, property.getProfileHref())));
54 |
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/security/services/impl/SocialFetchServiceProd.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.security.services.impl;
2 |
3 | import com.wheejuni.jwtdemo.domain.SocialProviders;
4 | import com.wheejuni.jwtdemo.dtos.SocialLoginDto;
5 | import com.wheejuni.jwtdemo.security.services.specification.SocialFetchService;
6 | import com.wheejuni.jwtdemo.security.social.SocialUserProperty;
7 | import org.springframework.beans.factory.annotation.Value;
8 | import org.springframework.http.HttpEntity;
9 | import org.springframework.http.HttpHeaders;
10 | import org.springframework.http.HttpMethod;
11 | import org.springframework.stereotype.Service;
12 | import org.springframework.web.client.RestTemplate;
13 |
14 | @Service
15 | public class SocialFetchServiceProd implements SocialFetchService {
16 |
17 | private static final String HEADER_PREFIX = "Bearer ";
18 |
19 | @Override
20 | public SocialUserProperty getSocialUserInfo(SocialLoginDto dto) {
21 | SocialProviders provider = dto.getProvider();
22 | RestTemplate restTemplate = new RestTemplate();
23 |
24 | HttpEntity entity = new HttpEntity<>("parameter", generateHeader(dto.getToken()));
25 |
26 | return restTemplate.exchange(provider.getUserinfoEndpoint(), HttpMethod.GET, entity, provider.getPropertyMetaclass()).getBody();
27 | }
28 |
29 | private HttpHeaders generateHeader(String token) {
30 | HttpHeaders header = new HttpHeaders();
31 |
32 | header.add("Authorization", generateHeaderContent(token));
33 | return header;
34 | }
35 |
36 | private String generateHeaderContent(String token) {
37 | StringBuilder sb = new StringBuilder();
38 |
39 | sb.append(HEADER_PREFIX);
40 | sb.append(token);
41 |
42 | return sb.toString();
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/security/services/impl/SocialFetchServiceTest.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.security.services.impl;
2 |
3 | import com.wheejuni.jwtdemo.dtos.SocialLoginDto;
4 | import com.wheejuni.jwtdemo.security.services.specification.SocialFetchService;
5 | import com.wheejuni.jwtdemo.security.social.SocialUserProperty;
6 | import org.springframework.stereotype.Service;
7 |
8 | @Service
9 | public class SocialFetchServiceTest implements SocialFetchService {
10 |
11 | @Override
12 | public SocialUserProperty getSocialUserInfo(SocialLoginDto dto) {
13 | return null;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/security/services/specification/SocialFetchService.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.security.services.specification;
2 |
3 | import com.wheejuni.jwtdemo.dtos.SocialLoginDto;
4 | import com.wheejuni.jwtdemo.security.social.SocialUserProperty;
5 |
6 | public interface SocialFetchService {
7 |
8 | SocialUserProperty getSocialUserInfo(SocialLoginDto dto);
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/security/social/KakaoUserProperty.kt:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.security.social
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty
4 |
5 | data class KakaoUserProperty(
6 |
7 | @field:JsonProperty("kaccount_email")
8 | private val userEmail: String? = null,
9 |
10 | @field:JsonProperty("kaccount_email_verified")
11 | val verified: Boolean = false,
12 |
13 | @field:JsonProperty("id")
14 | private val userUniqueId: Long? = null,
15 |
16 | @field:JsonProperty("properties")
17 | private val userProperties: MutableMap? = null): SocialUserProperty {
18 |
19 | override fun getUserId(): String {
20 | return userUniqueId.toString()
21 | }
22 |
23 | override fun getUserNickname(): String {
24 | return userProperties!!["nickname"]!!
25 | }
26 |
27 | override fun getProfileHref(): String {
28 | return userProperties!!["profile_image"]!!
29 | }
30 |
31 | override fun getEmail(): String {
32 | return userEmail!!
33 | }
34 |
35 |
36 | }
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/security/social/NaverUserProperty.kt:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.security.social
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty
4 |
5 | data class NaverUserProperty(
6 |
7 | @field:JsonProperty("resultcode")
8 | val resultcode: String? = null,
9 |
10 | @field:JsonProperty("message")
11 | val message: String? = null,
12 |
13 | @field:JsonProperty("response")
14 | private val properties: MutableMap? = null): SocialUserProperty {
15 |
16 | override fun getUserId(): String {
17 | return properties!!["id"]!!
18 | }
19 |
20 | override fun getUserNickname(): String {
21 | return properties!!["nickname"]!!
22 | }
23 |
24 | override fun getProfileHref(): String {
25 | return properties!!["profile_image"]!!
26 | }
27 |
28 | override fun getEmail(): String {
29 | return properties!!["email"]!!
30 | }
31 |
32 | }
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/security/social/SocialUserProperty.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.security.social;
2 |
3 | public interface SocialUserProperty {
4 |
5 | String getUserId();
6 |
7 | String getUserNickname();
8 |
9 | String getProfileHref();
10 |
11 | String getEmail();
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/security/tokens/JwtPostProcessingToken.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.security.tokens;
2 |
3 | import com.wheejuni.jwtdemo.domain.UserRole;
4 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
5 | import org.springframework.security.core.GrantedAuthority;
6 | import org.springframework.security.core.authority.SimpleGrantedAuthority;
7 |
8 | import java.util.Arrays;
9 | import java.util.Collection;
10 | import java.util.stream.Collectors;
11 |
12 | public class JwtPostProcessingToken extends UsernamePasswordAuthenticationToken {
13 |
14 | private JwtPostProcessingToken(Object principal, Object credentials, Collection extends GrantedAuthority> authorities) {
15 | super(principal, credentials, authorities);
16 | }
17 |
18 | public JwtPostProcessingToken(String username, UserRole role) {
19 | super(username, "1234", parseAuthorities(role));
20 | }
21 |
22 | private static Collection extends GrantedAuthority> parseAuthorities(UserRole role) {
23 | return Arrays.asList(role).stream().map(r -> new SimpleGrantedAuthority(r.getRoleName())).collect(Collectors.toList());
24 | }
25 |
26 | public String getUserid() {
27 | return (String)super.getPrincipal();
28 | }
29 |
30 | public String getPassword() {
31 | return (String)super.getCredentials();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/security/tokens/JwtPreProcessingToken.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.security.tokens;
2 |
3 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
4 |
5 | public class JwtPreProcessingToken extends UsernamePasswordAuthenticationToken {
6 |
7 | private JwtPreProcessingToken(Object principal, Object credentials) {
8 | super(principal, credentials);
9 | }
10 |
11 | public JwtPreProcessingToken(String token) {
12 | this(token, token.length());
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/security/tokens/PostAuthorizationToken.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.security.tokens;
2 |
3 | import com.wheejuni.jwtdemo.security.AccountContext;
4 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
5 | import org.springframework.security.core.GrantedAuthority;
6 |
7 | import java.util.Collection;
8 |
9 | public class PostAuthorizationToken extends UsernamePasswordAuthenticationToken {
10 |
11 | private PostAuthorizationToken(Object principal, Object credentials, Collection extends GrantedAuthority> authorities) {
12 | super(principal, credentials, authorities);
13 | }
14 |
15 | public AccountContext getAccountContext() {
16 | return (AccountContext)super.getPrincipal();
17 | }
18 |
19 | public static PostAuthorizationToken getTokenFromAccountContext(AccountContext context) {
20 | return new PostAuthorizationToken(context, context.getPassword(), context.getAuthorities());
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/security/tokens/PreAuthorizationToken.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.security.tokens;
2 |
3 | import com.wheejuni.jwtdemo.domain.SocialProviders;
4 | import com.wheejuni.jwtdemo.dtos.FormLoginDto;
5 | import com.wheejuni.jwtdemo.dtos.SocialLoginDto;
6 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
7 |
8 | public class PreAuthorizationToken extends UsernamePasswordAuthenticationToken {
9 |
10 | private PreAuthorizationToken(String username, String password) {
11 | super(username, password);
12 | }
13 |
14 | private PreAuthorizationToken(SocialProviders providers, SocialLoginDto dto) {
15 | super(providers, dto);
16 | }
17 |
18 | public PreAuthorizationToken(FormLoginDto dto) {
19 | this(dto.getId(), dto.getPassword());
20 | }
21 |
22 | public PreAuthorizationToken(SocialLoginDto dto) {
23 | this(dto.getProvider(), dto);
24 | }
25 |
26 | public String getUsername() {
27 | return (String)super.getPrincipal();
28 | }
29 |
30 | public String getUserPassword() {
31 | return (String)super.getCredentials();
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/com/wheejuni/jwtdemo/security/tokens/SocialPreAuthorizationToken.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.security.tokens;
2 |
3 | import com.wheejuni.jwtdemo.dtos.SocialLoginDto;
4 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
5 |
6 | public class SocialPreAuthorizationToken extends UsernamePasswordAuthenticationToken {
7 |
8 | public SocialPreAuthorizationToken(SocialLoginDto dto) {
9 | super(dto.getProvider(), dto);
10 | }
11 |
12 | public SocialLoginDto getDto() {
13 | return (SocialLoginDto)super.getCredentials();
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | profiles:
3 | active: local-dev
4 |
5 | ---
6 | spring:
7 | profiles: local-dev
8 | datasource:
9 | url: jdbc:h2:~/jwt-demo;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;AUTO_SERVER=true
10 | driver-class-name: org.h2.Driver
11 | username: sa
12 | password:
13 | platform: org.hibernate.dialect.H2Dialect
14 |
15 | h2:
16 | console:
17 | enabled: true
18 | jpa:
19 | hibernate:
20 | ddl-auto: create
21 | show-sql: true
22 |
23 | jwt:
24 | signingKey: test
25 | issuer: bomee
26 | expiry: 14400
--------------------------------------------------------------------------------
/src/test/java/com/wheejuni/jwtdemo/JwtdemoApplicationTests.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo;
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 JwtdemoApplicationTests {
11 |
12 | @Test
13 | public void contextLoads() {
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/test/java/com/wheejuni/jwtdemo/security/HeaderTokenExtractorTest.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.security;
2 |
3 | import com.wheejuni.jwtdemo.security.jwt.HeaderTokenExtractor;
4 | import org.junit.Before;
5 | import org.junit.Test;
6 |
7 | import static org.hamcrest.CoreMatchers.is;
8 | import static org.junit.Assert.*;
9 |
10 | public class HeaderTokenExtractorTest {
11 |
12 | private HeaderTokenExtractor extractor = new HeaderTokenExtractor();
13 | private String header;
14 |
15 | @Before
16 | public void setUp() {
17 | this.header = "Bearer asdfhakjsdh.asdfdhfkhdkfj";
18 | }
19 |
20 | @Test
21 | public void TEST_JWT_EXTRACT() {
22 | assertThat(extractor.extract(this.header), is("asdfhakjsdh.asdfdhfkhdkfj"));
23 | }
24 |
25 | }
--------------------------------------------------------------------------------
/src/test/java/com/wheejuni/jwtdemo/security/JwtFactoryTest.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.security;
2 |
3 | import com.wheejuni.jwtdemo.domain.Account;
4 | import com.wheejuni.jwtdemo.security.jwt.JwtFactory;
5 | import org.junit.Before;
6 | import org.junit.Test;
7 | import org.junit.runner.RunWith;
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 | import org.springframework.beans.factory.annotation.Autowired;
11 | import org.springframework.boot.test.context.SpringBootTest;
12 | import org.springframework.test.context.junit4.SpringRunner;
13 |
14 | @SpringBootTest
15 | @RunWith(SpringRunner.class)
16 | public class JwtFactoryTest {
17 |
18 | private static final Logger log = LoggerFactory.getLogger(JwtFactoryTest.class);
19 |
20 | private AccountContext context;
21 |
22 | @Autowired
23 | private JwtFactory factory;
24 |
25 | @Before
26 | public void setUp() {
27 | Account account = new Account();
28 | log.error("userid: {}, password: {}, role: {}", account.getUserId(), account.getPassword(), account.getUserRole());
29 | this.context = AccountContext.fromAccountModel(account);
30 | }
31 |
32 | @Test
33 | public void TEST_JWT_GENERATE() {
34 | log.error(factory.generateToken(this.context));
35 | }
36 | }
--------------------------------------------------------------------------------
/src/test/java/com/wheejuni/jwtdemo/security/services/specification/SocialFetchServiceTest.java:
--------------------------------------------------------------------------------
1 | package com.wheejuni.jwtdemo.security.services.specification;
2 |
3 | import com.wheejuni.jwtdemo.domain.SocialProviders;
4 | import com.wheejuni.jwtdemo.dtos.SocialLoginDto;
5 | import com.wheejuni.jwtdemo.security.services.impl.SocialFetchServiceProd;
6 | import com.wheejuni.jwtdemo.security.social.SocialUserProperty;
7 | import lombok.extern.slf4j.Slf4j;
8 | import org.junit.Before;
9 | import org.junit.Test;
10 | import org.springframework.boot.test.context.SpringBootTest;
11 | import org.springframework.web.client.RestTemplate;
12 |
13 | import static org.hamcrest.CoreMatchers.is;
14 | import static org.junit.Assert.*;
15 |
16 | @Slf4j
17 | public class SocialFetchServiceTest {
18 |
19 | private SocialFetchServiceProd prod = new SocialFetchServiceProd();
20 | private SocialLoginDto dto;
21 |
22 | @Before
23 | public void setUp() {
24 | this.dto = new SocialLoginDto(SocialProviders.KAKAO, "lxLHHcv9X8Qt2xxpgdgME7n7oxpX9eINQ_3i4wopdtYAAAFjRJ6Fhw");
25 | }
26 |
27 |
28 | @Test
29 | public void restTemplate_Practice1() {
30 | log.debug(new RestTemplate().getForObject("http://www.naver.com", String.class));
31 | }
32 |
33 | @Test
34 | public void service_fetchSocialInfo() {
35 | SocialUserProperty property = prod.getSocialUserInfo(this.dto);
36 |
37 | assertThat(property.getEmail(), is("wheejuni@gmail.com"));
38 | }
39 |
40 | }
--------------------------------------------------------------------------------
/test-http/hello.http:
--------------------------------------------------------------------------------
1 | GET http://localhost:8080/api/hello
2 | Authorization: Bearer token_value
3 |
4 | ###
--------------------------------------------------------------------------------
/test-http/loggedin-request.http:
--------------------------------------------------------------------------------
1 | GET http://localhost:8080/api/hello
2 | Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJib21lZWhvdXNlIiwiVVNFUk5BTUUiOiJlbWFseXVuQG5hdmVyLmNvbSIsIlVTRVJfUk9MRSI6IlJPTEVfVVNFUiJ9.xE3sdMteckD1zO01cvqCWYijxy5GwBPYz7y7vC6I5hU
3 | ###
4 |
--------------------------------------------------------------------------------
/test-http/login-naver.http:
--------------------------------------------------------------------------------
1 | GET https://nid.naver.com/oauth2.0/token
2 |
--------------------------------------------------------------------------------
/test-http/login-request.http:
--------------------------------------------------------------------------------
1 | POST http://localhost:8080/formlogin
2 | Content-Type: application/json
3 |
4 | {"userid" : "emalyun@naver.com", "password" : "1234"}
5 |
6 | ###
7 |
--------------------------------------------------------------------------------
/test-http/login-social.http:
--------------------------------------------------------------------------------
1 | POST http://localhost:8080/social
2 | Content-Type: application/json
3 |
4 | {"provider" : "NAVER", "token" : "AAAAQnNiK8JK2IpwABX/7P8A5dPfoiqkl9cbfe6f1HiqVnHBhk4Gsdw0AEvIVtKQSHqV2d6kpE00uXtSPp8r8HAFqtN1TlbWQCoEt89XTvYzrxD5"}
5 |
6 | ###
--------------------------------------------------------------------------------