├── .gitignore
├── .mvn
└── wrapper
│ ├── maven-wrapper.jar
│ └── maven-wrapper.properties
├── README.md
├── mvnw
├── mvnw.cmd
├── oauth2-opaque-resource
├── README.md
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── marcosbarbero
│ │ └── lab
│ │ └── sec
│ │ └── oauth
│ │ └── opaque
│ │ └── ds
│ │ ├── ResourceServerOpaqueApplication.java
│ │ ├── config
│ │ ├── ResourceServerConfiguration.java
│ │ └── WebSecurityConfiguration.java
│ │ └── web
│ │ └── UserController.java
│ └── resources
│ └── application.yml
├── oauth2-opaque-server
├── README.md
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── marcosbarbero
│ │ └── lab
│ │ └── sec
│ │ └── oauth
│ │ └── opaque
│ │ ├── OAuth2ServerOpaqueApplication.java
│ │ ├── config
│ │ ├── AuthorizationServerConfiguration.java
│ │ ├── ResourceServerConfiguration.java
│ │ └── WebSecurityConfiguration.java
│ │ └── web
│ │ └── ProfileController.java
│ └── resources
│ ├── application.yml
│ ├── data.sql
│ └── schema.sql
└── pom.xml
/.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 | # OS
28 |
29 | .DS_Store
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/marcosbarbero/spring-boot2-oauth2-opaque-token/ffc3e60c2068b1748cc3d56c1e87a54d2f0934df/.mvn/wrapper/maven-wrapper.jar
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | OAuth2 + Opaque Token using Spring Boot 2 / Spring Security 5
2 | ---
3 |
4 | Read more http://blog.marcosbarbero.com/oauth2-centralized-authorization-opaque-jdbc-spring-boot2/
--------------------------------------------------------------------------------
/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 Mingw, 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 | ##########################################################################################
204 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
205 | # This allows using the maven wrapper in projects that prohibit checking in binary data.
206 | ##########################################################################################
207 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
208 | if [ "$MVNW_VERBOSE" = true ]; then
209 | echo "Found .mvn/wrapper/maven-wrapper.jar"
210 | fi
211 | else
212 | if [ "$MVNW_VERBOSE" = true ]; then
213 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
214 | fi
215 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"
216 | while IFS="=" read key value; do
217 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
218 | esac
219 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
220 | if [ "$MVNW_VERBOSE" = true ]; then
221 | echo "Downloading from: $jarUrl"
222 | fi
223 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
224 |
225 | if command -v wget > /dev/null; then
226 | if [ "$MVNW_VERBOSE" = true ]; then
227 | echo "Found wget ... using wget"
228 | fi
229 | wget "$jarUrl" -O "$wrapperJarPath"
230 | elif command -v curl > /dev/null; then
231 | if [ "$MVNW_VERBOSE" = true ]; then
232 | echo "Found curl ... using curl"
233 | fi
234 | curl -o "$wrapperJarPath" "$jarUrl"
235 | else
236 | if [ "$MVNW_VERBOSE" = true ]; then
237 | echo "Falling back to using Java to download"
238 | fi
239 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
240 | if [ -e "$javaClass" ]; then
241 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
242 | if [ "$MVNW_VERBOSE" = true ]; then
243 | echo " - Compiling MavenWrapperDownloader.java ..."
244 | fi
245 | # Compiling the Java class
246 | ("$JAVA_HOME/bin/javac" "$javaClass")
247 | fi
248 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
249 | # Running the downloader
250 | if [ "$MVNW_VERBOSE" = true ]; then
251 | echo " - Running MavenWrapperDownloader.java ..."
252 | fi
253 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
254 | fi
255 | fi
256 | fi
257 | fi
258 | ##########################################################################################
259 | # End of extension
260 | ##########################################################################################
261 |
262 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
263 | if [ "$MVNW_VERBOSE" = true ]; then
264 | echo $MAVEN_PROJECTBASEDIR
265 | fi
266 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
267 |
268 | # For Cygwin, switch paths to Windows format before running java
269 | if $cygwin; then
270 | [ -n "$M2_HOME" ] &&
271 | M2_HOME=`cygpath --path --windows "$M2_HOME"`
272 | [ -n "$JAVA_HOME" ] &&
273 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
274 | [ -n "$CLASSPATH" ] &&
275 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
276 | [ -n "$MAVEN_PROJECTBASEDIR" ] &&
277 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
278 | fi
279 |
280 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
281 |
282 | exec "$JAVACMD" \
283 | $MAVEN_OPTS \
284 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
285 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
286 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
287 |
--------------------------------------------------------------------------------
/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 set title of command window
39 | title %0
40 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
42 |
43 | @REM set %HOME% to equivalent of $HOME
44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
45 |
46 | @REM Execute a user defined script before this one
47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending
49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
51 | :skipRcPre
52 |
53 | @setlocal
54 |
55 | set ERROR_CODE=0
56 |
57 | @REM To isolate internal variables from possible post scripts, we use another setlocal
58 | @setlocal
59 |
60 | @REM ==== START VALIDATION ====
61 | if not "%JAVA_HOME%" == "" goto OkJHome
62 |
63 | echo.
64 | echo Error: JAVA_HOME not found in your environment. >&2
65 | echo Please set the JAVA_HOME variable in your environment to match the >&2
66 | echo location of your Java installation. >&2
67 | echo.
68 | goto error
69 |
70 | :OkJHome
71 | if exist "%JAVA_HOME%\bin\java.exe" goto init
72 |
73 | echo.
74 | echo Error: JAVA_HOME is set to an invalid directory. >&2
75 | echo JAVA_HOME = "%JAVA_HOME%" >&2
76 | echo Please set the JAVA_HOME variable in your environment to match the >&2
77 | echo location of your Java installation. >&2
78 | echo.
79 | goto error
80 |
81 | @REM ==== END VALIDATION ====
82 |
83 | :init
84 |
85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
86 | @REM Fallback to current working directory if not found.
87 |
88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
90 |
91 | set EXEC_DIR=%CD%
92 | set WDIR=%EXEC_DIR%
93 | :findBaseDir
94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound
95 | cd ..
96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound
97 | set WDIR=%CD%
98 | goto findBaseDir
99 |
100 | :baseDirFound
101 | set MAVEN_PROJECTBASEDIR=%WDIR%
102 | cd "%EXEC_DIR%"
103 | goto endDetectBaseDir
104 |
105 | :baseDirNotFound
106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
107 | cd "%EXEC_DIR%"
108 |
109 | :endDetectBaseDir
110 |
111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
112 |
113 | @setlocal EnableExtensions EnableDelayedExpansion
114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
116 |
117 | :endReadAdditionalConfig
118 |
119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
122 |
123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"
124 | FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO (
125 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
126 | )
127 |
128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data.
130 | if exist %WRAPPER_JAR% (
131 | echo Found %WRAPPER_JAR%
132 | ) else (
133 | echo Couldn't find %WRAPPER_JAR%, downloading it ...
134 | echo Downloading from: %DOWNLOAD_URL%
135 | powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"
136 | echo Finished downloading %WRAPPER_JAR%
137 | )
138 | @REM End of extension
139 |
140 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
141 | if ERRORLEVEL 1 goto error
142 | goto end
143 |
144 | :error
145 | set ERROR_CODE=1
146 |
147 | :end
148 | @endlocal & set ERROR_CODE=%ERROR_CODE%
149 |
150 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
151 | @REM check for post script, once with legacy .bat ending and once with .cmd ending
152 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
153 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
154 | :skipRcPost
155 |
156 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
157 | if "%MAVEN_BATCH_PAUSE%" == "on" pause
158 |
159 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
160 |
161 | exit /B %ERROR_CODE%
162 |
--------------------------------------------------------------------------------
/oauth2-opaque-resource/README.md:
--------------------------------------------------------------------------------
1 | Resource Server Opaque Token - RemoteTokenServices
2 | ---
3 |
4 | The resource server hosts the [HTTP resources](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Identifying_resources_on_the_Web)
5 | in which can be a document a photo or something else, in our case it will be a REST API protected by OAuth2.
6 |
7 | ## Dependencies
8 |
9 | ```xml
10 |
11 |
12 | org.springframework.boot
13 | spring-boot-starter-web
14 |
15 |
16 |
17 | org.springframework.boot
18 | spring-boot-starter-security
19 |
20 |
21 | org.springframework.security.oauth.boot
22 | spring-security-oauth2-autoconfigure
23 | 2.1.2.RELEASE
24 |
25 |
26 |
27 | org.springframework.boot
28 | spring-boot-configuration-processor
29 | true
30 |
31 |
32 | ```
33 |
34 | ## Defining our protected API
35 |
36 | The code bellow defines the endpoint `/me` and returns the `Principal` object and it requires the authenticated
37 | user to have the `ROLE_USER` to access.
38 |
39 | ```java
40 | import org.springframework.http.ResponseEntity;
41 | import org.springframework.security.access.prepost.PreAuthorize;
42 | import org.springframework.web.bind.annotation.GetMapping;
43 | import org.springframework.web.bind.annotation.RequestMapping;
44 | import org.springframework.web.bind.annotation.RestController;
45 |
46 | import java.security.Principal;
47 |
48 | @RestController
49 | @RequestMapping("/me")
50 | public class UserController {
51 |
52 | @GetMapping
53 | @PreAuthorize("hasRole('ROLE_USER')")
54 | public ResponseEntity get(final Principal principal) {
55 | return ResponseEntity.ok(principal);
56 | }
57 |
58 | }
59 | ```
60 |
61 | The `@PreAuthorize` annotation validates whether the user has the given role prior to execute the code, to make it work
62 | it's necessary to enable the `prePost` annotations, to do so add the following class:
63 |
64 | ```java
65 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
66 |
67 | @EnableGlobalMethodSecurity(prePostEnabled = true)
68 | public class WebSecurityConfiguration {
69 |
70 | }
71 | ```
72 |
73 | The important part here is the `@EnableGlobalMethodSecurity(prePostEnabled = true)` annotation, the `prePostEnabled` flag
74 | is set to `false` by default, turning it to `true` makes the `@PreAuthorize` annotation to work.
75 |
76 | ## Resource Server Configuration
77 |
78 | Now let's add the Spring's configuration for the resource server.
79 |
80 | ```java
81 | import org.springframework.context.annotation.Configuration;
82 | import org.springframework.http.HttpMethod;
83 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
84 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
85 | import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
86 |
87 | @Configuration
88 | @EnableResourceServer
89 | public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
90 |
91 | }
92 | ```
93 |
94 | The `@EnableResourceServer` annotation, from the javadoc:
95 |
96 | >Convenient annotation for OAuth2 Resource Servers, enabling a Spring Security filter that authenticates requests via
97 | >an incoming OAuth2 token. Users should add this annotation and provide a @Bean
of type
98 | >{@link ResourceServerConfigurer} (e.g. via {@link ResourceServerConfigurerAdapter}) that specifies the details of the
99 | >resource (URL paths and resource id). In order to use this filter you must {@link EnableWebSecurity}
100 | >somewhere in your application, either in the same place as you use this annotation, or somewhere else.
101 |
102 | Now that we have all the necessary code in place we need to configure a [RemoteTokenServices](https://docs.spring.io/spring-security/oauth/apidocs/org/springframework/security/oauth2/provider/token/RemoteTokenServices.html),
103 | lucky for us Spring provides a configuration property where we can set the url where the tokens can be translated to
104 | an `Authentication` object.
105 |
106 | ```yaml
107 | security:
108 | oauth2:
109 | resource:
110 | user-info-uri: http://localhost:9001/profile/me
111 | ```
--------------------------------------------------------------------------------
/oauth2-opaque-resource/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | com.marcosbarbero.lab
8 | spring-boot2-oauth2-opaque
9 | 0.0.1-SNAPSHOT
10 | ..
11 |
12 |
13 | 4.0.0
14 |
15 | oauth2-opaque-resource
16 |
17 |
20 |
21 |
22 |
23 |
24 |
25 | org.springframework.boot
26 | spring-boot-maven-plugin
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/oauth2-opaque-resource/src/main/java/com/marcosbarbero/lab/sec/oauth/opaque/ds/ResourceServerOpaqueApplication.java:
--------------------------------------------------------------------------------
1 | package com.marcosbarbero.lab.sec.oauth.opaque.ds;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class ResourceServerOpaqueApplication {
8 |
9 | public static void main(String... args) {
10 | SpringApplication.run(ResourceServerOpaqueApplication.class, args);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/oauth2-opaque-resource/src/main/java/com/marcosbarbero/lab/sec/oauth/opaque/ds/config/ResourceServerConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.marcosbarbero.lab.sec.oauth.opaque.ds.config;
2 |
3 | import org.springframework.context.annotation.Configuration;
4 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
5 | import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
6 |
7 | @Configuration
8 | @EnableResourceServer
9 | public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/oauth2-opaque-resource/src/main/java/com/marcosbarbero/lab/sec/oauth/opaque/ds/config/WebSecurityConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.marcosbarbero.lab.sec.oauth.opaque.ds.config;
2 |
3 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
4 |
5 | @EnableGlobalMethodSecurity(prePostEnabled = true)
6 | public class WebSecurityConfiguration {
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/oauth2-opaque-resource/src/main/java/com/marcosbarbero/lab/sec/oauth/opaque/ds/web/UserController.java:
--------------------------------------------------------------------------------
1 | package com.marcosbarbero.lab.sec.oauth.opaque.ds.web;
2 |
3 | import org.springframework.http.ResponseEntity;
4 | import org.springframework.security.access.prepost.PreAuthorize;
5 | import org.springframework.web.bind.annotation.GetMapping;
6 | import org.springframework.web.bind.annotation.RequestMapping;
7 | import org.springframework.web.bind.annotation.RestController;
8 |
9 | import java.security.Principal;
10 |
11 | @RestController
12 | @RequestMapping("/me")
13 | public class UserController {
14 |
15 | @GetMapping
16 | @PreAuthorize("hasRole('ROLE_USER')")
17 | public ResponseEntity get(final Principal principal) {
18 | return ResponseEntity.ok(principal);
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/oauth2-opaque-resource/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | server:
2 | port: 9101
3 |
4 | security:
5 | oauth2:
6 | resource:
7 | user-info-uri: http://localhost:9001/profile/me
8 |
9 | spring:
10 | jackson:
11 | serialization:
12 | INDENT_OUTPUT: true
--------------------------------------------------------------------------------
/oauth2-opaque-server/README.md:
--------------------------------------------------------------------------------
1 | OAuth2 Centralized Authorization with Opaque Tokens using Spring Boot 2
2 | ---
3 |
4 | >Published on http://blog.marcosbarbero.com/oauth2-centralized-authorization-opaque-jdbc-spring-boot2/
5 |
6 | This guide walks through the process to create a centralized authentication and authorization server with Spring Boot 2,
7 | a demo resource server will also be provided.
8 |
9 | >If you're not familiar with OAuth2 I recommend this [read](https://www.oauth.com/).
10 |
11 | ## Pre-req
12 |
13 | - [JDK 1.8](https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html)
14 | - Text editor or your favorite IDE
15 | - [Maven 3.0+](https://maven.apache.org/download.cgi)
16 |
17 | ## Implementation Overview
18 |
19 | For this project we'll be using [Spring Security 5](https://spring.io/projects/spring-security) through Spring Boot.
20 | If you're familiar with the earlier versions this [Spring Boot Migration Guide](https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Migration-Guide#oauth2)
21 | might be useful.
22 |
23 | ## OAuth2 Terminology
24 |
25 | - **Resource Owner**
26 | - The user who authorizes an application to access his account. The access is limited to the `scope`.
27 | - **Resource Server**:
28 | - A server that handles authenticated requests after the `client` has obtained an `access token`.
29 | - **Client**
30 | - An application that access protected resources on behalf of the resource owner.
31 | - **Authorization Server**
32 | - A server which issues access tokens after successfully authenticating a `client` and `resource owner`, and authorizing the request.
33 | - **Access Token**
34 | - A unique token used to access protected resources
35 | - **Scope**
36 | - A Permission
37 | - **Grant type**
38 | - A `grant` is a method of acquiring an access token.
39 | - [Read more about grant types here](https://oauth.net/2/grant-types/)
40 |
41 | ### Authorization Server
42 |
43 | To build our `Authorization Server` we'll be using [Spring Security 5.x](https://spring.io/projects/spring-security) through
44 | [Spring Boot 2.0.x](https://spring.io/projects/spring-boot).
45 |
46 | #### Dependencies
47 |
48 | You can go to [start.spring.io](https://start.spring.io/) and generate a new project and then add the following dependencies:
49 |
50 | ```xml
51 |
52 |
53 | org.springframework.boot
54 | spring-boot-starter-web
55 |
56 |
57 |
58 | org.springframework.boot
59 | spring-boot-starter-security
60 |
61 |
62 | org.springframework.security.oauth.boot
63 | spring-security-oauth2-autoconfigure
64 | 2.1.2.RELEASE
65 |
66 |
67 |
68 | org.springframework.boot
69 | spring-boot-starter-jdbc
70 |
71 |
72 |
73 | org.springframework.boot
74 | spring-boot-configuration-processor
75 | true
76 |
77 |
78 |
79 | com.h2database
80 | h2
81 | runtime
82 |
83 |
84 | ```
85 |
86 | #### Database
87 |
88 | For the sake of this guide we'll be using [H2 Database](http://www.h2database.com/html/main.html).
89 | Here you can find a reference OAuth2 SQL schema required by Spring Security.
90 |
91 | ```sql
92 | CREATE TABLE IF NOT EXISTS oauth_client_details (
93 | client_id VARCHAR(256) PRIMARY KEY,
94 | resource_ids VARCHAR(256),
95 | client_secret VARCHAR(256) NOT NULL,
96 | scope VARCHAR(256),
97 | authorized_grant_types VARCHAR(256),
98 | web_server_redirect_uri VARCHAR(256),
99 | authorities VARCHAR(256),
100 | access_token_validity INTEGER,
101 | refresh_token_validity INTEGER,
102 | additional_information VARCHAR(4000),
103 | autoapprove VARCHAR(256)
104 | );
105 |
106 | CREATE TABLE IF NOT EXISTS oauth_client_token (
107 | token_id VARCHAR(256),
108 | token BLOB,
109 | authentication_id VARCHAR(256) PRIMARY KEY,
110 | user_name VARCHAR(256),
111 | client_id VARCHAR(256)
112 | );
113 |
114 | CREATE TABLE IF NOT EXISTS oauth_access_token (
115 | token_id VARCHAR(256),
116 | token BLOB,
117 | authentication_id VARCHAR(256),
118 | user_name VARCHAR(256),
119 | client_id VARCHAR(256),
120 | authentication BLOB,
121 | refresh_token VARCHAR(256)
122 | );
123 |
124 | CREATE TABLE IF NOT EXISTS oauth_refresh_token (
125 | token_id VARCHAR(256),
126 | token BLOB,
127 | authentication BLOB
128 | );
129 |
130 | CREATE TABLE IF NOT EXISTS oauth_code (
131 | code VARCHAR(256), authentication BLOB
132 | );
133 | ```
134 |
135 | And then add the following entry
136 |
137 | ```sql
138 | -- The encrypted client_secret it `secret`
139 | INSERT INTO oauth_client_details (client_id, client_secret, scope, authorized_grant_types, authorities, access_token_validity)
140 | VALUES ('clientId', '{bcrypt}$2a$10$vCXMWCn7fDZWOcLnIEhmK.74dvK1Eh8ae2WrWlhr2ETPLoxQctN4.', 'read,write', 'password,refresh_token,client_credentials', 'ROLE_CLIENT', 300);
141 | ```
142 |
143 | >The `client_secret` above was generated using [bcrypt](https://en.wikipedia.org/wiki/Bcrypt).
144 | >The prefix `{bcrypt}` is required because we'll using Spring Security 5.x's new feature of [DelegatingPasswordEncoder](https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#pe-dpe).
145 |
146 | Bellow here you can find the `User` and `Authority` reference SQL schema used by Spring's `org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl`.
147 |
148 | ```sql
149 | CREATE TABLE IF NOT EXISTS users (
150 | id INT AUTO_INCREMENT PRIMARY KEY,
151 | username VARCHAR(256) NOT NULL,
152 | password VARCHAR(256) NOT NULL,
153 | enabled TINYINT(1),
154 | UNIQUE KEY unique_username(username)
155 | );
156 |
157 | CREATE TABLE IF NOT EXISTS authorities (
158 | username VARCHAR(256) NOT NULL,
159 | authority VARCHAR(256) NOT NULL,
160 | PRIMARY KEY(username, authority)
161 | );
162 | ```
163 |
164 | Same as before add the following entries for the user and its authority.
165 |
166 | ```sql
167 | -- The encrypted password is `pass`
168 | INSERT INTO users (id, username, password, enabled) VALUES (1, 'user', '{bcrypt}$2a$10$cyf5NfobcruKQ8XGjUJkEegr9ZWFqaea6vjpXWEaSqTa2xL9wjgQC', 1);
169 | INSERT INTO authorities (username, authority) VALUES ('user', 'ROLE_USER');
170 | ```
171 |
172 | #### Spring Security Configuration
173 |
174 | Add the following Spring configuration class.
175 |
176 | ```java
177 |
178 | import org.springframework.context.annotation.Bean;
179 | import org.springframework.security.authentication.AuthenticationManager;
180 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
181 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
182 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
183 | import org.springframework.security.core.userdetails.UserDetailsService;
184 | import org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl;
185 | import org.springframework.security.crypto.factory.PasswordEncoderFactories;
186 | import org.springframework.security.crypto.password.PasswordEncoder;
187 |
188 | import javax.sql.DataSource;
189 |
190 | @EnableWebSecurity
191 | public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
192 |
193 | private final DataSource dataSource;
194 |
195 | private PasswordEncoder passwordEncoder;
196 | private UserDetailsService userDetailsService;
197 |
198 | public WebSecurityConfiguration(final DataSource dataSource) {
199 | this.dataSource = dataSource;
200 | }
201 |
202 | @Override
203 | protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
204 | auth.userDetailsService(userDetailsService())
205 | .passwordEncoder(passwordEncoder());
206 | }
207 |
208 | @Bean
209 | @Override
210 | public AuthenticationManager authenticationManagerBean() throws Exception {
211 | return super.authenticationManagerBean();
212 | }
213 |
214 | @Bean
215 | public PasswordEncoder passwordEncoder() {
216 | if (passwordEncoder == null) {
217 | passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
218 | }
219 | return passwordEncoder;
220 | }
221 |
222 | @Bean
223 | public UserDetailsService userDetailsService() {
224 | if (userDetailsService == null) {
225 | userDetailsService = new JdbcDaoImpl();
226 | ((JdbcDaoImpl) userDetailsService).setDataSource(dataSource);
227 | }
228 | return userDetailsService;
229 | }
230 |
231 | }
232 | ```
233 |
234 | Quoting from [Spring Blog](https://spring.io/blog/2013/07/03/spring-security-java-config-preview-web-security#websecurityconfigureradapter):
235 |
236 | >The @EnableWebSecurity annotation and WebSecurityConfigurerAdapter work together to provide web based security.
237 |
238 | If you are using Spring Boot the `DataSource` object will be auto-configured and you can just inject it to the class instead of defining it yourself.
239 | it needs to be injected to the `UserDetailsService` in which will be using the provided `JdbcDaoImpl` provided by Spring Security, if necessary
240 | you can replace this with your own implementation.
241 |
242 | As the Spring Security's `AuthenticationManager` is required by some auto-configured Spring `@Bean`s it's necessary to
243 | override the `authenticationManagerBean` method and annotate is as a `@Bean`.
244 |
245 | The `PasswordEncoder` will be handled by `PasswordEncoderFactories.createDelegatingPasswordEncoder()` in which handles a
246 | few of password encoders and delegates based on a prefix, in our example we are prefixing the passwords with `{bcrypt}`.
247 |
248 | ### Authorization Server Configuration
249 |
250 | The authorization server validates the `client` and `user` credentials and provides the tokens.
251 |
252 | Add the following Spring configuration class.
253 |
254 | ```java
255 | import org.springframework.context.annotation.Bean;
256 | import org.springframework.context.annotation.Configuration;
257 | import org.springframework.security.authentication.AuthenticationManager;
258 | import org.springframework.security.crypto.password.PasswordEncoder;
259 | import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
260 | import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
261 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
262 | import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
263 | import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
264 | import org.springframework.security.oauth2.provider.ClientDetailsService;
265 | import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
266 | import org.springframework.security.oauth2.provider.token.TokenStore;
267 | import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
268 |
269 | import javax.sql.DataSource;
270 |
271 | @Configuration
272 | @EnableAuthorizationServer
273 | public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
274 |
275 | private final DataSource dataSource;
276 | private final PasswordEncoder passwordEncoder;
277 | private final AuthenticationManager authenticationManager;
278 |
279 | private TokenStore tokenStore;
280 |
281 | public AuthorizationServerConfiguration(final DataSource dataSource, final PasswordEncoder passwordEncoder,
282 | final AuthenticationManager authenticationManager) {
283 | this.dataSource = dataSource;
284 | this.passwordEncoder = passwordEncoder;
285 | this.authenticationManager = authenticationManager;
286 | }
287 |
288 | @Bean
289 | public TokenStore tokenStore() {
290 | if (tokenStore == null) {
291 | tokenStore = new JdbcTokenStore(dataSource);
292 | }
293 | return tokenStore;
294 | }
295 |
296 | @Bean
297 | public DefaultTokenServices tokenServices(final ClientDetailsService clientDetailsService) {
298 | DefaultTokenServices tokenServices = new DefaultTokenServices();
299 | tokenServices.setSupportRefreshToken(true);
300 | tokenServices.setTokenStore(tokenStore());
301 | tokenServices.setClientDetailsService(clientDetailsService);
302 | tokenServices.setAuthenticationManager(authenticationManager);
303 | return tokenServices;
304 | }
305 |
306 | @Override
307 | public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
308 | clients.jdbc(dataSource);
309 | }
310 |
311 | @Override
312 | public void configure(final AuthorizationServerEndpointsConfigurer endpoints) {
313 | endpoints.authenticationManager(authenticationManager)
314 | .tokenStore(tokenStore());
315 | }
316 |
317 | @Override
318 | public void configure(final AuthorizationServerSecurityConfigurer oauthServer) {
319 | oauthServer.passwordEncoder(passwordEncoder)
320 | .tokenKeyAccess("permitAll()")
321 | .checkTokenAccess("isAuthenticated()");
322 | }
323 |
324 | }
325 | ```
326 |
327 | ### User Info Endpoint
328 |
329 | Now we need to define an endpoint where the authorization token can be decoded into an `Authorization` object, to do so
330 | add the following class.
331 |
332 | ```java
333 | import org.springframework.http.ResponseEntity;
334 | import org.springframework.web.bind.annotation.GetMapping;
335 | import org.springframework.web.bind.annotation.RequestMapping;
336 | import org.springframework.web.bind.annotation.RestController;
337 |
338 | import java.security.Principal;
339 |
340 | @RestController
341 | @RequestMapping("/profile")
342 | public class UserController {
343 |
344 | @GetMapping("/me")
345 | public ResponseEntity get(final Principal principal) {
346 | return ResponseEntity.ok(principal);
347 | }
348 |
349 | }
350 | ```
351 |
352 | ### Resource Server Configuration
353 |
354 | [Follow this link](../resource-server-opaque).
355 |
356 | ### Testing all together
357 |
358 | To test all together we need to spin up the `Authorization Server` and the `Resource Server` as well, in my setup it will be
359 | running on port `9001` and `9101` accordingly.
360 |
361 | #### Generating the token
362 |
363 | ```bash
364 | $ curl -u clientId:secret -X POST localhost:9001/oauth/token\?grant_type=password\&username=user\&password=pass
365 |
366 | {
367 | "access_token" : "e47876b0-9962-41f1-ace3-e3381250ccea",
368 | "token_type" : "bearer",
369 | "refresh_token" : "8e17a71c-cb39-4904-8205-4d9f8c71aeef",
370 | "expires_in" : 299,
371 | "scope" : "read write"
372 | }
373 | ```
374 |
375 | #### Accessing the resource
376 |
377 | Now that you have generated the token copy the `access_token` and add it to the request on the `Authorization`
378 | HTTP Header, e.g:
379 |
380 | ```bash
381 | $ curl -i localhost:9101/me -H "Authorization: Bearer c06a4137-fa07-4d9a-97f9-85d1ba820d3a"
382 |
383 | {
384 | "authorities" : [ {
385 | "authority" : "ROLE_USER"
386 | } ],
387 | "details" : {
388 | "remoteAddress" : "127.0.0.1",
389 | "sessionId" : null,
390 | "tokenValue" : "c06a4137-fa07-4d9a-97f9-85d1ba820d3a",
391 | "tokenType" : "Bearer",
392 | "decodedDetails" : null
393 | },
394 | "authenticated" : true,
395 | "userAuthentication" : {
396 | "authorities" : [ {
397 | "authority" : "ROLE_USER"
398 | } ],
399 | "details" : {
400 | "authorities" : [ {
401 | "authority" : "ROLE_USER"
402 | } ],
403 | "details" : {
404 | "remoteAddress" : "127.0.0.1",
405 | "sessionId" : null,
406 | "tokenValue" : "c06a4137-fa07-4d9a-97f9-85d1ba820d3a",
407 | "tokenType" : "Bearer",
408 | "decodedDetails" : null
409 | },
410 | "authenticated" : true,
411 | "userAuthentication" : {
412 | "authorities" : [ {
413 | "authority" : "ROLE_USER"
414 | } ],
415 | "details" : {
416 | "grant_type" : "password",
417 | "username" : "user"
418 | },
419 | "authenticated" : true,
420 | "principal" : {
421 | "password" : null,
422 | "username" : "user",
423 | "authorities" : [ {
424 | "authority" : "ROLE_USER"
425 | } ],
426 | "accountNonExpired" : true,
427 | "accountNonLocked" : true,
428 | "credentialsNonExpired" : true,
429 | "enabled" : true
430 | },
431 | "credentials" : null,
432 | "name" : "user"
433 | },
434 | "clientOnly" : false,
435 | "oauth2Request" : {
436 | "clientId" : "clientId",
437 | "scope" : [ "read", "write" ],
438 | "requestParameters" : {
439 | "grant_type" : "password",
440 | "username" : "user"
441 | },
442 | "resourceIds" : [ ],
443 | "authorities" : [ {
444 | "authority" : "ROLE_CLIENT"
445 | } ],
446 | "approved" : true,
447 | "refresh" : false,
448 | "redirectUri" : null,
449 | "responseTypes" : [ ],
450 | "extensions" : { },
451 | "grantType" : "password",
452 | "refreshTokenRequest" : null
453 | },
454 | "credentials" : "",
455 | "principal" : {
456 | "password" : null,
457 | "username" : "user",
458 | "authorities" : [ {
459 | "authority" : "ROLE_USER"
460 | } ],
461 | "accountNonExpired" : true,
462 | "accountNonLocked" : true,
463 | "credentialsNonExpired" : true,
464 | "enabled" : true
465 | },
466 | "name" : "user"
467 | },
468 | "authenticated" : true,
469 | "principal" : "user",
470 | "credentials" : "N/A",
471 | "name" : "user"
472 | },
473 | "principal" : "user",
474 | "credentials" : "",
475 | "clientOnly" : false,
476 | "oauth2Request" : {
477 | "clientId" : null,
478 | "scope" : [ ],
479 | "requestParameters" : { },
480 | "resourceIds" : [ ],
481 | "authorities" : [ ],
482 | "approved" : true,
483 | "refresh" : false,
484 | "redirectUri" : null,
485 | "responseTypes" : [ ],
486 | "extensions" : { },
487 | "grantType" : null,
488 | "refreshTokenRequest" : null
489 | },
490 | "name" : "user"
491 | }
492 | ```
493 |
494 | # Footnote
495 | - The code used for this guide can be found on [GitHub](https://github.com/marcosbarbero/spring-boot-n-cloud-playground/tree/master/security).
496 | - [OAuth 2.0](https://www.oauth.com/)
497 | - [Spring Security Java Config Preview](https://spring.io/blog/2013/07/03/spring-security-java-config-preview-web-security)
498 | - [Spring Boot 2 - Migration Guide](https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Migration-Guide#authenticationmanager-bean)
499 | - [Spring - OAuth2 Developers Guide](https://projects.spring.io/spring-security-oauth/docs/oauth2.html)
--------------------------------------------------------------------------------
/oauth2-opaque-server/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | com.marcosbarbero.lab
8 | spring-boot2-oauth2-opaque
9 | 0.0.1-SNAPSHOT
10 | ..
11 |
12 |
13 | 4.0.0
14 |
15 | oauth2-opaque-server
16 |
17 |
20 |
21 |
22 |
23 |
24 | org.springframework.boot
25 | spring-boot-starter-jdbc
26 |
27 |
28 |
29 | com.h2database
30 | h2
31 | runtime
32 |
33 |
34 |
35 |
36 |
37 |
38 | org.springframework.boot
39 | spring-boot-maven-plugin
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/oauth2-opaque-server/src/main/java/com/marcosbarbero/lab/sec/oauth/opaque/OAuth2ServerOpaqueApplication.java:
--------------------------------------------------------------------------------
1 | package com.marcosbarbero.lab.sec.oauth.opaque;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class OAuth2ServerOpaqueApplication {
8 |
9 | public static void main(String... args) {
10 | SpringApplication.run(OAuth2ServerOpaqueApplication.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/oauth2-opaque-server/src/main/java/com/marcosbarbero/lab/sec/oauth/opaque/config/AuthorizationServerConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.marcosbarbero.lab.sec.oauth.opaque.config;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.context.annotation.Configuration;
5 | import org.springframework.security.authentication.AuthenticationManager;
6 | import org.springframework.security.crypto.password.PasswordEncoder;
7 | import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
8 | import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
9 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
10 | import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
11 | import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
12 | import org.springframework.security.oauth2.provider.ClientDetailsService;
13 | import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
14 | import org.springframework.security.oauth2.provider.token.TokenStore;
15 | import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
16 |
17 | import javax.sql.DataSource;
18 |
19 | @Configuration
20 | @EnableAuthorizationServer
21 | public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
22 |
23 | private final DataSource dataSource;
24 | private final PasswordEncoder passwordEncoder;
25 | private final AuthenticationManager authenticationManager;
26 |
27 | private TokenStore tokenStore;
28 |
29 | public AuthorizationServerConfiguration(final DataSource dataSource, final PasswordEncoder passwordEncoder,
30 | final AuthenticationManager authenticationManager) {
31 | this.dataSource = dataSource;
32 | this.passwordEncoder = passwordEncoder;
33 | this.authenticationManager = authenticationManager;
34 | }
35 |
36 | @Bean
37 | public TokenStore tokenStore() {
38 | if (tokenStore == null) {
39 | tokenStore = new JdbcTokenStore(dataSource);
40 | }
41 | return tokenStore;
42 | }
43 |
44 | @Bean
45 | public DefaultTokenServices tokenServices(final ClientDetailsService clientDetailsService) {
46 | DefaultTokenServices tokenServices = new DefaultTokenServices();
47 | tokenServices.setSupportRefreshToken(true);
48 | tokenServices.setTokenStore(tokenStore());
49 | tokenServices.setClientDetailsService(clientDetailsService);
50 | tokenServices.setAuthenticationManager(authenticationManager);
51 | return tokenServices;
52 | }
53 |
54 | @Override
55 | public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
56 | clients.jdbc(dataSource);
57 | }
58 |
59 | @Override
60 | public void configure(final AuthorizationServerEndpointsConfigurer endpoints) {
61 | endpoints.authenticationManager(authenticationManager)
62 | .tokenStore(tokenStore());
63 | }
64 |
65 | @Override
66 | public void configure(final AuthorizationServerSecurityConfigurer oauthServer) {
67 | oauthServer.passwordEncoder(passwordEncoder)
68 | .tokenKeyAccess("permitAll()")
69 | .checkTokenAccess("isAuthenticated()");
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/oauth2-opaque-server/src/main/java/com/marcosbarbero/lab/sec/oauth/opaque/config/ResourceServerConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.marcosbarbero.lab.sec.oauth.opaque.config;
2 |
3 | import org.springframework.context.annotation.Configuration;
4 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
5 | import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
6 | import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
7 | import org.springframework.security.oauth2.provider.token.TokenStore;
8 |
9 | @Configuration
10 | @EnableResourceServer
11 | public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
12 |
13 | private final TokenStore tokenStore;
14 |
15 | public ResourceServerConfiguration(final TokenStore tokenStore) {
16 | this.tokenStore = tokenStore;
17 | }
18 |
19 | @Override
20 | public void configure(final ResourceServerSecurityConfigurer resources) {
21 | resources.tokenStore(tokenStore);
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/oauth2-opaque-server/src/main/java/com/marcosbarbero/lab/sec/oauth/opaque/config/WebSecurityConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.marcosbarbero.lab.sec.oauth.opaque.config;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.security.authentication.AuthenticationManager;
5 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
6 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
7 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
8 | import org.springframework.security.core.userdetails.UserDetailsService;
9 | import org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl;
10 | import org.springframework.security.crypto.factory.PasswordEncoderFactories;
11 | import org.springframework.security.crypto.password.PasswordEncoder;
12 |
13 | import javax.sql.DataSource;
14 |
15 | @EnableWebSecurity
16 | public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
17 |
18 | private final DataSource dataSource;
19 |
20 | private PasswordEncoder passwordEncoder;
21 | private UserDetailsService userDetailsService;
22 |
23 | public WebSecurityConfiguration(final DataSource dataSource) {
24 | this.dataSource = dataSource;
25 | }
26 |
27 | @Override
28 | protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
29 | auth.userDetailsService(userDetailsService())
30 | .passwordEncoder(passwordEncoder());
31 | }
32 |
33 | @Bean
34 | @Override
35 | public AuthenticationManager authenticationManagerBean() throws Exception {
36 | return super.authenticationManagerBean();
37 | }
38 |
39 | @Bean
40 | public PasswordEncoder passwordEncoder() {
41 | if (passwordEncoder == null) {
42 | passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
43 | }
44 | return passwordEncoder;
45 | }
46 |
47 | @Bean
48 | @Override
49 | public UserDetailsService userDetailsService() {
50 | if (userDetailsService == null) {
51 | userDetailsService = new JdbcDaoImpl();
52 | ((JdbcDaoImpl) userDetailsService).setDataSource(dataSource);
53 | }
54 | return userDetailsService;
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/oauth2-opaque-server/src/main/java/com/marcosbarbero/lab/sec/oauth/opaque/web/ProfileController.java:
--------------------------------------------------------------------------------
1 | package com.marcosbarbero.lab.sec.oauth.opaque.web;
2 |
3 | import org.springframework.http.ResponseEntity;
4 | import org.springframework.web.bind.annotation.GetMapping;
5 | import org.springframework.web.bind.annotation.RequestMapping;
6 | import org.springframework.web.bind.annotation.RestController;
7 |
8 | import java.security.Principal;
9 |
10 | @RestController
11 | @RequestMapping("/profile")
12 | public class ProfileController {
13 |
14 | @GetMapping("/me")
15 | public ResponseEntity get(final Principal principal) {
16 | return ResponseEntity.ok(principal);
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/oauth2-opaque-server/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | server:
2 | port: 9001
3 |
4 | spring:
5 | jackson:
6 | serialization:
7 | INDENT_OUTPUT: true
--------------------------------------------------------------------------------
/oauth2-opaque-server/src/main/resources/data.sql:
--------------------------------------------------------------------------------
1 | -- The encrypted client_secret it `secret`
2 | INSERT INTO oauth_client_details (client_id, client_secret, scope, authorized_grant_types, authorities, access_token_validity)
3 | VALUES ('clientId', '{bcrypt}$2a$10$vCXMWCn7fDZWOcLnIEhmK.74dvK1Eh8ae2WrWlhr2ETPLoxQctN4.', 'read,write', 'password,refresh_token,client_credentials', 'ROLE_CLIENT', 300);
4 |
5 | -- The encrypted password is `pass`
6 | INSERT INTO users (id, username, password, enabled) VALUES (1, 'user', '{bcrypt}$2a$10$cyf5NfobcruKQ8XGjUJkEegr9ZWFqaea6vjpXWEaSqTa2xL9wjgQC', 1);
7 | INSERT INTO users (id, username, password, enabled) VALUES (2, 'guest', '{bcrypt}$2a$10$cyf5NfobcruKQ8XGjUJkEegr9ZWFqaea6vjpXWEaSqTa2xL9wjgQC', 1);
8 |
9 | INSERT INTO authorities (username, authority) VALUES ('user', 'ROLE_USER');
10 | INSERT INTO authorities (username, authority) VALUES ('guest', 'ROLE_GUEST');
11 |
--------------------------------------------------------------------------------
/oauth2-opaque-server/src/main/resources/schema.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE IF NOT EXISTS oauth_client_details (
2 | client_id VARCHAR(256) PRIMARY KEY,
3 | resource_ids VARCHAR(256),
4 | client_secret VARCHAR(256) NOT NULL,
5 | scope VARCHAR(256),
6 | authorized_grant_types VARCHAR(256),
7 | web_server_redirect_uri VARCHAR(256),
8 | authorities VARCHAR(256),
9 | access_token_validity INTEGER,
10 | refresh_token_validity INTEGER,
11 | additional_information VARCHAR(4000),
12 | autoapprove VARCHAR(256)
13 | );
14 |
15 | CREATE TABLE IF NOT EXISTS oauth_client_token (
16 | token_id VARCHAR(256),
17 | token BLOB,
18 | authentication_id VARCHAR(256) PRIMARY KEY,
19 | user_name VARCHAR(256),
20 | client_id VARCHAR(256)
21 | );
22 |
23 | CREATE TABLE IF NOT EXISTS oauth_access_token (
24 | token_id VARCHAR(256),
25 | token BLOB,
26 | authentication_id VARCHAR(256),
27 | user_name VARCHAR(256),
28 | client_id VARCHAR(256),
29 | authentication BLOB,
30 | refresh_token VARCHAR(256)
31 | );
32 |
33 | CREATE TABLE IF NOT EXISTS oauth_refresh_token (
34 | token_id VARCHAR(256),
35 | token BLOB,
36 | authentication BLOB
37 | );
38 |
39 | CREATE TABLE IF NOT EXISTS oauth_code (
40 | code VARCHAR(256), authentication BLOB
41 | );
42 |
43 | CREATE TABLE IF NOT EXISTS users (
44 | id INT AUTO_INCREMENT PRIMARY KEY,
45 | username VARCHAR(256) NOT NULL,
46 | password VARCHAR(256) NOT NULL,
47 | enabled TINYINT(1),
48 | UNIQUE KEY unique_username(username)
49 | );
50 |
51 | CREATE TABLE IF NOT EXISTS authorities (
52 | username VARCHAR(256) NOT NULL,
53 | authority VARCHAR(256) NOT NULL,
54 | PRIMARY KEY(username, authority)
55 | );
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 2.1.2.RELEASE
9 |
10 |
11 |
12 | com.marcosbarbero.lab
13 | spring-boot2-oauth2-opaque
14 | 0.0.1-SNAPSHOT
15 | pom
16 | spring-boot2-oauth2-opaque
17 |
18 |
21 |
22 |
23 |
24 |
25 | Apache License, Version 2.0
26 | http://www.apache.org/licenses/LICENSE-2.0.txt
27 | repo
28 |
29 |
30 |
31 |
32 |
33 | marcosbarbero
34 | marcos.hgb@gmail.com
35 | Marcos Barbero
36 | Europe/Amsterdam
37 |
38 | Project Lead
39 |
40 |
41 |
42 |
43 |
44 | scm:git:https://github.com/marcosbarbero/spring-boot2-oauth2-opaque.git
45 |
46 | scm:git:git@github.com:marcosbarbero/spring-boot2-oauth2-opaque.git
47 |
48 | https://github.com/marcosbarbero/spring-boot2-oauth2-opaque
49 | HEAD
50 |
51 |
52 |
53 | 1.8
54 |
55 |
56 |
57 | oauth2-opaque-server
58 | oauth2-opaque-resource
59 |
60 |
61 |
62 |
63 | org.springframework.boot
64 | spring-boot-starter-web
65 |
66 |
67 |
68 | org.springframework.boot
69 | spring-boot-starter-security
70 |
71 |
72 |
73 | org.springframework.security.oauth.boot
74 | spring-security-oauth2-autoconfigure
75 | 2.1.2.RELEASE
76 |
77 |
78 |
79 | org.springframework.boot
80 | spring-boot-configuration-processor
81 | true
82 |
83 |
84 |
85 | org.springframework.boot
86 | spring-boot-starter-test
87 | test
88 |
89 |
90 |
91 |
92 |
--------------------------------------------------------------------------------