-
-
Save g2px1/5bffa023e9d792c5ec8a29ec166a5e54 to your computer and use it in GitHub Desktop.
JpaRegisteredClientRepository
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2020-2021 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. | |
*/ | |
import java.time.Instant; | |
import javax.persistence.Column; | |
import javax.persistence.Entity; | |
import javax.persistence.Id; | |
/** | |
* @author Steve Riesenberg | |
*/ | |
@Entity | |
public class Client { | |
@Id | |
private String id; | |
private String clientId; | |
private Instant clientIdIssuedAt; | |
private String clientSecret; | |
private Instant clientSecretExpiresAt; | |
private String clientName; | |
@Column(length = 1000) | |
private String clientAuthenticationMethods; | |
@Column(length = 1000) | |
private String authorizationGrantTypes; | |
@Column(length = 1000) | |
private String redirectUris; | |
@Column(length = 1000) | |
private String scopes; | |
@Column(length = 2000) | |
private String clientSettings; | |
@Column(length = 2000) | |
private String tokenSettings; | |
public String getId() { | |
return id; | |
} | |
public void setId(String id) { | |
this.id = id; | |
} | |
public String getClientId() { | |
return clientId; | |
} | |
public void setClientId(String clientId) { | |
this.clientId = clientId; | |
} | |
public Instant getClientIdIssuedAt() { | |
return clientIdIssuedAt; | |
} | |
public void setClientIdIssuedAt(Instant clientIdIssuedAt) { | |
this.clientIdIssuedAt = clientIdIssuedAt; | |
} | |
public String getClientSecret() { | |
return clientSecret; | |
} | |
public void setClientSecret(String clientSecret) { | |
this.clientSecret = clientSecret; | |
} | |
public Instant getClientSecretExpiresAt() { | |
return clientSecretExpiresAt; | |
} | |
public void setClientSecretExpiresAt(Instant clientSecretExpiresAt) { | |
this.clientSecretExpiresAt = clientSecretExpiresAt; | |
} | |
public String getClientName() { | |
return clientName; | |
} | |
public void setClientName(String clientName) { | |
this.clientName = clientName; | |
} | |
public String getClientAuthenticationMethods() { | |
return clientAuthenticationMethods; | |
} | |
public void setClientAuthenticationMethods(String clientAuthenticationMethods) { | |
this.clientAuthenticationMethods = clientAuthenticationMethods; | |
} | |
public String getAuthorizationGrantTypes() { | |
return authorizationGrantTypes; | |
} | |
public void setAuthorizationGrantTypes(String authorizationGrantTypes) { | |
this.authorizationGrantTypes = authorizationGrantTypes; | |
} | |
public String getRedirectUris() { | |
return redirectUris; | |
} | |
public void setRedirectUris(String redirectUris) { | |
this.redirectUris = redirectUris; | |
} | |
public String getScopes() { | |
return scopes; | |
} | |
public void setScopes(String scopes) { | |
this.scopes = scopes; | |
} | |
public String getClientSettings() { | |
return clientSettings; | |
} | |
public void setClientSettings(String clientSettings) { | |
this.clientSettings = clientSettings; | |
} | |
public String getTokenSettings() { | |
return tokenSettings; | |
} | |
public void setTokenSettings(String tokenSettings) { | |
this.tokenSettings = tokenSettings; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2020-2021 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. | |
*/ | |
import java.util.Optional; | |
import org.springframework.data.jpa.repository.JpaRepository; | |
import org.springframework.stereotype.Repository; | |
/** | |
* @author Steve Riesenberg | |
*/ | |
@Repository | |
public interface ClientRepository extends JpaRepository<Client, String> { | |
Optional<Client> findByClientId(String clientId); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2020-2021 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. | |
*/ | |
import java.util.ArrayList; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Set; | |
import com.fasterxml.jackson.core.type.TypeReference; | |
import com.fasterxml.jackson.databind.Module; | |
import com.fasterxml.jackson.databind.ObjectMapper; | |
import org.springframework.security.crypto.password.PasswordEncoder; | |
import org.springframework.security.jackson2.SecurityJackson2Modules; | |
import org.springframework.security.oauth2.core.AuthorizationGrantType; | |
import org.springframework.security.oauth2.core.ClientAuthenticationMethod; | |
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; | |
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository; | |
import org.springframework.security.oauth2.server.authorization.config.ClientSettings; | |
import org.springframework.security.oauth2.server.authorization.config.TokenSettings; | |
import org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module; | |
import org.springframework.stereotype.Component; | |
import org.springframework.util.Assert; | |
import org.springframework.util.StringUtils; | |
/** | |
* @author Steve Riesenberg | |
*/ | |
@Component | |
public class JpaRegisteredClientRepository implements RegisteredClientRepository { | |
private final ClientRepository clientRepository; | |
private final PasswordEncoder passwordEncoder; | |
private final ObjectMapper objectMapper = new ObjectMapper(); | |
public JpaRegisteredClientRepository(ClientRepository clientRepository, PasswordEncoder passwordEncoder) { | |
Assert.notNull(clientRepository, "clientRepository cannot be null"); | |
Assert.notNull(passwordEncoder, "passwordEncoder cannot be null"); | |
this.clientRepository = clientRepository; | |
this.passwordEncoder = passwordEncoder; | |
ClassLoader classLoader = JpaOAuth2AuthorizationService.class.getClassLoader(); | |
List<Module> securityModules = SecurityJackson2Modules.getModules(classLoader); | |
this.objectMapper.registerModules(securityModules); | |
this.objectMapper.registerModule(new OAuth2AuthorizationServerJackson2Module()); | |
} | |
@Override | |
public void save(RegisteredClient registeredClient) { | |
Assert.notNull(registeredClient, "registeredClient cannot be null"); | |
this.clientRepository.save(toEntity(registeredClient)); | |
} | |
@Override | |
public RegisteredClient findById(String id) { | |
Assert.hasText(id, "id cannot be empty"); | |
return this.clientRepository.findById(id).map(this::toObject).orElse(null); | |
} | |
@Override | |
public RegisteredClient findByClientId(String clientId) { | |
Assert.hasText(clientId, "clientId cannot be empty"); | |
return this.clientRepository.findByClientId(clientId).map(this::toObject).orElse(null); | |
} | |
private RegisteredClient toObject(Client client) { | |
Set<String> clientAuthenticationMethods = StringUtils.commaDelimitedListToSet( | |
client.getClientAuthenticationMethods()); | |
Set<String> authorizationGrantTypes = StringUtils.commaDelimitedListToSet( | |
client.getAuthorizationGrantTypes()); | |
Set<String> redirectUris = StringUtils.commaDelimitedListToSet( | |
client.getRedirectUris()); | |
Set<String> clientScopes = StringUtils.commaDelimitedListToSet( | |
client.getScopes()); | |
RegisteredClient.Builder builder = RegisteredClient.withId(client.getId()) | |
.clientId(client.getClientId()) | |
.clientIdIssuedAt(client.getClientIdIssuedAt()) | |
.clientSecret(client.getClientSecret()) | |
.clientSecretExpiresAt(client.getClientSecretExpiresAt()) | |
.clientName(client.getClientName()) | |
.clientAuthenticationMethods(authenticationMethods -> | |
clientAuthenticationMethods.forEach(authenticationMethod -> | |
authenticationMethods.add(resolveClientAuthenticationMethod(authenticationMethod)))) | |
.authorizationGrantTypes((grantTypes) -> | |
authorizationGrantTypes.forEach(grantType -> | |
grantTypes.add(resolveAuthorizationGrantType(grantType)))) | |
.redirectUris((uris) -> uris.addAll(redirectUris)) | |
.scopes((scopes) -> scopes.addAll(clientScopes)); | |
Map<String, Object> clientSettingsMap = parseMap(client.getClientSettings()); | |
builder.clientSettings(ClientSettings.withSettings(clientSettingsMap).build()); | |
Map<String, Object> tokenSettingsMap = parseMap(client.getTokenSettings()); | |
builder.tokenSettings(TokenSettings.withSettings(tokenSettingsMap).build()); | |
return builder.build(); | |
} | |
private Client toEntity(RegisteredClient registeredClient) { | |
List<String> clientAuthenticationMethods = new ArrayList<>(registeredClient.getClientAuthenticationMethods().size()); | |
registeredClient.getClientAuthenticationMethods().forEach(clientAuthenticationMethod -> | |
clientAuthenticationMethods.add(clientAuthenticationMethod.getValue())); | |
List<String> authorizationGrantTypes = new ArrayList<>(registeredClient.getAuthorizationGrantTypes().size()); | |
registeredClient.getAuthorizationGrantTypes().forEach(authorizationGrantType -> | |
authorizationGrantTypes.add(authorizationGrantType.getValue())); | |
Client entity = new Client(); | |
entity.setId(registeredClient.getId()); | |
entity.setClientId(registeredClient.getClientId()); | |
entity.setClientIdIssuedAt(registeredClient.getClientIdIssuedAt()); | |
entity.setClientSecret(encode(registeredClient.getClientSecret())); | |
entity.setClientSecretExpiresAt(registeredClient.getClientSecretExpiresAt()); | |
entity.setClientName(registeredClient.getClientName()); | |
entity.setClientAuthenticationMethods(StringUtils.collectionToCommaDelimitedString(clientAuthenticationMethods)); | |
entity.setAuthorizationGrantTypes(StringUtils.collectionToCommaDelimitedString(authorizationGrantTypes)); | |
entity.setRedirectUris(StringUtils.collectionToCommaDelimitedString(registeredClient.getRedirectUris())); | |
entity.setScopes(StringUtils.collectionToCommaDelimitedString(registeredClient.getScopes())); | |
entity.setClientSettings(writeMap(registeredClient.getClientSettings().getSettings())); | |
entity.setTokenSettings(writeMap(registeredClient.getTokenSettings().getSettings())); | |
return entity; | |
} | |
private Map<String, Object> parseMap(String data) { | |
try { | |
return this.objectMapper.readValue(data, new TypeReference<>() { | |
}); | |
} catch (Exception ex) { | |
throw new IllegalArgumentException(ex.getMessage(), ex); | |
} | |
} | |
private String writeMap(Map<String, Object> data) { | |
try { | |
return this.objectMapper.writeValueAsString(data); | |
} catch (Exception ex) { | |
throw new IllegalArgumentException(ex.getMessage(), ex); | |
} | |
} | |
private String encode(String value) { | |
if (value != null) { | |
return this.passwordEncoder.encode(value); | |
} | |
return null; | |
} | |
private static AuthorizationGrantType resolveAuthorizationGrantType(String authorizationGrantType) { | |
if (AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equals(authorizationGrantType)) { | |
return AuthorizationGrantType.AUTHORIZATION_CODE; | |
} else if (AuthorizationGrantType.CLIENT_CREDENTIALS.getValue().equals(authorizationGrantType)) { | |
return AuthorizationGrantType.CLIENT_CREDENTIALS; | |
} else if (AuthorizationGrantType.REFRESH_TOKEN.getValue().equals(authorizationGrantType)) { | |
return AuthorizationGrantType.REFRESH_TOKEN; | |
} | |
return new AuthorizationGrantType(authorizationGrantType); // Custom authorization grant type | |
} | |
private static ClientAuthenticationMethod resolveClientAuthenticationMethod(String clientAuthenticationMethod) { | |
if (ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue().equals(clientAuthenticationMethod)) { | |
return ClientAuthenticationMethod.CLIENT_SECRET_BASIC; | |
} else if (ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue().equals(clientAuthenticationMethod)) { | |
return ClientAuthenticationMethod.CLIENT_SECRET_POST; | |
} else if (ClientAuthenticationMethod.NONE.getValue().equals(clientAuthenticationMethod)) { | |
return ClientAuthenticationMethod.NONE; | |
} | |
return new ClientAuthenticationMethod(clientAuthenticationMethod); // Custom client authentication method | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment