소스 검색

Polish "How-to: Implement core services with JPA"

Issue gh-545
Steve Riesenberg 3 년 전
부모
커밋
7edbe2a30d
14개의 변경된 파일108개의 추가작업 그리고 162개의 파일을 삭제
  1. 3 9
      docs/src/docs/asciidoc/examples/src/main/java/sample/jpa/entity/authorization/Authorization.java
  2. 5 15
      docs/src/docs/asciidoc/examples/src/main/java/sample/jpa/entity/authorizationConsent/AuthorizationConsent.java
  3. 3 9
      docs/src/docs/asciidoc/examples/src/main/java/sample/jpa/entity/client/Client.java
  4. 3 6
      docs/src/docs/asciidoc/examples/src/main/java/sample/jpa/repository/authorization/AuthorizationRepository.java
  5. 3 6
      docs/src/docs/asciidoc/examples/src/main/java/sample/jpa/repository/authorizationConsent/AuthorizationConsentRepository.java
  6. 3 6
      docs/src/docs/asciidoc/examples/src/main/java/sample/jpa/repository/client/ClientRepository.java
  7. 3 6
      docs/src/docs/asciidoc/examples/src/main/java/sample/jpa/service/authorization/JpaOAuth2AuthorizationService.java
  8. 4 6
      docs/src/docs/asciidoc/examples/src/main/java/sample/jpa/service/authorizationConsent/JpaOAuth2AuthorizationConsentService.java
  9. 3 6
      docs/src/docs/asciidoc/examples/src/main/java/sample/jpa/service/client/JpaRegisteredClientRepository.java
  10. 0 6
      docs/src/docs/asciidoc/examples/src/main/resources/oauth2-authorization-consent-schema.sql
  11. 0 28
      docs/src/docs/asciidoc/examples/src/main/resources/oauth2-authorization-schema.sql
  12. 0 15
      docs/src/docs/asciidoc/examples/src/main/resources/oauth2-registered-client-schema.sql
  13. 3 0
      docs/src/docs/asciidoc/examples/src/test/java/sample/jpa/JpaTests.java
  14. 75 44
      docs/src/docs/asciidoc/guides/how-to-jpa.adoc

+ 3 - 9
docs/src/docs/asciidoc/examples/src/main/java/sample/jpa/Authorization.java → docs/src/docs/asciidoc/examples/src/main/java/sample/jpa/entity/authorization/Authorization.java

@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package sample.jpa;
+package sample.jpa.entity.authorization;
 
 import java.time.Instant;
 
@@ -21,10 +21,6 @@ import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.Id;
 
-/**
- * @author Steve Riesenberg
- */
-// tag::class[]
 @Entity
 public class Authorization {
 	@Id
@@ -69,8 +65,7 @@ public class Authorization {
 	@Column(length = 2000)
 	private String oidcIdTokenClaims;
 
-	// getters and setters
-// end::class[]
+	// @fold:on
 	public String getId() {
 		return id;
 	}
@@ -270,6 +265,5 @@ public class Authorization {
 	public void setOidcIdTokenClaims(String idTokenClaims) {
 		this.oidcIdTokenClaims = idTokenClaims;
 	}
-// tag::class[]
+	// @fold:off
 }
-// end::class[]

+ 5 - 15
docs/src/docs/asciidoc/examples/src/main/java/sample/jpa/AuthorizationConsent.java → docs/src/docs/asciidoc/examples/src/main/java/sample/jpa/entity/authorizationConsent/AuthorizationConsent.java

@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package sample.jpa;
+package sample.jpa.entity.authorizationConsent;
 
 import java.io.Serializable;
 import java.util.Objects;
@@ -23,10 +23,6 @@ import javax.persistence.Entity;
 import javax.persistence.Id;
 import javax.persistence.IdClass;
 
-/**
- * @author Steve Riesenberg
- */
-// tag::class[]
 @Entity
 @IdClass(AuthorizationConsent.AuthorizationConsentId.class)
 public class AuthorizationConsent {
@@ -37,8 +33,7 @@ public class AuthorizationConsent {
 	@Column(length = 1000)
 	private String authorities;
 
-	// getters and setters
-// end::class[]
+	// @fold:on
 	public String getRegisteredClientId() {
 		return registeredClientId;
 	}
@@ -62,14 +57,13 @@ public class AuthorizationConsent {
 	public void setAuthorities(String authorities) {
 		this.authorities = authorities;
 	}
-// tag::class[]
+	// @fold:off
 
 	public static class AuthorizationConsentId implements Serializable {
 		private String registeredClientId;
 		private String principalName;
 
-		// getters and setters
-// end::class[]
+		// @fold:on
 		public String getRegisteredClientId() {
 			return registeredClientId;
 		}
@@ -85,10 +79,8 @@ public class AuthorizationConsent {
 		public void setPrincipalName(String principalName) {
 			this.principalName = principalName;
 		}
-// tag::class[]
+		// @fold:off
 
-		// equals and hashCode
-// end::class[]
 		@Override
 		public boolean equals(Object o) {
 			if (this == o) return true;
@@ -101,7 +93,5 @@ public class AuthorizationConsent {
 		public int hashCode() {
 			return Objects.hash(registeredClientId, principalName);
 		}
-// tag::class[]
 	}
 }
-// end::class[]

+ 3 - 9
docs/src/docs/asciidoc/examples/src/main/java/sample/jpa/Client.java → docs/src/docs/asciidoc/examples/src/main/java/sample/jpa/entity/client/Client.java

@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package sample.jpa;
+package sample.jpa.entity.client;
 
 import java.time.Instant;
 
@@ -21,10 +21,6 @@ import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.Id;
 
-/**
- * @author Steve Riesenberg
- */
-// tag::class[]
 @Entity
 public class Client {
 	@Id
@@ -47,8 +43,7 @@ public class Client {
 	@Column(length = 2000)
 	private String tokenSettings;
 
-	// getters and setters
-// end::class[]
+	// @fold:on
 	public String getId() {
 		return id;
 	}
@@ -144,6 +139,5 @@ public class Client {
 	public void setTokenSettings(String tokenSettings) {
 		this.tokenSettings = tokenSettings;
 	}
-// tag::class[]
+	// @fold:off
 }
-// end::class[]

+ 3 - 6
docs/src/docs/asciidoc/examples/src/main/java/sample/jpa/AuthorizationRepository.java → docs/src/docs/asciidoc/examples/src/main/java/sample/jpa/repository/authorization/AuthorizationRepository.java

@@ -13,19 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package sample.jpa;
+package sample.jpa.repository.authorization;
 
 import java.util.Optional;
 
+import sample.jpa.entity.authorization.Authorization;
+
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.Query;
 import org.springframework.data.repository.query.Param;
 import org.springframework.stereotype.Repository;
 
-/**
- * @author Steve Riesenberg
- */
-// tag::class[]
 @Repository
 public interface AuthorizationRepository extends JpaRepository<Authorization, String> {
 	Optional<Authorization> findByState(String state);
@@ -39,4 +37,3 @@ public interface AuthorizationRepository extends JpaRepository<Authorization, St
 	)
 	Optional<Authorization> findByStateOrAuthorizationCodeValueOrAccessTokenValueOrRefreshTokenValue(@Param("token") String token);
 }
-// end::class[]

+ 3 - 6
docs/src/docs/asciidoc/examples/src/main/java/sample/jpa/AuthorizationConsentRepository.java → docs/src/docs/asciidoc/examples/src/main/java/sample/jpa/repository/authorizationConsent/AuthorizationConsentRepository.java

@@ -13,20 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package sample.jpa;
+package sample.jpa.repository.authorizationConsent;
 
 import java.util.Optional;
 
+import sample.jpa.entity.authorizationConsent.AuthorizationConsent;
+
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.stereotype.Repository;
 
-/**
- * @author Steve Riesenberg
- */
-// tag::class[]
 @Repository
 public interface AuthorizationConsentRepository extends JpaRepository<AuthorizationConsent, AuthorizationConsent.AuthorizationConsentId> {
 	Optional<AuthorizationConsent> findByRegisteredClientIdAndPrincipalName(String registeredClientId, String principalName);
 	void deleteByRegisteredClientIdAndPrincipalName(String registeredClientId, String principalName);
 }
-// end::class[]

+ 3 - 6
docs/src/docs/asciidoc/examples/src/main/java/sample/jpa/ClientRepository.java → docs/src/docs/asciidoc/examples/src/main/java/sample/jpa/repository/client/ClientRepository.java

@@ -13,19 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package sample.jpa;
+package sample.jpa.repository.client;
 
 import java.util.Optional;
 
+import sample.jpa.entity.client.Client;
+
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.stereotype.Repository;
 
-/**
- * @author Steve Riesenberg
- */
-// tag::class[]
 @Repository
 public interface ClientRepository extends JpaRepository<Client, String> {
 	Optional<Client> findByClientId(String clientId);
 }
-// end::class[]

+ 3 - 6
docs/src/docs/asciidoc/examples/src/main/java/sample/jpa/JpaOAuth2AuthorizationService.java → docs/src/docs/asciidoc/examples/src/main/java/sample/jpa/service/authorization/JpaOAuth2AuthorizationService.java

@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package sample.jpa;
+package sample.jpa.service.authorization;
 
 import java.time.Instant;
 import java.util.List;
@@ -24,6 +24,8 @@ import java.util.function.Consumer;
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.Module;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import sample.jpa.entity.authorization.Authorization;
+import sample.jpa.repository.authorization.AuthorizationRepository;
 
 import org.springframework.dao.DataRetrievalFailureException;
 import org.springframework.security.jackson2.SecurityJackson2Modules;
@@ -44,10 +46,6 @@ import org.springframework.stereotype.Component;
 import org.springframework.util.Assert;
 import org.springframework.util.StringUtils;
 
-/**
- * @author Steve Riesenberg
- */
-// tag::class[]
 @Component
 public class JpaOAuth2AuthorizationService implements OAuth2AuthorizationService {
 	private final AuthorizationRepository authorizationRepository;
@@ -261,4 +259,3 @@ public class JpaOAuth2AuthorizationService implements OAuth2AuthorizationService
 		return new AuthorizationGrantType(authorizationGrantType);              // Custom authorization grant type
 	}
 }
-// end::class[]

+ 4 - 6
docs/src/docs/asciidoc/examples/src/main/java/sample/jpa/JpaOAuth2AuthorizationConsentService.java → docs/src/docs/asciidoc/examples/src/main/java/sample/jpa/service/authorizationConsent/JpaOAuth2AuthorizationConsentService.java

@@ -13,11 +13,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package sample.jpa;
+package sample.jpa.service.authorizationConsent;
 
 import java.util.HashSet;
 import java.util.Set;
 
+import sample.jpa.entity.authorizationConsent.AuthorizationConsent;
+import sample.jpa.repository.authorizationConsent.AuthorizationConsentRepository;
+
 import org.springframework.dao.DataRetrievalFailureException;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.authority.SimpleGrantedAuthority;
@@ -29,10 +32,6 @@ import org.springframework.stereotype.Component;
 import org.springframework.util.Assert;
 import org.springframework.util.StringUtils;
 
-/**
- * @author Steve Riesenberg
- */
-// tag::class[]
 @Component
 public class JpaOAuth2AuthorizationConsentService implements OAuth2AuthorizationConsentService {
 	private final AuthorizationConsentRepository authorizationConsentRepository;
@@ -99,4 +98,3 @@ public class JpaOAuth2AuthorizationConsentService implements OAuth2Authorization
 		return entity;
 	}
 }
-// end::class[]

+ 3 - 6
docs/src/docs/asciidoc/examples/src/main/java/sample/jpa/JpaRegisteredClientRepository.java → docs/src/docs/asciidoc/examples/src/main/java/sample/jpa/service/client/JpaRegisteredClientRepository.java

@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package sample.jpa;
+package sample.jpa.service.client;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -23,6 +23,8 @@ import java.util.Set;
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.Module;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import sample.jpa.entity.client.Client;
+import sample.jpa.repository.client.ClientRepository;
 
 import org.springframework.security.jackson2.SecurityJackson2Modules;
 import org.springframework.security.oauth2.core.AuthorizationGrantType;
@@ -36,10 +38,6 @@ import org.springframework.stereotype.Component;
 import org.springframework.util.Assert;
 import org.springframework.util.StringUtils;
 
-/**
- * @author Steve Riesenberg
- */
-// tag::class[]
 @Component
 public class JpaRegisteredClientRepository implements RegisteredClientRepository {
 	private final ClientRepository clientRepository;
@@ -172,4 +170,3 @@ public class JpaRegisteredClientRepository implements RegisteredClientRepository
 		return new ClientAuthenticationMethod(clientAuthenticationMethod);      // Custom client authentication method
 	}
 }
-// end::class[]

+ 0 - 6
docs/src/docs/asciidoc/examples/src/main/resources/oauth2-authorization-consent-schema.sql

@@ -1,6 +0,0 @@
-CREATE TABLE authorizationConsent (
-    registeredClientId varchar(255) NOT NULL,
-    principalName varchar(255) NOT NULL,
-    authorities varchar(1000) NOT NULL,
-    PRIMARY KEY (registeredClientId, principalName)
-);

+ 0 - 28
docs/src/docs/asciidoc/examples/src/main/resources/oauth2-authorization-schema.sql

@@ -1,28 +0,0 @@
-CREATE TABLE authorization (
-    id varchar(255) NOT NULL,
-    registeredClientId varchar(255) NOT NULL,
-    principalName varchar(255) NOT NULL,
-    authorizationGrantType varchar(255) NOT NULL,
-    attributes varchar(4000) DEFAULT NULL,
-    state varchar(500) DEFAULT NULL,
-    authorizationCodeValue varchar(4000) DEFAULT NULL,
-    authorizationCodeIssuedAt timestamp DEFAULT NULL,
-    authorizationCodeExpiresAt timestamp DEFAULT NULL,
-    authorizationCodeMetadata varchar(2000) DEFAULT NULL,
-    accessTokenValue varchar(4000) DEFAULT NULL,
-    accessTokenIssuedAt timestamp DEFAULT NULL,
-    accessTokenExpiresAt timestamp DEFAULT NULL,
-    accessTokenMetadata varchar(2000) DEFAULT NULL,
-    accessTokenType varchar(255) DEFAULT NULL,
-    accessTokenScopes varchar(1000) DEFAULT NULL,
-    refreshTokenValue varchar(4000) DEFAULT NULL,
-    refreshTokenIssuedAt timestamp DEFAULT NULL,
-    refreshTokenExpiresAt timestamp DEFAULT NULL,
-    refreshTokenMetadata varchar(2000) DEFAULT NULL,
-    oidcIdTokenValue varchar(4000) DEFAULT NULL,
-    oidcIdTokenIssuedAt timestamp DEFAULT NULL,
-    oidcIdTokenExpiresAt timestamp DEFAULT NULL,
-    oidcIdTokenMetadata varchar(2000) DEFAULT NULL,
-    oidcIdTokenClaims varchar(2000) DEFAULT NULL,
-    PRIMARY KEY (id)
-);

+ 0 - 15
docs/src/docs/asciidoc/examples/src/main/resources/oauth2-registered-client-schema.sql

@@ -1,15 +0,0 @@
-CREATE TABLE client (
-    id varchar(255) NOT NULL,
-    clientId varchar(255) NOT NULL,
-    clientIdIssuedAt timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL,
-    clientSecret varchar(255) DEFAULT NULL,
-    clientSecretExpiresAt timestamp DEFAULT NULL,
-    clientName varchar(255) NOT NULL,
-    clientAuthenticationMethods varchar(1000) NOT NULL,
-    authorizationGrantTypes varchar(1000) NOT NULL,
-    redirectUris varchar(1000) DEFAULT NULL,
-    scopes varchar(1000) NOT NULL,
-    clientSettings varchar(2000) NOT NULL,
-    tokenSettings varchar(2000) NOT NULL,
-    PRIMARY KEY (id)
-);

+ 3 - 0
docs/src/docs/asciidoc/examples/src/test/java/sample/jpa/JpaTests.java

@@ -26,6 +26,9 @@ import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import sample.AuthorizationCodeGrantFlow;
 import sample.jose.TestJwks;
+import sample.jpa.service.authorization.JpaOAuth2AuthorizationService;
+import sample.jpa.service.authorizationConsent.JpaOAuth2AuthorizationConsentService;
+import sample.jpa.service.client.JpaRegisteredClientRepository;
 import sample.test.SpringTestContext;
 import sample.test.SpringTestContextExtension;
 

+ 75 - 44
docs/src/docs/asciidoc/guides/how-to-jpa.adoc

@@ -2,7 +2,6 @@
 = How-to: Implement core services with JPA
 :index-link: ../how-to.html
 :docs-dir: ..
-:examples-dir: ../examples
 
 This guide shows how to implement the xref:{docs-dir}/core-model-components.adoc#core-model-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 can make modifications to suit your needs.
@@ -18,7 +17,8 @@ The purpose of this guide is to provide a starting point for implementing these
 This guide provides 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-model-components.adoc#core-model-components[domain objects] used by the core services.
 
-NOTE: Except for token, state, metadata, settings, and claims values, we use the JPA default column length of 255 for all columns.
+[NOTE]
+Except for token, state, metadata, settings, and claims values, we 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.
 You are encouraged to experiment and test before deploying to production.
 
@@ -35,7 +35,21 @@ The following listing shows the `client` schema.
 .Client Schema
 [source,sql]
 ----
-include::{examples-dir}/src/main/resources/oauth2-registered-client-schema.sql[]
+CREATE TABLE client (
+    id varchar(255) NOT NULL,
+    clientId varchar(255) NOT NULL,
+    clientIdIssuedAt timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    clientSecret varchar(255) DEFAULT NULL,
+    clientSecretExpiresAt timestamp DEFAULT NULL,
+    clientName varchar(255) NOT NULL,
+    clientAuthenticationMethods varchar(1000) NOT NULL,
+    authorizationGrantTypes varchar(1000) NOT NULL,
+    redirectUris varchar(1000) DEFAULT NULL,
+    scopes varchar(1000) NOT NULL,
+    clientSettings varchar(2000) NOT NULL,
+    tokenSettings varchar(2000) NOT NULL,
+    PRIMARY KEY (id)
+);
 ----
 
 [[authorization-schema]]
@@ -44,7 +58,8 @@ include::{examples-dir}/src/main/resources/oauth2-registered-client-schema.sql[]
 The xref:{docs-dir}/core-model-components.adoc#oauth2-authorization[`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.
+[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 listing shows the `authorization` schema.
@@ -52,7 +67,34 @@ The following listing shows the `authorization` schema.
 .Authorization Schema
 [source,sql]
 ----
-include::{examples-dir}/src/main/resources/oauth2-authorization-schema.sql[]
+CREATE TABLE authorization (
+    id varchar(255) NOT NULL,
+    registeredClientId varchar(255) NOT NULL,
+    principalName varchar(255) NOT NULL,
+    authorizationGrantType varchar(255) NOT NULL,
+    attributes varchar(4000) DEFAULT NULL,
+    state varchar(500) DEFAULT NULL,
+    authorizationCodeValue varchar(4000) DEFAULT NULL,
+    authorizationCodeIssuedAt timestamp DEFAULT NULL,
+    authorizationCodeExpiresAt timestamp DEFAULT NULL,
+    authorizationCodeMetadata varchar(2000) DEFAULT NULL,
+    accessTokenValue varchar(4000) DEFAULT NULL,
+    accessTokenIssuedAt timestamp DEFAULT NULL,
+    accessTokenExpiresAt timestamp DEFAULT NULL,
+    accessTokenMetadata varchar(2000) DEFAULT NULL,
+    accessTokenType varchar(255) DEFAULT NULL,
+    accessTokenScopes varchar(1000) DEFAULT NULL,
+    refreshTokenValue varchar(4000) DEFAULT NULL,
+    refreshTokenIssuedAt timestamp DEFAULT NULL,
+    refreshTokenExpiresAt timestamp DEFAULT NULL,
+    refreshTokenMetadata varchar(2000) DEFAULT NULL,
+    oidcIdTokenValue varchar(4000) DEFAULT NULL,
+    oidcIdTokenIssuedAt timestamp DEFAULT NULL,
+    oidcIdTokenExpiresAt timestamp DEFAULT NULL,
+    oidcIdTokenMetadata varchar(2000) DEFAULT NULL,
+    oidcIdTokenClaims varchar(2000) DEFAULT NULL,
+    PRIMARY KEY (id)
+);
 ----
 
 [[authorization-consent-schema]]
@@ -64,7 +106,12 @@ The following listing shows the `authorizationConsent` schema.
 .Authorization Consent Schema
 [source,sql]
 ----
-include::{examples-dir}/src/main/resources/oauth2-authorization-consent-schema.sql[]
+CREATE TABLE authorizationConsent (
+    registeredClientId varchar(255) NOT NULL,
+    principalName varchar(255) NOT NULL,
+    authorities varchar(1000) NOT NULL,
+    PRIMARY KEY (registeredClientId, principalName)
+);
 ----
 
 [[create-jpa-entities]]
@@ -72,7 +119,8 @@ include::{examples-dir}/src/main/resources/oauth2-authorization-consent-schema.s
 
 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.
+[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.
 
 * <<client-entity>>
@@ -84,33 +132,27 @@ They allow the schema to be created dynamically and therefore do not require the
 
 The following listing shows the `Client` entity, which is used to persist information mapped from the xref:{docs-dir}/core-model-components.adoc#registered-client[`RegisteredClient`] domain object.
 
+[[sample.jpa.entity.client]]
 .Client Entity
-[source,java]
-----
-include::{examples-dir}/src/main/java/sample/jpa/Client.java[tag=class]
-----
+include::code:Client[]
 
 [[authorization-entity]]
 === Authorization Entity
 
 The following listing shows the `Authorization` entity, which is used to persist information mapped from the xref:{docs-dir}/core-model-components.adoc#oauth2-authorization[`OAuth2Authorization`] domain object.
 
+[[sample.jpa.entity.authorization]]
 .Authorization Entity
-[source,java]
-----
-include::{examples-dir}/src/main/java/sample/jpa/Authorization.java[tag=class]
-----
+include::code:Authorization[]
 
 [[authorization-consent-entity]]
 === Authorization Consent Entity
 
 The following listing shows the `AuthorizationConsent` entity, which is used to persist information mapped from the xref:{docs-dir}/core-model-components.adoc#oauth2-authorization-consent[`OAuth2AuthorizationConsent`] domain object.
 
+[[sample.jpa.entity.authorizationConsent]]
 .Authorization Consent Entity
-[source,java]
-----
-include::{examples-dir}/src/main/java/sample/jpa/AuthorizationConsent.java[tag=class]
-----
+include::code:AuthorizationConsent[]
 
 [[create-spring-data-repositories]]
 == Create Spring Data repositories
@@ -126,11 +168,9 @@ By closely examining the interfaces of each core service and reviewing the `Jdbc
 
 The following listing shows the `ClientRepository`, which is able to find a <<client-entity,`Client`>> by the `id` and `clientId` fields.
 
+[[sample.jpa.repository.client]]
 .Client Repository
-[source,java]
-----
-include::{examples-dir}/src/main/java/sample/jpa/ClientRepository.java[tag=class]
-----
+include::code:ClientRepository[]
 
 [[authorization-repository]]
 === Authorization Repository
@@ -138,22 +178,18 @@ include::{examples-dir}/src/main/java/sample/jpa/ClientRepository.java[tag=class
 The following listing shows the `AuthorizationRepository`, which is able to find 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.
 
+[[sample.jpa.repository.authorization]]
 .Authorization Repository
-[source,java]
-----
-include::{examples-dir}/src/main/java/sample/jpa/AuthorizationRepository.java[tag=class]
-----
+include::code:AuthorizationRepository[]
 
 [[authorization-consent-repository]]
 === Authorization Consent Repository
 
 The following listing shows the `AuthorizationConsentRepository`, which is able to find and delete an <<authorization-consent-entity,`AuthorizationConsent`>> by the `registeredClientId` and `principalName` fields that form a composite primary key.
 
+[[sample.jpa.repository.authorizationConsent]]
 .Authorization Consent Repository
-[source,java]
-----
-include::{examples-dir}/src/main/java/sample/jpa/AuthorizationConsentRepository.java[tag=class]
-----
+include::code:AuthorizationConsentRepository[]
 
 [[implement-core-services]]
 == Implement core services
@@ -161,7 +197,8 @@ include::{examples-dir}/src/main/java/sample/jpa/AuthorizationConsentRepository.
 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 and from string values for enumerations and reading and 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.
+[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.
 
 * <<registered-client-repository>>
@@ -173,30 +210,24 @@ While these examples continue to do so, you may need to split these fields out i
 
 The following listing shows the `JpaRegisteredClientRepository`, which uses a <<client-repository,`ClientRepository`>> for persisting a <<client-entity,`Client`>> and maps to and from the xref:{docs-dir}/core-model-components.adoc#registered-client[`RegisteredClient`] domain object.
 
+[[sample.jpa.service.client]]
 .`RegisteredClientRepository` Implementation
-[source,java]
-----
-include::{examples-dir}/src/main/java/sample/jpa/JpaRegisteredClientRepository.java[tag=class]
-----
+include::code:JpaRegisteredClientRepository[]
 
 [[authorization-service]]
 === Authorization Service
 
 The following listing shows the `JpaOAuth2AuthorizationService`, which uses an <<authorization-repository,`AuthorizationRepository`>> for persisting an <<authorization-entity,`Authorization`>> and maps to and from the xref:{docs-dir}/core-model-components.adoc#oauth2-authorization[`OAuth2Authorization`] domain object.
 
+[[sample.jpa.service.authorization]]
 .`OAuth2AuthorizationService` Implementation
-[source,java]
-----
-include::{examples-dir}/src/main/java/sample/jpa/JpaOAuth2AuthorizationService.java[tag=class]
-----
+include::code:JpaOAuth2AuthorizationService[]
 
 [[authorization-consent-service]]
 === Authorization Consent Service
 
 The following listing shows the `JpaOAuth2AuthorizationConsentService`, which uses an <<authorization-consent-repository,`AuthorizationConsentRepository`>> for persisting an <<authorization-consent-entity,`AuthorizationConsent`>> and maps to and from the xref:{docs-dir}/core-model-components.adoc#oauth2-authorization-consent[`OAuth2AuthorizationConsent`] domain object.
 
+[[sample.jpa.service.authorizationConsent]]
 .`OAuth2AuthorizationConsentService` Implementation
-[source,java]
-----
-include::{examples-dir}/src/main/java/sample/jpa/JpaOAuth2AuthorizationConsentService.java[tag=class]
-----
+include::code:JpaOAuth2AuthorizationConsentService[]