how-to-jpa.adoc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. [[how-to-jpa]]
  2. = How-to: Implement core services with JPA
  3. :index-link: ../how-to.html
  4. :docs-dir: ..
  5. This guide shows how to implement the xref:core-model-components.adoc[core services] of xref:index.adoc[Spring Authorization Server] with JPA.
  6. 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.
  7. * xref:guides/how-to-jpa.adoc#define-data-model[Define the data model]
  8. * xref:guides/how-to-jpa.adoc#create-jpa-entities[Create JPA entities]
  9. * xref:guides/how-to-jpa.adoc#create-spring-data-repositories[Create Spring Data repositories]
  10. * xref:guides/how-to-jpa.adoc#implement-core-services[Implement core services]
  11. [[define-data-model]]
  12. == Define the data model
  13. This guide provides a starting point for the data model and uses the simplest possible structure and data types.
  14. To come up with the initial schema, we begin by reviewing the xref:core-model-components.adoc[domain objects] used by the core services.
  15. [NOTE]
  16. Except for token, state, metadata, settings, and claims values, we use the JPA default column length of 255 for all columns.
  17. In reality, the length and even type of columns you use may need to be customized.
  18. You are encouraged to experiment and test before deploying to production.
  19. * xref:guides/how-to-jpa.adoc#client-schema[Client Schema]
  20. * xref:guides/how-to-jpa.adoc#authorization-schema[Authorization Schema]
  21. * xref:guides/how-to-jpa.adoc#authorization-consent-schema[Authorization Consent Schema]
  22. [[client-schema]]
  23. === Client Schema
  24. The xref:core-model-components.adoc#registered-client[`RegisteredClient`] domain object contains a few multi-valued fields and some settings fields that require storing arbitrary key/value data.
  25. The following listing shows the `client` schema.
  26. .Client Schema
  27. [source,sql]
  28. ----
  29. CREATE TABLE client (
  30. id varchar(255) NOT NULL,
  31. clientId varchar(255) NOT NULL,
  32. clientIdIssuedAt timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL,
  33. clientSecret varchar(255) DEFAULT NULL,
  34. clientSecretExpiresAt timestamp DEFAULT NULL,
  35. clientName varchar(255) NOT NULL,
  36. clientAuthenticationMethods varchar(1000) NOT NULL,
  37. authorizationGrantTypes varchar(1000) NOT NULL,
  38. redirectUris varchar(1000) DEFAULT NULL,
  39. postLogoutRedirectUris varchar(1000) DEFAULT NULL,
  40. scopes varchar(1000) NOT NULL,
  41. clientSettings varchar(2000) NOT NULL,
  42. tokenSettings varchar(2000) NOT NULL,
  43. PRIMARY KEY (id)
  44. );
  45. ----
  46. [[authorization-schema]]
  47. === Authorization Schema
  48. The xref: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.
  49. The built-in JDBC implementation utilizes a flattened structure that prefers performance over normalization, which we adopt here as well.
  50. [CAUTION]
  51. It has been difficult to find a flattened database schema that works well in all cases and with all database vendors.
  52. You may need to normalize or heavily alter the following schema for your needs.
  53. The following listing shows the `authorization` schema.
  54. .Authorization Schema
  55. [source,sql]
  56. ----
  57. CREATE TABLE authorization (
  58. id varchar(255) NOT NULL,
  59. registeredClientId varchar(255) NOT NULL,
  60. principalName varchar(255) NOT NULL,
  61. authorizationGrantType varchar(255) NOT NULL,
  62. authorizedScopes varchar(1000) DEFAULT NULL,
  63. attributes varchar(4000) DEFAULT NULL,
  64. state varchar(500) DEFAULT NULL,
  65. authorizationCodeValue varchar(4000) DEFAULT NULL,
  66. authorizationCodeIssuedAt timestamp DEFAULT NULL,
  67. authorizationCodeExpiresAt timestamp DEFAULT NULL,
  68. authorizationCodeMetadata varchar(2000) DEFAULT NULL,
  69. accessTokenValue varchar(4000) DEFAULT NULL,
  70. accessTokenIssuedAt timestamp DEFAULT NULL,
  71. accessTokenExpiresAt timestamp DEFAULT NULL,
  72. accessTokenMetadata varchar(2000) DEFAULT NULL,
  73. accessTokenType varchar(255) DEFAULT NULL,
  74. accessTokenScopes varchar(1000) DEFAULT NULL,
  75. refreshTokenValue varchar(4000) DEFAULT NULL,
  76. refreshTokenIssuedAt timestamp DEFAULT NULL,
  77. refreshTokenExpiresAt timestamp DEFAULT NULL,
  78. refreshTokenMetadata varchar(2000) DEFAULT NULL,
  79. oidcIdTokenValue varchar(4000) DEFAULT NULL,
  80. oidcIdTokenIssuedAt timestamp DEFAULT NULL,
  81. oidcIdTokenExpiresAt timestamp DEFAULT NULL,
  82. oidcIdTokenMetadata varchar(2000) DEFAULT NULL,
  83. oidcIdTokenClaims varchar(2000) DEFAULT NULL,
  84. userCodeValue varchar(4000) DEFAULT NULL,
  85. userCodeIssuedAt timestamp DEFAULT NULL,
  86. userCodeExpiresAt timestamp DEFAULT NULL,
  87. userCodeMetadata varchar(2000) DEFAULT NULL,
  88. deviceCodeValue varchar(4000) DEFAULT NULL,
  89. deviceCodeIssuedAt timestamp DEFAULT NULL,
  90. deviceCodeExpiresAt timestamp DEFAULT NULL,
  91. deviceCodeMetadata varchar(2000) DEFAULT NULL,
  92. PRIMARY KEY (id)
  93. );
  94. ----
  95. [[authorization-consent-schema]]
  96. === Authorization Consent Schema
  97. The xref:core-model-components.adoc#oauth2-authorization-consent[`OAuth2AuthorizationConsent`] domain object is the simplest to model and contains only a single multi-valued field in addition to a composite key.
  98. The following listing shows the `authorizationconsent` schema.
  99. .Authorization Consent Schema
  100. [source,sql]
  101. ----
  102. CREATE TABLE authorizationConsent (
  103. registeredClientId varchar(255) NOT NULL,
  104. principalName varchar(255) NOT NULL,
  105. authorities varchar(1000) NOT NULL,
  106. PRIMARY KEY (registeredClientId, principalName)
  107. );
  108. ----
  109. [[create-jpa-entities]]
  110. == Create JPA entities
  111. The preceding schema examples provide a reference for the structure of the entities we need to create.
  112. [NOTE]
  113. The following entities are minimally annotated and are just examples.
  114. They allow the schema to be created dynamically and therefore do not require the above sql scripts to be executed manually.
  115. * xref:guides/how-to-jpa.adoc#client-entity[Client Entity]
  116. * xref:guides/how-to-jpa.adoc#authorization-entity[Authorization Entity]
  117. * xref:guides/how-to-jpa.adoc#authorization-consent-entity[Authorization Consent Entity]
  118. [[client-entity]]
  119. === Client Entity
  120. The following listing shows the `Client` entity, which is used to persist information mapped from the xref:core-model-components.adoc#registered-client[`RegisteredClient`] domain object.
  121. [[sample.jpa.entity.client]]
  122. .Client Entity
  123. [source,java]
  124. ----
  125. include::{examples-dir}/main/java/sample/jpa/entity/client/Client.java[]
  126. ----
  127. [[authorization-entity]]
  128. === Authorization Entity
  129. The following listing shows the `Authorization` entity, which is used to persist information mapped from the xref:core-model-components.adoc#oauth2-authorization[`OAuth2Authorization`] domain object.
  130. [[sample.jpa.entity.authorization]]
  131. .Authorization Entity
  132. [source,java]
  133. ----
  134. include::{examples-dir}/main/java/sample/jpa/entity/authorization/Authorization.java[]
  135. ----
  136. [[authorization-consent-entity]]
  137. === Authorization Consent Entity
  138. The following listing shows the `AuthorizationConsent` entity, which is used to persist information mapped from the xref:core-model-components.adoc#oauth2-authorization-consent[`OAuth2AuthorizationConsent`] domain object.
  139. [[sample.jpa.entity.authorizationconsent]]
  140. .Authorization Consent Entity
  141. [source,java]
  142. ----
  143. include::{examples-dir}/main/java/sample/jpa/entity/authorizationconsent/AuthorizationConsent.java[]
  144. ----
  145. [[create-spring-data-repositories]]
  146. == Create Spring Data repositories
  147. 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.
  148. * xref:guides/how-to-jpa.adoc#client-repository[Client Repository]
  149. * xref:guides/how-to-jpa.adoc#authorization-repository[Authorization Repository]
  150. * xref:guides/how-to-jpa.adoc#authorization-consent-repository[Authorization Consent Repository]
  151. [[client-repository]]
  152. === Client Repository
  153. The following listing shows the `ClientRepository`, which is able to find a xref:guides/how-to-jpa.adoc#client-entity[`Client`] by the `id` and `clientId` fields.
  154. [[sample.jpa.repository.client]]
  155. .Client Repository
  156. [source,java]
  157. ----
  158. include::{examples-dir}/main/java/sample/jpa/repository/client/ClientRepository.java[]
  159. ----
  160. [[authorization-repository]]
  161. === Authorization Repository
  162. The following listing shows the `AuthorizationRepository`, which is able to find an xref:guides/how-to-jpa.adoc#authorization-entity[`Authorization`] by the `id` field as well as the `state`, `authorizationCodeValue`, `accessTokenValue`, `refreshTokenValue`, `userCodeValue` and `deviceCodeValue` token fields.
  163. It also allows querying a combination of token fields.
  164. [[sample.jpa.repository.authorization]]
  165. .Authorization Repository
  166. [source,java]
  167. ----
  168. include::{examples-dir}/main/java/sample/jpa/repository/authorization/AuthorizationRepository.java[]
  169. ----
  170. [[authorization-consent-repository]]
  171. === Authorization Consent Repository
  172. The following listing shows the `AuthorizationConsentRepository`, which is able to find and delete an xref:guides/how-to-jpa.adoc#authorization-consent-entity[`AuthorizationConsent`] by the `registeredClientId` and `principalName` fields that form a composite primary key.
  173. [[sample.jpa.repository.authorizationconsent]]
  174. .Authorization Consent Repository
  175. [source,java]
  176. ----
  177. include::{examples-dir}/main/java/sample/jpa/repository/authorizationconsent/AuthorizationConsentRepository.java[]
  178. ----
  179. [[implement-core-services]]
  180. == Implement core services
  181. With the above xref:guides/how-to-jpa.adoc#create-jpa-entities[entities] and xref:guides/how-to-jpa.adoc#create-spring-data-repositories[repositories], we can begin implementing the core services.
  182. 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.
  183. [CAUTION]
  184. Keep in mind that writing JSON data to text columns with a fixed length has proven problematic with the `Jdbc` implementations.
  185. 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.
  186. * <<registered-client-repository>>
  187. * xref:guides/how-to-jpa.adoc#authorization-service[Authorization Service]
  188. * xref:guides/how-to-jpa.adoc#authorization-consent-service[Authorization Consent Service]
  189. [[registered-client-repository]]
  190. === Registered Client Repository
  191. The following listing shows the `JpaRegisteredClientRepository`, which uses a xref:guides/how-to-jpa.adoc#client-repository[`ClientRepository`] for persisting a xref:guides/how-to-jpa.adoc#client-entity[`Client`] and maps to and from the xref:core-model-components.adoc#registered-client[`RegisteredClient`] domain object.
  192. [[sample.jpa.service.client]]
  193. .`RegisteredClientRepository` Implementation
  194. [source,java]
  195. ----
  196. include::{examples-dir}/main/java/sample/jpa/service/client/JpaRegisteredClientRepository.java[]
  197. ----
  198. [[authorization-service]]
  199. === Authorization Service
  200. The following listing shows the `JpaOAuth2AuthorizationService`, which uses an xref:guides/how-to-jpa.adoc#authorization-repository[`AuthorizationRepository`] for persisting an xref:guides/how-to-jpa.adoc#authorization-entity[`Authorization`] and maps to and from the xref:core-model-components.adoc#oauth2-authorization[`OAuth2Authorization`] domain object.
  201. [[sample.jpa.service.authorization]]
  202. .`OAuth2AuthorizationService` Implementation
  203. [source,java]
  204. ----
  205. include::{examples-dir}/main/java/sample/jpa/service/authorization/JpaOAuth2AuthorizationService.java[]
  206. ----
  207. [[authorization-consent-service]]
  208. === Authorization Consent Service
  209. The following listing shows the `JpaOAuth2AuthorizationConsentService`, which uses an xref:guides/how-to-jpa.adoc#authorization-consent-repository[`AuthorizationConsentRepository`] for persisting an xref:guides/how-to-jpa.adoc#authorization-consent-entity[`AuthorizationConsent`] and maps to and from the xref:core-model-components.adoc#oauth2-authorization-consent[`OAuth2AuthorizationConsent`] domain object.
  210. [[sample.jpa.service.authorizationconsent]]
  211. .`OAuth2AuthorizationConsentService` Implementation
  212. [source,java]
  213. ----
  214. include::{examples-dir}/main/java/sample/jpa/service/authorizationconsent/JpaOAuth2AuthorizationConsentService.java[]
  215. ----