48 changed files with 420 additions and 484 deletions
@ -0,0 +1,143 @@
@@ -0,0 +1,143 @@
|
||||
/* |
||||
* Copyright 2019-2020 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.gradle.docs; |
||||
|
||||
import java.net.URI; |
||||
import java.util.Collections; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
import org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask; |
||||
import org.asciidoctor.gradle.jvm.AsciidoctorJExtension; |
||||
import org.asciidoctor.gradle.jvm.AsciidoctorJPlugin; |
||||
import org.asciidoctor.gradle.jvm.AsciidoctorTask; |
||||
import org.asciidoctor.gradle.jvm.pdf.AsciidoctorJPdfPlugin; |
||||
import org.gradle.api.Plugin; |
||||
import org.gradle.api.Project; |
||||
|
||||
/** |
||||
* Conventions that are applied in the presence of the {@link AsciidoctorJPlugin}. When |
||||
* the plugin is applied: |
||||
* |
||||
* <ul> |
||||
* <li>All warnings are made fatal. |
||||
* <li>A task is created to resolve and unzip our documentation resources (CSS and |
||||
* Javascript). |
||||
* <li>For each {@link AsciidoctorTask} (HTML only): |
||||
* <ul> |
||||
* <li>A configuration named asciidoctorExtensions is used to add the |
||||
* <a href="https://github.com/spring-io/spring-asciidoctor-extensions#block-switch">block |
||||
* switch</a> extension |
||||
* <li>{@code doctype} {@link AsciidoctorTask#options(Map) option} is configured. |
||||
* <li>{@link AsciidoctorTask#attributes(Map) Attributes} are configured for syntax |
||||
* highlighting, CSS styling, docinfo, etc. |
||||
* </ul> |
||||
* <li>For each {@link AbstractAsciidoctorTask} (HTML and PDF): |
||||
* <ul> |
||||
* <li>{@link AsciidoctorTask#attributes(Map) Attributes} are configured to enable |
||||
* warnings for references to missing attributes, the year is added as @{code today-year}, |
||||
* etc |
||||
* <li>{@link AbstractAsciidoctorTask#baseDirFollowsSourceDir() baseDirFollowsSourceDir()} |
||||
* is enabled. |
||||
* </ul> |
||||
* </ul> |
||||
* |
||||
* @author Andy Wilkinson |
||||
* @author Rob Winch |
||||
* @author Steve Riesenberg |
||||
*/ |
||||
public class SpringAsciidoctorPlugin implements Plugin<Project> { |
||||
private static final String ASCIIDOCTORJ_VERSION = "2.4.3"; |
||||
private static final String EXTENSIONS_CONFIGURATION_NAME = "asciidoctorExtensions"; |
||||
|
||||
@Override |
||||
public void apply(Project project) { |
||||
// Apply asciidoctor plugin
|
||||
project.getPluginManager().apply(AsciidoctorJPlugin.class); |
||||
project.getPluginManager().apply(AsciidoctorJPdfPlugin.class); |
||||
|
||||
// Configure asciidoctor
|
||||
project.getPlugins().withType(AsciidoctorJPlugin.class, (asciidoctorPlugin) -> { |
||||
configureDocumentationDependenciesRepository(project); |
||||
makeAllWarningsFatal(project); |
||||
upgradeAsciidoctorJVersion(project); |
||||
createAsciidoctorExtensionsConfiguration(project); |
||||
project.getTasks().withType(AbstractAsciidoctorTask.class, this::configureAsciidoctorExtension); |
||||
}); |
||||
} |
||||
|
||||
private void configureDocumentationDependenciesRepository(Project project) { |
||||
project.getRepositories().maven((mavenRepo) -> { |
||||
mavenRepo.setUrl(URI.create("https://repo.spring.io/release")); |
||||
mavenRepo.mavenContent((mavenContent) -> { |
||||
mavenContent.includeGroup("io.spring.asciidoctor"); |
||||
mavenContent.includeGroup("io.spring.asciidoctor.backends"); |
||||
mavenContent.includeGroup("io.spring.docresources"); |
||||
}); |
||||
}); |
||||
} |
||||
|
||||
private void makeAllWarningsFatal(Project project) { |
||||
project.getExtensions().getByType(AsciidoctorJExtension.class).fatalWarnings(".*"); |
||||
} |
||||
|
||||
private void upgradeAsciidoctorJVersion(Project project) { |
||||
project.getExtensions().getByType(AsciidoctorJExtension.class).setVersion(ASCIIDOCTORJ_VERSION); |
||||
} |
||||
|
||||
private void createAsciidoctorExtensionsConfiguration(Project project) { |
||||
project.getConfigurations().create(EXTENSIONS_CONFIGURATION_NAME, (configuration) -> { |
||||
project.getConfigurations().matching((candidate) -> "management".equals(candidate.getName())) |
||||
.all(configuration::extendsFrom); |
||||
configuration.getDependencies().add(project.getDependencies() |
||||
.create("io.spring.asciidoctor.backends:spring-asciidoctor-backends:0.0.3")); |
||||
configuration.getDependencies() |
||||
.add(project.getDependencies().create("org.asciidoctor:asciidoctorj-pdf:1.5.3")); |
||||
}); |
||||
} |
||||
|
||||
private void configureAsciidoctorExtension(AbstractAsciidoctorTask asciidoctorTask) { |
||||
asciidoctorTask.configurations(EXTENSIONS_CONFIGURATION_NAME); |
||||
configureCommonAttributes(asciidoctorTask); |
||||
configureOptions(asciidoctorTask); |
||||
asciidoctorTask.baseDirFollowsSourceDir(); |
||||
asciidoctorTask.resources((resourcesSpec) -> { |
||||
resourcesSpec.from(asciidoctorTask.getSourceDir(), (resourcesSrcDirSpec) -> { |
||||
// Not using intermediateWorkDir.
|
||||
// See https://github.com/asciidoctor/asciidoctor-gradle-plugin/issues/523
|
||||
resourcesSrcDirSpec.include("images/*.png", "css/**", "js/**"); |
||||
}); |
||||
}); |
||||
if (asciidoctorTask instanceof AsciidoctorTask) { |
||||
boolean pdf = asciidoctorTask.getName().toLowerCase().contains("pdf"); |
||||
String backend = (!pdf) ? "spring-html" : "spring-pdf"; |
||||
((AsciidoctorTask) asciidoctorTask).outputOptions((outputOptions) -> |
||||
outputOptions.backends(backend)); |
||||
} |
||||
} |
||||
|
||||
private void configureCommonAttributes(AbstractAsciidoctorTask asciidoctorTask) { |
||||
Map<String, Object> attributes = new HashMap<>(); |
||||
attributes.put("attribute-missing", "warn"); |
||||
attributes.put("revnumber", null); |
||||
asciidoctorTask.attributes(attributes); |
||||
} |
||||
|
||||
private void configureOptions(AbstractAsciidoctorTask asciidoctorTask) { |
||||
asciidoctorTask.options(Collections.singletonMap("doctype", "book")); |
||||
} |
||||
} |
||||
@ -1,2 +0,0 @@
@@ -1,2 +0,0 @@
|
||||
package-lock.json |
||||
node_modules |
||||
@ -1,11 +0,0 @@
@@ -1,11 +0,0 @@
|
||||
name: reference |
||||
version: 0.2.2 |
||||
prerelease: '-SNAPSHOT' |
||||
|
||||
title: Spring Authorization Server |
||||
display_version: latest |
||||
start_page: ROOT:index.adoc |
||||
nav: |
||||
- modules/ROOT/nav.adoc |
||||
- modules/guides/nav.adoc |
||||
- modules/appendix/nav.adoc |
||||
@ -1,16 +0,0 @@
@@ -1,16 +0,0 @@
|
||||
site: |
||||
title: Spring Authorization Server |
||||
url: https://docs.spring.io/spring-authorization-server |
||||
start_page: reference::index.adoc |
||||
asciidoc: |
||||
attributes: |
||||
page-pagination: true |
||||
content: |
||||
sources: |
||||
- url: ../ |
||||
branches: [main] |
||||
start_path: docs |
||||
ui: |
||||
bundle: |
||||
url: https://github.com/spring-io/antora-ui-spring/releases/download/latest/ui-bundle.zip |
||||
snapshot: true |
||||
@ -1,7 +0,0 @@
@@ -1,7 +0,0 @@
|
||||
.xref:index.adoc[Reference Manual] |
||||
* xref:overview.adoc[Overview] |
||||
* xref:getting-help.adoc[Getting Help] |
||||
* xref:getting-started.adoc[Getting Started] |
||||
* xref:configuration-model.adoc[Configuration Model] |
||||
* xref:core-components.adoc[Core Components] |
||||
* xref:protocol-endpoints.adoc[Protocol Endpoints] |
||||
@ -1,3 +0,0 @@
@@ -1,3 +0,0 @@
|
||||
= Reference Manual |
||||
|
||||
This page is under construction. |
||||
@ -1 +0,0 @@
@@ -1 +0,0 @@
|
||||
* xref:index.adoc[Appendices] |
||||
Binary file not shown.
@ -1,5 +0,0 @@
@@ -1,5 +0,0 @@
|
||||
distributionBase=GRADLE_USER_HOME |
||||
distributionPath=wrapper/dists |
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip |
||||
zipStoreBase=GRADLE_USER_HOME |
||||
zipStorePath=wrapper/dists |
||||
@ -1,185 +0,0 @@
@@ -1,185 +0,0 @@
|
||||
#!/usr/bin/env sh |
||||
|
||||
# |
||||
# Copyright 2015 the original author or authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# https://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
# |
||||
|
||||
############################################################################## |
||||
## |
||||
## Gradle start up script for UN*X |
||||
## |
||||
############################################################################## |
||||
|
||||
# Attempt to set APP_HOME |
||||
# Resolve links: $0 may be a link |
||||
PRG="$0" |
||||
# Need this for relative symlinks. |
||||
while [ -h "$PRG" ] ; do |
||||
ls=`ls -ld "$PRG"` |
||||
link=`expr "$ls" : '.*-> \(.*\)$'` |
||||
if expr "$link" : '/.*' > /dev/null; then |
||||
PRG="$link" |
||||
else |
||||
PRG=`dirname "$PRG"`"/$link" |
||||
fi |
||||
done |
||||
SAVED="`pwd`" |
||||
cd "`dirname \"$PRG\"`/" >/dev/null |
||||
APP_HOME="`pwd -P`" |
||||
cd "$SAVED" >/dev/null |
||||
|
||||
APP_NAME="Gradle" |
||||
APP_BASE_NAME=`basename "$0"` |
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. |
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' |
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value. |
||||
MAX_FD="maximum" |
||||
|
||||
warn () { |
||||
echo "$*" |
||||
} |
||||
|
||||
die () { |
||||
echo |
||||
echo "$*" |
||||
echo |
||||
exit 1 |
||||
} |
||||
|
||||
# OS specific support (must be 'true' or 'false'). |
||||
cygwin=false |
||||
msys=false |
||||
darwin=false |
||||
nonstop=false |
||||
case "`uname`" in |
||||
CYGWIN* ) |
||||
cygwin=true |
||||
;; |
||||
Darwin* ) |
||||
darwin=true |
||||
;; |
||||
MSYS* | MINGW* ) |
||||
msys=true |
||||
;; |
||||
NONSTOP* ) |
||||
nonstop=true |
||||
;; |
||||
esac |
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar |
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM. |
||||
if [ -n "$JAVA_HOME" ] ; then |
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then |
||||
# IBM's JDK on AIX uses strange locations for the executables |
||||
JAVACMD="$JAVA_HOME/jre/sh/java" |
||||
else |
||||
JAVACMD="$JAVA_HOME/bin/java" |
||||
fi |
||||
if [ ! -x "$JAVACMD" ] ; then |
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME |
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the |
||||
location of your Java installation." |
||||
fi |
||||
else |
||||
JAVACMD="java" |
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. |
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the |
||||
location of your Java installation." |
||||
fi |
||||
|
||||
# Increase the maximum file descriptors if we can. |
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then |
||||
MAX_FD_LIMIT=`ulimit -H -n` |
||||
if [ $? -eq 0 ] ; then |
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then |
||||
MAX_FD="$MAX_FD_LIMIT" |
||||
fi |
||||
ulimit -n $MAX_FD |
||||
if [ $? -ne 0 ] ; then |
||||
warn "Could not set maximum file descriptor limit: $MAX_FD" |
||||
fi |
||||
else |
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" |
||||
fi |
||||
fi |
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock |
||||
if $darwin; then |
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" |
||||
fi |
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java |
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then |
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"` |
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` |
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"` |
||||
|
||||
# We build the pattern for arguments to be converted via cygpath |
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` |
||||
SEP="" |
||||
for dir in $ROOTDIRSRAW ; do |
||||
ROOTDIRS="$ROOTDIRS$SEP$dir" |
||||
SEP="|" |
||||
done |
||||
OURCYGPATTERN="(^($ROOTDIRS))" |
||||
# Add a user-defined pattern to the cygpath arguments |
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then |
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" |
||||
fi |
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh |
||||
i=0 |
||||
for arg in "$@" ; do |
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` |
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option |
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition |
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` |
||||
else |
||||
eval `echo args$i`="\"$arg\"" |
||||
fi |
||||
i=`expr $i + 1` |
||||
done |
||||
case $i in |
||||
0) set -- ;; |
||||
1) set -- "$args0" ;; |
||||
2) set -- "$args0" "$args1" ;; |
||||
3) set -- "$args0" "$args1" "$args2" ;; |
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;; |
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; |
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; |
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; |
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; |
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; |
||||
esac |
||||
fi |
||||
|
||||
# Escape application args |
||||
save () { |
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done |
||||
echo " " |
||||
} |
||||
APP_ARGS=`save "$@"` |
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules |
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" |
||||
|
||||
exec "$JAVACMD" "$@" |
||||
@ -1,89 +0,0 @@
@@ -1,89 +0,0 @@
|
||||
@rem |
||||
@rem Copyright 2015 the original author or authors. |
||||
@rem |
||||
@rem Licensed under the Apache License, Version 2.0 (the "License"); |
||||
@rem you may not use this file except in compliance with the License. |
||||
@rem You may obtain a copy of the License at |
||||
@rem |
||||
@rem https://www.apache.org/licenses/LICENSE-2.0 |
||||
@rem |
||||
@rem Unless required by applicable law or agreed to in writing, software |
||||
@rem distributed under the License is distributed on an "AS IS" BASIS, |
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
@rem See the License for the specific language governing permissions and |
||||
@rem limitations under the License. |
||||
@rem |
||||
|
||||
@if "%DEBUG%" == "" @echo off |
||||
@rem ########################################################################## |
||||
@rem |
||||
@rem Gradle startup script for Windows |
||||
@rem |
||||
@rem ########################################################################## |
||||
|
||||
@rem Set local scope for the variables with windows NT shell |
||||
if "%OS%"=="Windows_NT" setlocal |
||||
|
||||
set DIRNAME=%~dp0 |
||||
if "%DIRNAME%" == "" set DIRNAME=. |
||||
set APP_BASE_NAME=%~n0 |
||||
set APP_HOME=%DIRNAME% |
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter. |
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi |
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. |
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" |
||||
|
||||
@rem Find java.exe |
||||
if defined JAVA_HOME goto findJavaFromJavaHome |
||||
|
||||
set JAVA_EXE=java.exe |
||||
%JAVA_EXE% -version >NUL 2>&1 |
||||
if "%ERRORLEVEL%" == "0" goto execute |
||||
|
||||
echo. |
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. |
||||
echo. |
||||
echo Please set the JAVA_HOME variable in your environment to match the |
||||
echo location of your Java installation. |
||||
|
||||
goto fail |
||||
|
||||
:findJavaFromJavaHome |
||||
set JAVA_HOME=%JAVA_HOME:"=% |
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe |
||||
|
||||
if exist "%JAVA_EXE%" goto execute |
||||
|
||||
echo. |
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% |
||||
echo. |
||||
echo Please set the JAVA_HOME variable in your environment to match the |
||||
echo location of your Java installation. |
||||
|
||||
goto fail |
||||
|
||||
:execute |
||||
@rem Setup the command line |
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar |
||||
|
||||
|
||||
@rem Execute Gradle |
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* |
||||
|
||||
:end |
||||
@rem End local scope for the variables with windows NT shell |
||||
if "%ERRORLEVEL%"=="0" goto mainEnd |
||||
|
||||
:fail |
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of |
||||
rem the _cmd.exe /c_ return code! |
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 |
||||
exit /b 1 |
||||
|
||||
:mainEnd |
||||
if "%OS%"=="Windows_NT" endlocal |
||||
|
||||
:omega |
||||
@ -1,2 +0,0 @@
@@ -1,2 +0,0 @@
|
||||
.xref:index.adoc["How-to" Guides] |
||||
* xref:how-to-jpa.adoc[Implement core services with JPA] |
||||
@ -1,144 +0,0 @@
@@ -1,144 +0,0 @@
|
||||
= How-to: Implement core services with JPA |
||||
|
||||
[[getting-started]] |
||||
== Getting Started |
||||
|
||||
In this guide, we will demonstrate how to implement the xref:ROOT:core-components.adoc[core services] of xref:ROOT:index.adoc[Spring Authorization Server] with JPA. The purpose of this guide is to provide a starting point for implementing these services yourself, with the intention that you will make modifications as necessary to suit your needs. |
||||
|
||||
[[define-data-model]] |
||||
== Define the data model |
||||
|
||||
This guide seeks to provide a starting point for the data model and uses the simplest possible structure and data types. To come up with the initial schema, we begin by reviewing the xref:ROOT:core-components.adoc[domain objects] used by the core services. Please note: |
||||
|
||||
NOTE: Except for token, state, metadata, settings and claims values, we will use the JPA default column length of 255 for all columns. In reality, the length and even type of columns you use may need to be customized. Your mileage may vary and you are encouraged to experiment and test before deploying to production. |
||||
|
||||
The xref:ROOT:core-components.adoc#registered-client-repository[`RegisteredClient`] domain object contains a few multi-valued fields and some settings fields that require storing arbitrary key/value data. The following is an example that we will use to create a JPA entity. |
||||
|
||||
[[client-schema]] |
||||
.Client Schema |
||||
[source,sql] |
||||
---- |
||||
include::example$src/main/resources/oauth2-registered-client-schema.sql[] |
||||
---- |
||||
|
||||
The xref:ROOT:core-components.adoc#oauth2-authorization-service[`OAuth2Authorization`] domain object is more complex, and contains several multi-valued fields as well as numerous arbitrarily long token values, metadata, settings and claims values. The built-in JDBC implementation utilizes a flattened structure that prefers performance over normalization, which we adopt here as well. |
||||
|
||||
CAUTION: It has been difficult to find a flattened database schema that works well in all cases and with all database vendors. You may need to normalize or heavily alter the following schema for your needs. |
||||
|
||||
The following is an example that we will use to create a JPA entity. |
||||
|
||||
[[authorization-schema]] |
||||
.Authorization Schema |
||||
[source,sql] |
||||
---- |
||||
include::example$src/main/resources/oauth2-authorization-schema.sql[] |
||||
---- |
||||
|
||||
The xref:ROOT:core-components.adoc#oauth2-authorization-consent-service[`OAuth2AuthorizationConsent`] domain object is the simplest to model, and only contains a single multi-valued field in addition to a composite key. The following is an example that we will use to create a JPA entity. |
||||
|
||||
[[authorization-consent-schema]] |
||||
.Authorization Consent Schema |
||||
[source,sql] |
||||
---- |
||||
include::example$src/main/resources/oauth2-authorization-consent-schema.sql[] |
||||
---- |
||||
|
||||
[[create-jpa-entities]] |
||||
== Create JPA entities |
||||
|
||||
The preceding schema examples provide a reference for the structure of the entities we need to create. |
||||
|
||||
NOTE: The following entities are minimally annotated and are just examples. They allow the schema to be created dynamically and therefore do not require the above sql scripts to be executed manually. |
||||
|
||||
The following is an example of the `Client` entity which is used to persist information mapped from the xref:ROOT:core-components.adoc#registered-client-repository[`RegisteredClient`] domain object. |
||||
|
||||
[[client-entity]] |
||||
.Client Entity |
||||
[source,java] |
||||
---- |
||||
include::example$src/main/java/sample/jpa/Client.java[tag=class] |
||||
---- |
||||
|
||||
The following is an example of the `Authorization` entity which is used to persist information mapped from the xref:ROOT:core-components.adoc#oauth2-authorization-service[`OAuth2Authorization`] domain object. |
||||
|
||||
[[authorization-entity]] |
||||
.Authorization Entity |
||||
[source,java] |
||||
---- |
||||
include::example$src/main/java/sample/jpa/Authorization.java[tag=class] |
||||
---- |
||||
|
||||
The following is an example of the `AuthorizationConsent` entity which is used to persist information mapped from the xref:ROOT:core-components.adoc#oauth2-authorization-consent-service[`OAuth2AuthorizationConsent`] domain object. |
||||
|
||||
[[authorization-consent-entity]] |
||||
.Authorization Consent Entity |
||||
[source,java] |
||||
---- |
||||
include::example$src/main/java/sample/jpa/AuthorizationConsent.java[tag=class] |
||||
---- |
||||
|
||||
[[create-spring-data-repositories]] |
||||
== Create Spring Data repositories |
||||
|
||||
By closely examining the interfaces of each core service and reviewing the `Jdbc` implementations, we can derive a minimal set of queries needed for supporting a JPA version of each interface. |
||||
|
||||
The following is an example of the `ClientRepository` capable of finding a <<client-entity,`Client`>> by the `id` and `clientId` fields. |
||||
|
||||
[[client-repository]] |
||||
.Client Repository |
||||
[source,java] |
||||
---- |
||||
include::example$src/main/java/sample/jpa/ClientRepository.java[tag=class] |
||||
---- |
||||
|
||||
The following is an example of the `AuthorizationRepository` capable of finding an <<authorization-entity,`Authorization`>> by the `id` field as well as the `state`, `authorizationCodeValue`, `accessTokenValue` and `refreshTokenValue` token fields. It also allows querying a combination of token fields. |
||||
|
||||
[[authorization-repository]] |
||||
.Authorization Repository |
||||
[source,java] |
||||
---- |
||||
include::example$src/main/java/sample/jpa/AuthorizationRepository.java[tag=class] |
||||
---- |
||||
|
||||
The following is an example of the `AuthorizationConsentRepository` capable of finding and deleting an <<authorization-consent-entity,`AuthorizationConsent`>> by the `registeredClientId` and `principalName` fields, which form a composite primary key. |
||||
|
||||
[[authorization-consent-repository]] |
||||
.Authorization Consent Repository |
||||
[source,java] |
||||
---- |
||||
include::example$src/main/java/sample/jpa/AuthorizationConsentRepository.java[tag=class] |
||||
---- |
||||
|
||||
[[implement-core-services]] |
||||
== Implement core services |
||||
|
||||
With the above <<create-jpa-entities,entities>> and <<create-spring-data-repositories,repositories>>, we can begin implementing the core services. By reviewing the `Jdbc` implementations, we can derive a minimal set of internal utilities for converting to/from string values for enumerations and reading/writing JSON data for attributes, settings, metadata and claims fields. |
||||
|
||||
CAUTION: Keep in mind that writing JSON data to text columns with a fixed length has proven problematic with the `Jdbc` implementations. While these examples continue to do so, you may need to split these fields out into a separate table or data store that supports arbitrarily long data values. |
||||
|
||||
The following is an example of the `JpaRegisteredClientRepository` which uses a <<client-repository,`ClientRepository`>> for persisting a <<client-entity,`Client`>>, and maps to/from the xref:ROOT:core-components.adoc#registered-client-repository[`RegisteredClient`] domain object. |
||||
|
||||
[[jpa-registered-client-repository]] |
||||
.`RegisteredClientRepository` Implementation |
||||
[source,java] |
||||
---- |
||||
include::example$src/main/java/sample/jpa/JpaRegisteredClientRepository.java[tag=class] |
||||
---- |
||||
|
||||
The following is an example of the `JpaOAuth2AuthorizationService` which uses an <<authorization-repository,`AuthorizationRepository`>> for persisting an <<authorization-entity,`Authorization`>>, and maps to/from the xref:ROOT:core-components.adoc#oauth2-authorization-service[`OAuth2Authorization`] domain object. |
||||
|
||||
[[jpa-oauth2-authorization-service]] |
||||
.`OAuth2AuthorizationService` Implementation |
||||
[source,java] |
||||
---- |
||||
include::example$src/main/java/sample/jpa/JpaOAuth2AuthorizationService.java[tag=class] |
||||
---- |
||||
|
||||
The following is an example of the `JpaOAuth2AuthorizationConsentService` which uses an <<authorization-consent-repository,`AuthorizationConsentRepository`>> for persisting an <<authorization-consent-entity,`AuthorizationConsent`>>, and maps to/from the xref:ROOT:core-components.adoc#oauth2-authorization-consent-service[`OAuth2AuthorizationConsent`] domain object. |
||||
|
||||
[[jpa-oauth2-authorization-consent-service]] |
||||
.`OAuth2AuthorizationConsentService` Implementation |
||||
[source,java] |
||||
---- |
||||
include::example$src/main/java/sample/jpa/JpaOAuth2AuthorizationConsentService.java[tag=class] |
||||
---- |
||||
@ -1,6 +0,0 @@
@@ -1,6 +0,0 @@
|
||||
{ |
||||
"dependencies": { |
||||
"@antora/cli": "^3.0.0-alpha.8", |
||||
"@antora/site-generator-default": "^3.0.0-alpha.8" |
||||
} |
||||
} |
||||
@ -1,3 +1,23 @@
@@ -1,3 +1,23 @@
|
||||
plugins { |
||||
id "io.spring.convention.docs" |
||||
} |
||||
|
||||
asciidoctor { |
||||
attributes([ |
||||
"spring-authorization-server-version": project.version, |
||||
"examples-dir": "examples", |
||||
"docs-java": "$sourceDir/examples/src/main/java", |
||||
"chomp": "default headers packages", |
||||
"toc": "left", |
||||
"toclevels": "4" |
||||
]) |
||||
} |
||||
|
||||
docsZip { |
||||
from (api) { |
||||
into "api" |
||||
} |
||||
from(asciidoctor) { |
||||
into "reference/html" |
||||
} |
||||
} |
||||
|
||||
@ -1,3 +1,6 @@
@@ -1,3 +1,6 @@
|
||||
[[appendices]] |
||||
= Appendices |
||||
:toc: left |
||||
:toclevels: 1 |
||||
|
||||
This page is under construction. |
||||
@ -1,4 +1,7 @@
@@ -1,4 +1,7 @@
|
||||
[[configuration-model]] |
||||
= Configuration Model |
||||
:toc: left |
||||
:toclevels: 1 |
||||
|
||||
This page is under construction. |
||||
|
||||
@ -1,4 +1,7 @@
@@ -1,4 +1,7 @@
|
||||
[[core-components]] |
||||
= Core Components |
||||
:toc: left |
||||
:toclevels: 1 |
||||
|
||||
This page is under construction. |
||||
|
||||
@ -1,3 +1,6 @@
@@ -1,3 +1,6 @@
|
||||
[[getting-help]] |
||||
= Getting Help |
||||
:toc: left |
||||
:toclevels: 1 |
||||
|
||||
This page is under construction. |
||||
@ -1,4 +1,7 @@
@@ -1,4 +1,7 @@
|
||||
[[getting-started]] |
||||
= Getting Started |
||||
:toc: left |
||||
:toclevels: 1 |
||||
|
||||
This page is under construction. |
||||
|
||||
@ -0,0 +1,174 @@
@@ -0,0 +1,174 @@
|
||||
[[how-to-jpa]] |
||||
= How-to: Implement core services with JPA |
||||
:toc: left |
||||
:toclevels: 3 |
||||
:index-link: ../how-to.html |
||||
:docs-dir: .. |
||||
:examples-dir: ../examples |
||||
|
||||
[[jpa-getting-started]] |
||||
== Getting Started |
||||
|
||||
In this guide, we will demonstrate how to implement the xref:{docs-dir}/core-components.adoc#core-components[core services] of xref:{docs-dir}/index.adoc#top[Spring Authorization Server] with JPA. The purpose of this guide is to provide a starting point for implementing these services yourself, with the intention that you will make modifications as necessary to suit your needs. |
||||
|
||||
[[jpa-define-data-model]] |
||||
== Define the data model |
||||
|
||||
This guide seeks to provide a starting point for the data model and uses the simplest possible structure and data types. To come up with the initial schema, we begin by reviewing the xref:{docs-dir}/core-components.adoc#core-components[domain objects] used by the core services. Please note: |
||||
|
||||
NOTE: Except for token, state, metadata, settings and claims values, we will use the JPA default column length of 255 for all columns. In reality, the length and even type of columns you use may need to be customized. Your mileage may vary and you are encouraged to experiment and test before deploying to production. |
||||
|
||||
[[jpa-client-schema]] |
||||
=== Client Schema |
||||
|
||||
The xref:{docs-dir}/core-components.adoc#registered-client-repository[`RegisteredClient`] domain object contains a few multi-valued fields and some settings fields that require storing arbitrary key/value data. The following is an example that we will use to create a JPA entity. |
||||
|
||||
.Client Schema |
||||
[source,sql] |
||||
---- |
||||
include::{examples-dir}/src/main/resources/oauth2-registered-client-schema.sql[] |
||||
---- |
||||
|
||||
[[jpa-authorization-schema]] |
||||
=== Authorization Schema |
||||
|
||||
The xref:{docs-dir}/core-components.adoc#oauth2-authorization-service[`OAuth2Authorization`] domain object is more complex, and contains several multi-valued fields as well as numerous arbitrarily long token values, metadata, settings and claims values. The built-in JDBC implementation utilizes a flattened structure that prefers performance over normalization, which we adopt here as well. |
||||
|
||||
CAUTION: It has been difficult to find a flattened database schema that works well in all cases and with all database vendors. You may need to normalize or heavily alter the following schema for your needs. |
||||
|
||||
The following is an example that we will use to create a JPA entity. |
||||
|
||||
.Authorization Schema |
||||
[source,sql] |
||||
---- |
||||
include::{examples-dir}/src/main/resources/oauth2-authorization-schema.sql[] |
||||
---- |
||||
|
||||
[[jpa-authorization-consent-schema]] |
||||
=== Authorization Consent Schema |
||||
|
||||
The xref:{docs-dir}/core-components.adoc#oauth2-authorization-consent-service[`OAuth2AuthorizationConsent`] domain object is the simplest to model, and only contains a single multi-valued field in addition to a composite key. The following is an example that we will use to create a JPA entity. |
||||
|
||||
.Authorization Consent Schema |
||||
[source,sql] |
||||
---- |
||||
include::{examples-dir}/src/main/resources/oauth2-authorization-consent-schema.sql[] |
||||
---- |
||||
|
||||
[[jpa-create-jpa-entities]] |
||||
== Create JPA entities |
||||
|
||||
The preceding schema examples provide a reference for the structure of the entities we need to create. |
||||
|
||||
NOTE: The following entities are minimally annotated and are just examples. They allow the schema to be created dynamically and therefore do not require the above sql scripts to be executed manually. |
||||
|
||||
[[jpa-client-entity]] |
||||
=== Client Entity |
||||
|
||||
The following is an example of the `Client` entity which is used to persist information mapped from the xref:{docs-dir}/core-components.adoc#registered-client-repository[`RegisteredClient`] domain object. |
||||
|
||||
.Client Entity |
||||
[source,java] |
||||
---- |
||||
include::{examples-dir}/src/main/java/sample/jpa/Client.java[tag=class] |
||||
---- |
||||
|
||||
[[jpa-authorization-entity]] |
||||
=== Authorization Entity |
||||
|
||||
The following is an example of the `Authorization` entity which is used to persist information mapped from the xref:{docs-dir}/core-components.adoc#oauth2-authorization-service[`OAuth2Authorization`] domain object. |
||||
|
||||
.Authorization Entity |
||||
[source,java] |
||||
---- |
||||
include::{examples-dir}/src/main/java/sample/jpa/Authorization.java[tag=class] |
||||
---- |
||||
|
||||
[[jpa-authorization-consent-entity]] |
||||
=== Authorization Consent Entity |
||||
|
||||
The following is an example of the `AuthorizationConsent` entity which is used to persist information mapped from the xref:{docs-dir}/core-components.adoc#oauth2-authorization-consent-service[`OAuth2AuthorizationConsent`] domain object. |
||||
|
||||
.Authorization Consent Entity |
||||
[source,java] |
||||
---- |
||||
include::{examples-dir}/src/main/java/sample/jpa/AuthorizationConsent.java[tag=class] |
||||
---- |
||||
|
||||
[[jpa-create-spring-data-repositories]] |
||||
== Create Spring Data repositories |
||||
|
||||
By closely examining the interfaces of each core service and reviewing the `Jdbc` implementations, we can derive a minimal set of queries needed for supporting a JPA version of each interface. |
||||
|
||||
[[jpa-client-repository]] |
||||
=== Client Repository |
||||
|
||||
The following is an example of the `ClientRepository` capable of finding a <<jpa-client-entity,`Client`>> by the `id` and `clientId` fields. |
||||
|
||||
.Client Repository |
||||
[source,java] |
||||
---- |
||||
include::{examples-dir}/src/main/java/sample/jpa/ClientRepository.java[tag=class] |
||||
---- |
||||
|
||||
[[jpa-authorization-repository]] |
||||
=== Authorization Repository |
||||
|
||||
The following is an example of the `AuthorizationRepository` capable of finding an <<jpa-authorization-entity,`Authorization`>> by the `id` field as well as the `state`, `authorizationCodeValue`, `accessTokenValue` and `refreshTokenValue` token fields. It also allows querying a combination of token fields. |
||||
|
||||
.Authorization Repository |
||||
[source,java] |
||||
---- |
||||
include::{examples-dir}/src/main/java/sample/jpa/AuthorizationRepository.java[tag=class] |
||||
---- |
||||
|
||||
[[jpa-authorization-consent-repository]] |
||||
=== Authorization Consent Repository |
||||
|
||||
The following is an example of the `AuthorizationConsentRepository` capable of finding and deleting an <<jpa-authorization-consent-entity,`AuthorizationConsent`>> by the `registeredClientId` and `principalName` fields, which form a composite primary key. |
||||
|
||||
.Authorization Consent Repository |
||||
[source,java] |
||||
---- |
||||
include::{examples-dir}/src/main/java/sample/jpa/AuthorizationConsentRepository.java[tag=class] |
||||
---- |
||||
|
||||
[[jpa-implement-core-services]] |
||||
== Implement core services |
||||
|
||||
With the above <<jpa-create-jpa-entities,entities>> and <<jpa-create-spring-data-repositories,repositories>>, we can begin implementing the core services. By reviewing the `Jdbc` implementations, we can derive a minimal set of internal utilities for converting to/from string values for enumerations and reading/writing JSON data for attributes, settings, metadata and claims fields. |
||||
|
||||
CAUTION: Keep in mind that writing JSON data to text columns with a fixed length has proven problematic with the `Jdbc` implementations. While these examples continue to do so, you may need to split these fields out into a separate table or data store that supports arbitrarily long data values. |
||||
|
||||
[[jpa-registered-client-repository]] |
||||
=== Registered Client Repository |
||||
|
||||
The following is an example of the `JpaRegisteredClientRepository` which uses a <<jpa-client-repository,`ClientRepository`>> for persisting a <<jpa-client-entity,`Client`>>, and maps to/from the xref:{docs-dir}/core-components.adoc#registered-client-repository[`RegisteredClient`] domain object. |
||||
|
||||
.`RegisteredClientRepository` Implementation |
||||
[source,java] |
||||
---- |
||||
include::{examples-dir}/src/main/java/sample/jpa/JpaRegisteredClientRepository.java[tag=class] |
||||
---- |
||||
|
||||
[[jpa-authorization-service]] |
||||
=== Authorization Service |
||||
|
||||
The following is an example of the `JpaOAuth2AuthorizationService` which uses an <<jpa-authorization-repository,`AuthorizationRepository`>> for persisting an <<jpa-authorization-entity,`Authorization`>>, and maps to/from the xref:{docs-dir}/core-components.adoc#oauth2-authorization-service[`OAuth2Authorization`] domain object. |
||||
|
||||
.`OAuth2AuthorizationService` Implementation |
||||
[source,java] |
||||
---- |
||||
include::{examples-dir}/src/main/java/sample/jpa/JpaOAuth2AuthorizationService.java[tag=class] |
||||
---- |
||||
|
||||
[[jpa-authorization-consent-service]] |
||||
=== Authorization Consent Service |
||||
|
||||
The following is an example of the `JpaOAuth2AuthorizationConsentService` which uses an <<jpa-authorization-consent-repository,`AuthorizationConsentRepository`>> for persisting an <<jpa-authorization-consent-entity,`AuthorizationConsent`>>, and maps to/from the xref:{docs-dir}/core-components.adoc#oauth2-authorization-consent-service[`OAuth2AuthorizationConsent`] domain object. |
||||
|
||||
.`OAuth2AuthorizationConsentService` Implementation |
||||
[source,java] |
||||
---- |
||||
include::{examples-dir}/src/main/java/sample/jpa/JpaOAuth2AuthorizationConsentService.java[tag=class] |
||||
---- |
||||
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
<productname>Spring Authorization Server</productname> |
||||
<releaseinfo>{spring-authorization-server-version}</releaseinfo> |
||||
<copyright> |
||||
<year>2020-2022</year> |
||||
</copyright> |
||||
<legalnotice> |
||||
<para> |
||||
Copies of this document may be made for your own use and for distribution to |
||||
others, provided that you do not charge any fee for such copies and further provided |
||||
that each copy contains this Copyright Notice, whether distributed in print or |
||||
electronically. |
||||
</para> |
||||
</legalnotice> |
||||
@ -0,0 +1,14 @@
@@ -0,0 +1,14 @@
|
||||
[[top]] |
||||
= Spring Authorization Server Reference |
||||
Joe Grandja, Steve Riesenberg |
||||
v{spring-authorization-server-version} |
||||
|
||||
[horizontal] |
||||
xref:overview.adoc[Overview] :: Introduction and list of supported features |
||||
xref:getting-help.adoc[Getting Help] :: Links to samples, questions and issues |
||||
xref:getting-started.adoc[Getting Started] :: System requirements, dependencies and developing your first application |
||||
xref:configuration-model.adoc[Configuration Model] :: Configuration and provider settings |
||||
xref:core-components.adoc[Core Components] :: Domain objects and service interfaces |
||||
xref:protocol-endpoints.adoc[Protocol Endpoints] :: Specifications and endpoints |
||||
xref:how-to.adoc["How-to" Guides] :: Guides to get the most from Spring Authorization Server |
||||
xref:appendix.adoc[Appendices] :: Additional information |
||||
@ -1,4 +1,7 @@
@@ -1,4 +1,7 @@
|
||||
[[overview]] |
||||
= Overview |
||||
:toc: left |
||||
:toclevels: 1 |
||||
|
||||
This page is under construction. |
||||
|
||||
Loading…
Reference in new issue