Polish "How-to: Implement core services with JPA"
Issue gh-545
This commit is contained in:
@@ -13,7 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package sample.jpa;
|
package sample.jpa.entity.authorization;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
|
||||||
@@ -21,10 +21,6 @@ import javax.persistence.Column;
|
|||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Steve Riesenberg
|
|
||||||
*/
|
|
||||||
// tag::class[]
|
|
||||||
@Entity
|
@Entity
|
||||||
public class Authorization {
|
public class Authorization {
|
||||||
@Id
|
@Id
|
||||||
@@ -69,8 +65,7 @@ public class Authorization {
|
|||||||
@Column(length = 2000)
|
@Column(length = 2000)
|
||||||
private String oidcIdTokenClaims;
|
private String oidcIdTokenClaims;
|
||||||
|
|
||||||
// getters and setters
|
// @fold:on
|
||||||
// end::class[]
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
@@ -270,6 +265,5 @@ public class Authorization {
|
|||||||
public void setOidcIdTokenClaims(String idTokenClaims) {
|
public void setOidcIdTokenClaims(String idTokenClaims) {
|
||||||
this.oidcIdTokenClaims = idTokenClaims;
|
this.oidcIdTokenClaims = idTokenClaims;
|
||||||
}
|
}
|
||||||
// tag::class[]
|
// @fold:off
|
||||||
}
|
}
|
||||||
// end::class[]
|
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package sample.jpa;
|
package sample.jpa.entity.authorizationConsent;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@@ -23,10 +23,6 @@ import javax.persistence.Entity;
|
|||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
import javax.persistence.IdClass;
|
import javax.persistence.IdClass;
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Steve Riesenberg
|
|
||||||
*/
|
|
||||||
// tag::class[]
|
|
||||||
@Entity
|
@Entity
|
||||||
@IdClass(AuthorizationConsent.AuthorizationConsentId.class)
|
@IdClass(AuthorizationConsent.AuthorizationConsentId.class)
|
||||||
public class AuthorizationConsent {
|
public class AuthorizationConsent {
|
||||||
@@ -37,8 +33,7 @@ public class AuthorizationConsent {
|
|||||||
@Column(length = 1000)
|
@Column(length = 1000)
|
||||||
private String authorities;
|
private String authorities;
|
||||||
|
|
||||||
// getters and setters
|
// @fold:on
|
||||||
// end::class[]
|
|
||||||
public String getRegisteredClientId() {
|
public String getRegisteredClientId() {
|
||||||
return registeredClientId;
|
return registeredClientId;
|
||||||
}
|
}
|
||||||
@@ -62,14 +57,13 @@ public class AuthorizationConsent {
|
|||||||
public void setAuthorities(String authorities) {
|
public void setAuthorities(String authorities) {
|
||||||
this.authorities = authorities;
|
this.authorities = authorities;
|
||||||
}
|
}
|
||||||
// tag::class[]
|
// @fold:off
|
||||||
|
|
||||||
public static class AuthorizationConsentId implements Serializable {
|
public static class AuthorizationConsentId implements Serializable {
|
||||||
private String registeredClientId;
|
private String registeredClientId;
|
||||||
private String principalName;
|
private String principalName;
|
||||||
|
|
||||||
// getters and setters
|
// @fold:on
|
||||||
// end::class[]
|
|
||||||
public String getRegisteredClientId() {
|
public String getRegisteredClientId() {
|
||||||
return registeredClientId;
|
return registeredClientId;
|
||||||
}
|
}
|
||||||
@@ -85,10 +79,8 @@ public class AuthorizationConsent {
|
|||||||
public void setPrincipalName(String principalName) {
|
public void setPrincipalName(String principalName) {
|
||||||
this.principalName = principalName;
|
this.principalName = principalName;
|
||||||
}
|
}
|
||||||
// tag::class[]
|
// @fold:off
|
||||||
|
|
||||||
// equals and hashCode
|
|
||||||
// end::class[]
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
@@ -101,7 +93,5 @@ public class AuthorizationConsent {
|
|||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(registeredClientId, principalName);
|
return Objects.hash(registeredClientId, principalName);
|
||||||
}
|
}
|
||||||
// tag::class[]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// end::class[]
|
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package sample.jpa;
|
package sample.jpa.entity.client;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
|
||||||
@@ -21,10 +21,6 @@ import javax.persistence.Column;
|
|||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Steve Riesenberg
|
|
||||||
*/
|
|
||||||
// tag::class[]
|
|
||||||
@Entity
|
@Entity
|
||||||
public class Client {
|
public class Client {
|
||||||
@Id
|
@Id
|
||||||
@@ -47,8 +43,7 @@ public class Client {
|
|||||||
@Column(length = 2000)
|
@Column(length = 2000)
|
||||||
private String tokenSettings;
|
private String tokenSettings;
|
||||||
|
|
||||||
// getters and setters
|
// @fold:on
|
||||||
// end::class[]
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
@@ -144,6 +139,5 @@ public class Client {
|
|||||||
public void setTokenSettings(String tokenSettings) {
|
public void setTokenSettings(String tokenSettings) {
|
||||||
this.tokenSettings = tokenSettings;
|
this.tokenSettings = tokenSettings;
|
||||||
}
|
}
|
||||||
// tag::class[]
|
// @fold:off
|
||||||
}
|
}
|
||||||
// end::class[]
|
|
||||||
@@ -13,19 +13,17 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package sample.jpa;
|
package sample.jpa.repository.authorization;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import sample.jpa.entity.authorization.Authorization;
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.data.repository.query.Param;
|
import org.springframework.data.repository.query.Param;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Steve Riesenberg
|
|
||||||
*/
|
|
||||||
// tag::class[]
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface AuthorizationRepository extends JpaRepository<Authorization, String> {
|
public interface AuthorizationRepository extends JpaRepository<Authorization, String> {
|
||||||
Optional<Authorization> findByState(String state);
|
Optional<Authorization> findByState(String state);
|
||||||
@@ -39,4 +37,3 @@ public interface AuthorizationRepository extends JpaRepository<Authorization, St
|
|||||||
)
|
)
|
||||||
Optional<Authorization> findByStateOrAuthorizationCodeValueOrAccessTokenValueOrRefreshTokenValue(@Param("token") String token);
|
Optional<Authorization> findByStateOrAuthorizationCodeValueOrAccessTokenValueOrRefreshTokenValue(@Param("token") String token);
|
||||||
}
|
}
|
||||||
// end::class[]
|
|
||||||
@@ -13,20 +13,17 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package sample.jpa;
|
package sample.jpa.repository.authorizationConsent;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import sample.jpa.entity.authorizationConsent.AuthorizationConsent;
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Steve Riesenberg
|
|
||||||
*/
|
|
||||||
// tag::class[]
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface AuthorizationConsentRepository extends JpaRepository<AuthorizationConsent, AuthorizationConsent.AuthorizationConsentId> {
|
public interface AuthorizationConsentRepository extends JpaRepository<AuthorizationConsent, AuthorizationConsent.AuthorizationConsentId> {
|
||||||
Optional<AuthorizationConsent> findByRegisteredClientIdAndPrincipalName(String registeredClientId, String principalName);
|
Optional<AuthorizationConsent> findByRegisteredClientIdAndPrincipalName(String registeredClientId, String principalName);
|
||||||
void deleteByRegisteredClientIdAndPrincipalName(String registeredClientId, String principalName);
|
void deleteByRegisteredClientIdAndPrincipalName(String registeredClientId, String principalName);
|
||||||
}
|
}
|
||||||
// end::class[]
|
|
||||||
@@ -13,19 +13,16 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package sample.jpa;
|
package sample.jpa.repository.client;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import sample.jpa.entity.client.Client;
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Steve Riesenberg
|
|
||||||
*/
|
|
||||||
// tag::class[]
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface ClientRepository extends JpaRepository<Client, String> {
|
public interface ClientRepository extends JpaRepository<Client, String> {
|
||||||
Optional<Client> findByClientId(String clientId);
|
Optional<Client> findByClientId(String clientId);
|
||||||
}
|
}
|
||||||
// end::class[]
|
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package sample.jpa;
|
package sample.jpa.service.authorization;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -24,6 +24,8 @@ import java.util.function.Consumer;
|
|||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import com.fasterxml.jackson.databind.Module;
|
import com.fasterxml.jackson.databind.Module;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
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.dao.DataRetrievalFailureException;
|
||||||
import org.springframework.security.jackson2.SecurityJackson2Modules;
|
import org.springframework.security.jackson2.SecurityJackson2Modules;
|
||||||
@@ -44,10 +46,6 @@ import org.springframework.stereotype.Component;
|
|||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Steve Riesenberg
|
|
||||||
*/
|
|
||||||
// tag::class[]
|
|
||||||
@Component
|
@Component
|
||||||
public class JpaOAuth2AuthorizationService implements OAuth2AuthorizationService {
|
public class JpaOAuth2AuthorizationService implements OAuth2AuthorizationService {
|
||||||
private final AuthorizationRepository authorizationRepository;
|
private final AuthorizationRepository authorizationRepository;
|
||||||
@@ -261,4 +259,3 @@ public class JpaOAuth2AuthorizationService implements OAuth2AuthorizationService
|
|||||||
return new AuthorizationGrantType(authorizationGrantType); // Custom authorization grant type
|
return new AuthorizationGrantType(authorizationGrantType); // Custom authorization grant type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// end::class[]
|
|
||||||
@@ -13,11 +13,14 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package sample.jpa;
|
package sample.jpa.service.authorizationConsent;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import sample.jpa.entity.authorizationConsent.AuthorizationConsent;
|
||||||
|
import sample.jpa.repository.authorizationConsent.AuthorizationConsentRepository;
|
||||||
|
|
||||||
import org.springframework.dao.DataRetrievalFailureException;
|
import org.springframework.dao.DataRetrievalFailureException;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
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.Assert;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Steve Riesenberg
|
|
||||||
*/
|
|
||||||
// tag::class[]
|
|
||||||
@Component
|
@Component
|
||||||
public class JpaOAuth2AuthorizationConsentService implements OAuth2AuthorizationConsentService {
|
public class JpaOAuth2AuthorizationConsentService implements OAuth2AuthorizationConsentService {
|
||||||
private final AuthorizationConsentRepository authorizationConsentRepository;
|
private final AuthorizationConsentRepository authorizationConsentRepository;
|
||||||
@@ -99,4 +98,3 @@ public class JpaOAuth2AuthorizationConsentService implements OAuth2Authorization
|
|||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// end::class[]
|
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package sample.jpa;
|
package sample.jpa.service.client;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -23,6 +23,8 @@ import java.util.Set;
|
|||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import com.fasterxml.jackson.databind.Module;
|
import com.fasterxml.jackson.databind.Module;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
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.jackson2.SecurityJackson2Modules;
|
||||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
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.Assert;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Steve Riesenberg
|
|
||||||
*/
|
|
||||||
// tag::class[]
|
|
||||||
@Component
|
@Component
|
||||||
public class JpaRegisteredClientRepository implements RegisteredClientRepository {
|
public class JpaRegisteredClientRepository implements RegisteredClientRepository {
|
||||||
private final ClientRepository clientRepository;
|
private final ClientRepository clientRepository;
|
||||||
@@ -172,4 +170,3 @@ public class JpaRegisteredClientRepository implements RegisteredClientRepository
|
|||||||
return new ClientAuthenticationMethod(clientAuthenticationMethod); // Custom client authentication method
|
return new ClientAuthenticationMethod(clientAuthenticationMethod); // Custom client authentication method
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// end::class[]
|
|
||||||
@@ -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)
|
|
||||||
);
|
|
||||||
@@ -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)
|
|
||||||
);
|
|
||||||
@@ -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)
|
|
||||||
);
|
|
||||||
@@ -26,6 +26,9 @@ import org.junit.jupiter.api.Test;
|
|||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import sample.AuthorizationCodeGrantFlow;
|
import sample.AuthorizationCodeGrantFlow;
|
||||||
import sample.jose.TestJwks;
|
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.SpringTestContext;
|
||||||
import sample.test.SpringTestContextExtension;
|
import sample.test.SpringTestContextExtension;
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
= How-to: Implement core services with JPA
|
= How-to: Implement core services with JPA
|
||||||
:index-link: ../how-to.html
|
:index-link: ../how-to.html
|
||||||
:docs-dir: ..
|
: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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
You are encouraged to experiment and test before deploying to production.
|
||||||
|
|
||||||
@@ -35,7 +35,21 @@ The following listing shows the `client` schema.
|
|||||||
.Client Schema
|
.Client Schema
|
||||||
[source,sql]
|
[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]]
|
[[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 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.
|
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.
|
You may need to normalize or heavily alter the following schema for your needs.
|
||||||
|
|
||||||
The following listing shows the `authorization` schema.
|
The following listing shows the `authorization` schema.
|
||||||
@@ -52,7 +67,34 @@ The following listing shows the `authorization` schema.
|
|||||||
.Authorization Schema
|
.Authorization Schema
|
||||||
[source,sql]
|
[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]]
|
[[authorization-consent-schema]]
|
||||||
@@ -64,7 +106,12 @@ The following listing shows the `authorizationConsent` schema.
|
|||||||
.Authorization Consent Schema
|
.Authorization Consent Schema
|
||||||
[source,sql]
|
[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]]
|
[[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.
|
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.
|
They allow the schema to be created dynamically and therefore do not require the above sql scripts to be executed manually.
|
||||||
|
|
||||||
* <<client-entity>>
|
* <<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.
|
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
|
.Client Entity
|
||||||
[source,java]
|
include::code:Client[]
|
||||||
----
|
|
||||||
include::{examples-dir}/src/main/java/sample/jpa/Client.java[tag=class]
|
|
||||||
----
|
|
||||||
|
|
||||||
[[authorization-entity]]
|
[[authorization-entity]]
|
||||||
=== 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.
|
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
|
.Authorization Entity
|
||||||
[source,java]
|
include::code:Authorization[]
|
||||||
----
|
|
||||||
include::{examples-dir}/src/main/java/sample/jpa/Authorization.java[tag=class]
|
|
||||||
----
|
|
||||||
|
|
||||||
[[authorization-consent-entity]]
|
[[authorization-consent-entity]]
|
||||||
=== 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.
|
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
|
.Authorization Consent Entity
|
||||||
[source,java]
|
include::code:AuthorizationConsent[]
|
||||||
----
|
|
||||||
include::{examples-dir}/src/main/java/sample/jpa/AuthorizationConsent.java[tag=class]
|
|
||||||
----
|
|
||||||
|
|
||||||
[[create-spring-data-repositories]]
|
[[create-spring-data-repositories]]
|
||||||
== 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.
|
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
|
.Client Repository
|
||||||
[source,java]
|
include::code:ClientRepository[]
|
||||||
----
|
|
||||||
include::{examples-dir}/src/main/java/sample/jpa/ClientRepository.java[tag=class]
|
|
||||||
----
|
|
||||||
|
|
||||||
[[authorization-repository]]
|
[[authorization-repository]]
|
||||||
=== 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.
|
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.
|
It also allows querying a combination of token fields.
|
||||||
|
|
||||||
|
[[sample.jpa.repository.authorization]]
|
||||||
.Authorization Repository
|
.Authorization Repository
|
||||||
[source,java]
|
include::code:AuthorizationRepository[]
|
||||||
----
|
|
||||||
include::{examples-dir}/src/main/java/sample/jpa/AuthorizationRepository.java[tag=class]
|
|
||||||
----
|
|
||||||
|
|
||||||
[[authorization-consent-repository]]
|
[[authorization-consent-repository]]
|
||||||
=== 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.
|
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
|
.Authorization Consent Repository
|
||||||
[source,java]
|
include::code:AuthorizationConsentRepository[]
|
||||||
----
|
|
||||||
include::{examples-dir}/src/main/java/sample/jpa/AuthorizationConsentRepository.java[tag=class]
|
|
||||||
----
|
|
||||||
|
|
||||||
[[implement-core-services]]
|
[[implement-core-services]]
|
||||||
== 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.
|
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.
|
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.
|
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>>
|
* <<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.
|
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
|
.`RegisteredClientRepository` Implementation
|
||||||
[source,java]
|
include::code:JpaRegisteredClientRepository[]
|
||||||
----
|
|
||||||
include::{examples-dir}/src/main/java/sample/jpa/JpaRegisteredClientRepository.java[tag=class]
|
|
||||||
----
|
|
||||||
|
|
||||||
[[authorization-service]]
|
[[authorization-service]]
|
||||||
=== 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.
|
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
|
.`OAuth2AuthorizationService` Implementation
|
||||||
[source,java]
|
include::code:JpaOAuth2AuthorizationService[]
|
||||||
----
|
|
||||||
include::{examples-dir}/src/main/java/sample/jpa/JpaOAuth2AuthorizationService.java[tag=class]
|
|
||||||
----
|
|
||||||
|
|
||||||
[[authorization-consent-service]]
|
[[authorization-consent-service]]
|
||||||
=== 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.
|
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
|
.`OAuth2AuthorizationConsentService` Implementation
|
||||||
[source,java]
|
include::code:JpaOAuth2AuthorizationConsentService[]
|
||||||
----
|
|
||||||
include::{examples-dir}/src/main/java/sample/jpa/JpaOAuth2AuthorizationConsentService.java[tag=class]
|
|
||||||
----
|
|
||||||
|
|||||||
Reference in New Issue
Block a user