Browse Source

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
Craig Andrews 4 years ago
parent
commit
05dc326389

+ 74 - 7
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/JdbcOAuth2AuthorizedClientService.java

@@ -17,6 +17,7 @@
 package org.springframework.security.oauth2.client;
 package org.springframework.security.oauth2.client;
 
 
 import java.nio.charset.StandardCharsets;
 import java.nio.charset.StandardCharsets;
+import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.SQLException;
 import java.sql.Timestamp;
 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.PreparedStatementSetter;
 import org.springframework.jdbc.core.RowMapper;
 import org.springframework.jdbc.core.RowMapper;
 import org.springframework.jdbc.core.SqlParameterValue;
 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.core.Authentication;
 import org.springframework.security.oauth2.client.registration.ClientRegistration;
 import org.springframework.security.oauth2.client.registration.ClientRegistration;
 import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
 import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
@@ -56,6 +60,7 @@ import org.springframework.util.StringUtils;
  *
  *
  * @author Joe Grandja
  * @author Joe Grandja
  * @author Stav Shamir
  * @author Stav Shamir
+ * @author Craig Andrews
  * @since 5.3
  * @since 5.3
  * @see OAuth2AuthorizedClientService
  * @see OAuth2AuthorizedClientService
  * @see OAuth2AuthorizedClient
  * @see OAuth2AuthorizedClient
@@ -107,6 +112,8 @@ public class JdbcOAuth2AuthorizedClientService implements OAuth2AuthorizedClient
 
 
 	protected Function<OAuth2AuthorizedClientHolder, List<SqlParameterValue>> authorizedClientParametersMapper;
 	protected Function<OAuth2AuthorizedClientHolder, List<SqlParameterValue>> authorizedClientParametersMapper;
 
 
+	protected final LobHandler lobHandler;
+
 	/**
 	/**
 	 * Constructs a {@code JdbcOAuth2AuthorizedClientService} using the provided
 	 * Constructs a {@code JdbcOAuth2AuthorizedClientService} using the provided
 	 * parameters.
 	 * parameters.
@@ -115,10 +122,28 @@ public class JdbcOAuth2AuthorizedClientService implements OAuth2AuthorizedClient
 	 */
 	 */
 	public JdbcOAuth2AuthorizedClientService(JdbcOperations jdbcOperations,
 	public JdbcOAuth2AuthorizedClientService(JdbcOperations jdbcOperations,
 			ClientRegistrationRepository clientRegistrationRepository) {
 			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(jdbcOperations, "jdbcOperations cannot be null");
 		Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
 		Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
+		Assert.notNull(lobHandler, "lobHandler cannot be null");
 		this.jdbcOperations = jdbcOperations;
 		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();
 		this.authorizedClientParametersMapper = new OAuth2AuthorizedClientParametersMapper();
 	}
 	}
 
 
@@ -163,15 +188,21 @@ public class JdbcOAuth2AuthorizedClientService implements OAuth2AuthorizedClient
 		SqlParameterValue principalNameParameter = parameters.remove(0);
 		SqlParameterValue principalNameParameter = parameters.remove(0);
 		parameters.add(clientRegistrationIdParameter);
 		parameters.add(clientRegistrationIdParameter);
 		parameters.add(principalNameParameter);
 		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) {
 	private void insertAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal) {
 		List<SqlParameterValue> parameters = this.authorizedClientParametersMapper
 		List<SqlParameterValue> parameters = this.authorizedClientParametersMapper
 				.apply(new OAuth2AuthorizedClientHolder(authorizedClient, principal));
 				.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
 	@Override
@@ -218,11 +249,18 @@ public class JdbcOAuth2AuthorizedClientService implements OAuth2AuthorizedClient
 
 
 		protected final ClientRegistrationRepository clientRegistrationRepository;
 		protected final ClientRegistrationRepository clientRegistrationRepository;
 
 
+		protected LobHandler lobHandler = new DefaultLobHandler();
+
 		public OAuth2AuthorizedClientRowMapper(ClientRegistrationRepository clientRegistrationRepository) {
 		public OAuth2AuthorizedClientRowMapper(ClientRegistrationRepository clientRegistrationRepository) {
 			Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
 			Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
 			this.clientRegistrationRepository = clientRegistrationRepository;
 			this.clientRegistrationRepository = clientRegistrationRepository;
 		}
 		}
 
 
+		public final void setLobHandler(LobHandler lobHandler) {
+			Assert.notNull(lobHandler, "lobHandler cannot be null");
+			this.lobHandler = lobHandler;
+		}
+
 		@Override
 		@Override
 		public OAuth2AuthorizedClient mapRow(ResultSet rs, int rowNum) throws SQLException {
 		public OAuth2AuthorizedClient mapRow(ResultSet rs, int rowNum) throws SQLException {
 			String clientRegistrationId = rs.getString("client_registration_id");
 			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"))) {
 			if (OAuth2AccessToken.TokenType.BEARER.getValue().equalsIgnoreCase(rs.getString("access_token_type"))) {
 				tokenType = OAuth2AccessToken.TokenType.BEARER;
 				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 issuedAt = rs.getTimestamp("access_token_issued_at").toInstant();
 			Instant expiresAt = rs.getTimestamp("access_token_expires_at").toInstant();
 			Instant expiresAt = rs.getTimestamp("access_token_expires_at").toInstant();
 			Set<String> scopes = Collections.emptySet();
 			Set<String> scopes = Collections.emptySet();
@@ -247,7 +286,7 @@ public class JdbcOAuth2AuthorizedClientService implements OAuth2AuthorizedClient
 			}
 			}
 			OAuth2AccessToken accessToken = new OAuth2AccessToken(tokenType, tokenValue, issuedAt, expiresAt, scopes);
 			OAuth2AccessToken accessToken = new OAuth2AccessToken(tokenType, tokenValue, issuedAt, expiresAt, scopes);
 			OAuth2RefreshToken refreshToken = null;
 			OAuth2RefreshToken refreshToken = null;
-			byte[] refreshTokenValue = rs.getBytes("refresh_token_value");
+			byte[] refreshTokenValue = this.lobHandler.getBlobAsBytes(rs, "refresh_token_value");
 			if (refreshTokenValue != null) {
 			if (refreshTokenValue != null) {
 				tokenValue = new String(refreshTokenValue, StandardCharsets.UTF_8);
 				tokenValue = new String(refreshTokenValue, StandardCharsets.UTF_8);
 				issuedAt = null;
 				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);
+		}
+
+	}
+
 }
 }