Use LobHandler in JdbcOAuth2AuthorizedClientService

LobHandler provides an abstraction for handling large binary fields and large text
fields in specific databases, no matter if represented as simple types or
Large OBjects.

Its use provides compatibility with many databases eliminating the need
for custom OAuth2AuthorizedClientParametersMapper and
OAuth2AuthorizedClientRowMapper implementations.

Closes gh-9070
This commit is contained in:
Craig Andrews 2020-10-01 14:29:51 -04:00 committed by Joe Grandja
parent 6714112961
commit 05dc326389
1 changed files with 74 additions and 7 deletions

View File

@ -17,6 +17,7 @@
package org.springframework.security.oauth2.client;
import java.nio.charset.StandardCharsets;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
@ -35,6 +36,9 @@ import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.SqlParameterValue;
import org.springframework.jdbc.support.lob.DefaultLobHandler;
import org.springframework.jdbc.support.lob.LobCreator;
import org.springframework.jdbc.support.lob.LobHandler;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
@ -56,6 +60,7 @@ import org.springframework.util.StringUtils;
*
* @author Joe Grandja
* @author Stav Shamir
* @author Craig Andrews
* @since 5.3
* @see OAuth2AuthorizedClientService
* @see OAuth2AuthorizedClient
@ -107,6 +112,8 @@ public class JdbcOAuth2AuthorizedClientService implements OAuth2AuthorizedClient
protected Function<OAuth2AuthorizedClientHolder, List<SqlParameterValue>> authorizedClientParametersMapper;
protected final LobHandler lobHandler;
/**
* Constructs a {@code JdbcOAuth2AuthorizedClientService} using the provided
* parameters.
@ -115,10 +122,28 @@ public class JdbcOAuth2AuthorizedClientService implements OAuth2AuthorizedClient
*/
public JdbcOAuth2AuthorizedClientService(JdbcOperations jdbcOperations,
ClientRegistrationRepository clientRegistrationRepository) {
this(jdbcOperations, clientRegistrationRepository, new DefaultLobHandler());
}
/**
* Constructs a {@code JdbcOAuth2AuthorizedClientService} using the provided
* parameters.
* @param jdbcOperations the JDBC operations
* @param clientRegistrationRepository the repository of client registrations
* @param lobHandler the handler for large binary fields and large text fields
* @since 5.5
*/
public JdbcOAuth2AuthorizedClientService(JdbcOperations jdbcOperations,
ClientRegistrationRepository clientRegistrationRepository, LobHandler lobHandler) {
Assert.notNull(jdbcOperations, "jdbcOperations cannot be null");
Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
Assert.notNull(lobHandler, "lobHandler cannot be null");
this.jdbcOperations = jdbcOperations;
this.authorizedClientRowMapper = new OAuth2AuthorizedClientRowMapper(clientRegistrationRepository);
this.lobHandler = lobHandler;
OAuth2AuthorizedClientRowMapper authorizedClientRowMapper = new OAuth2AuthorizedClientRowMapper(
clientRegistrationRepository);
authorizedClientRowMapper.setLobHandler(lobHandler);
this.authorizedClientRowMapper = authorizedClientRowMapper;
this.authorizedClientParametersMapper = new OAuth2AuthorizedClientParametersMapper();
}
@ -163,15 +188,21 @@ public class JdbcOAuth2AuthorizedClientService implements OAuth2AuthorizedClient
SqlParameterValue principalNameParameter = parameters.remove(0);
parameters.add(clientRegistrationIdParameter);
parameters.add(principalNameParameter);
PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters.toArray());
this.jdbcOperations.update(UPDATE_AUTHORIZED_CLIENT_SQL, pss);
try (LobCreator lobCreator = this.lobHandler.getLobCreator()) {
PreparedStatementSetter pss = new LobCreatorArgumentPreparedStatementSetter(lobCreator,
parameters.toArray());
this.jdbcOperations.update(UPDATE_AUTHORIZED_CLIENT_SQL, pss);
}
}
private void insertAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal) {
List<SqlParameterValue> parameters = this.authorizedClientParametersMapper
.apply(new OAuth2AuthorizedClientHolder(authorizedClient, principal));
PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters.toArray());
this.jdbcOperations.update(SAVE_AUTHORIZED_CLIENT_SQL, pss);
try (LobCreator lobCreator = this.lobHandler.getLobCreator()) {
PreparedStatementSetter pss = new LobCreatorArgumentPreparedStatementSetter(lobCreator,
parameters.toArray());
this.jdbcOperations.update(SAVE_AUTHORIZED_CLIENT_SQL, pss);
}
}
@Override
@ -218,11 +249,18 @@ public class JdbcOAuth2AuthorizedClientService implements OAuth2AuthorizedClient
protected final ClientRegistrationRepository clientRegistrationRepository;
protected LobHandler lobHandler = new DefaultLobHandler();
public OAuth2AuthorizedClientRowMapper(ClientRegistrationRepository clientRegistrationRepository) {
Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
this.clientRegistrationRepository = clientRegistrationRepository;
}
public final void setLobHandler(LobHandler lobHandler) {
Assert.notNull(lobHandler, "lobHandler cannot be null");
this.lobHandler = lobHandler;
}
@Override
public OAuth2AuthorizedClient mapRow(ResultSet rs, int rowNum) throws SQLException {
String clientRegistrationId = rs.getString("client_registration_id");
@ -237,7 +275,8 @@ public class JdbcOAuth2AuthorizedClientService implements OAuth2AuthorizedClient
if (OAuth2AccessToken.TokenType.BEARER.getValue().equalsIgnoreCase(rs.getString("access_token_type"))) {
tokenType = OAuth2AccessToken.TokenType.BEARER;
}
String tokenValue = new String(rs.getBytes("access_token_value"), StandardCharsets.UTF_8);
String tokenValue = new String(this.lobHandler.getBlobAsBytes(rs, "access_token_value"),
StandardCharsets.UTF_8);
Instant issuedAt = rs.getTimestamp("access_token_issued_at").toInstant();
Instant expiresAt = rs.getTimestamp("access_token_expires_at").toInstant();
Set<String> scopes = Collections.emptySet();
@ -247,7 +286,7 @@ public class JdbcOAuth2AuthorizedClientService implements OAuth2AuthorizedClient
}
OAuth2AccessToken accessToken = new OAuth2AccessToken(tokenType, tokenValue, issuedAt, expiresAt, scopes);
OAuth2RefreshToken refreshToken = null;
byte[] refreshTokenValue = rs.getBytes("refresh_token_value");
byte[] refreshTokenValue = this.lobHandler.getBlobAsBytes(rs, "refresh_token_value");
if (refreshTokenValue != null) {
tokenValue = new String(refreshTokenValue, StandardCharsets.UTF_8);
issuedAt = null;
@ -346,4 +385,32 @@ public class JdbcOAuth2AuthorizedClientService implements OAuth2AuthorizedClient
}
private static final class LobCreatorArgumentPreparedStatementSetter extends ArgumentPreparedStatementSetter {
protected final LobCreator lobCreator;
private LobCreatorArgumentPreparedStatementSetter(LobCreator lobCreator, Object[] args) {
super(args);
this.lobCreator = lobCreator;
}
@Override
protected void doSetValue(PreparedStatement ps, int parameterPosition, Object argValue) throws SQLException {
if (argValue instanceof SqlParameterValue) {
SqlParameterValue paramValue = (SqlParameterValue) argValue;
if (paramValue.getSqlType() == Types.BLOB) {
if (paramValue.getValue() != null) {
Assert.isInstanceOf(byte[].class, paramValue.getValue(),
"Value of blob parameter must be byte[]");
}
byte[] valueBytes = (byte[]) paramValue.getValue();
this.lobCreator.setBlobAsBytes(ps, parameterPosition, valueBytes);
return;
}
}
super.doSetValue(ps, parameterPosition, argValue);
}
}
}