如何基于OAuth2实现适配Google API的自定义DataStoreFactory?
自定义Google API DataStoreFactory实现方案
核心逻辑说明
Google Java API客户端的DataStore抽象是通用的键值存储接口,只要正确实现DataStoreFactory和DataStore的接口方法,SDK会自动完成令牌的存取、刷新后自动持久化操作,无需额外手动处理令牌同步逻辑。
接口方法实现说明
1. DataStoreFactory实现
仅需要实现getDataStore方法:
- 方法入参
arg0是存储实例的唯一ID,SDK默认会传入固定值StoredCredential,可直接作为数据库存储的分区标识/表名使用 - 方法返回你自定义实现的DataStore实例即可,实例中可注入你项目中的数据库操作组件
2. DataStore各方法实现逻辑
所有方法直接对应你自有数据库的增删改查操作:
getId():直接返回初始化DataStore时传入的存储ID(即getDataStore的入参)getDataStoreFactory():返回当前关联的自定义DataStoreFactory实例get(String key):入参key为用户唯一标识(一般为你业务系统内的用户ID),根据key查询数据库中存储的凭证,组装为StoredCredential对象返回即可set(String key, Serializable value):入参value为SDK生成的StoredCredential对象,包含access令牌、refresh令牌、过期时间等核心信息。你可直接将value序列化后存储,也可拆分为独立字段(如access_token、refresh_token、expires_at)存入数据库,存在对应key则更新,不存在则新增,最终返回当前DataStore实例delete(String key):根据入参key删除数据库中对应的凭证记录,返回当前DataStore实例containsKey(String key):查询数据库中是否存在对应key的凭证记录,返回布尔值size():返回数据库中存储的凭证总条数isEmpty():判断凭证存储是否为空,可直接返回size() == 0keySet():返回数据库中所有凭证的用户标识key的集合values():返回数据库中所有存储的StoredCredential对象集合containsValue(Serializable value):遍历所有存储的凭证,判断是否存在匹配项,该方法调用频率极低,若业务无需要也可直接抛出UnsupportedOperationExceptionclear():清空所有凭证记录,按需实现即可,无需要也可直接抛出UnsupportedOperationException
自动刷新相关说明
无需自行实现DataStoreCredentialRefreshListener。当你将自定义DataStoreFactory传入GoogleAuthorizationCodeFlow后,SDK内部会自动注册默认的刷新监听器,access令牌过期后SDK自动刷新得到新令牌时,会主动调用你实现的DataStore.set()方法,自动将最新的令牌信息持久化到你的数据库,无需额外手动处理。
简化实现示例
// 自定义DataStoreFactory实现 import com.google.api.client.util.store.DataStore; import com.google.api.client.util.store.DataStoreFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.io.IOException; import java.io.Serializable; @Component public class MyCustomDatastoreFactory implements DataStoreFactory { @Autowired private CredentialMapper credentialMapper; @Override public <V extends Serializable> DataStore<V> getDataStore(String storeId) throws IOException { return new MyCustomDataStore<>(storeId, this, credentialMapper); } }
// 自定义DataStore实现 import com.google.api.client.auth.oauth2.StoredCredential; import com.google.api.client.util.store.DataStore; import com.google.api.client.util.store.DataStoreFactory; import java.io.IOException; import java.io.Serializable; import java.util.Collection; import java.util.Set; import java.util.stream.Collectors; public class MyCustomDataStore<V extends Serializable> implements DataStore<V> { private final String storeId; private final DataStoreFactory dataStoreFactory; private final CredentialMapper credentialMapper; public MyCustomDataStore(String storeId, DataStoreFactory dataStoreFactory, CredentialMapper credentialMapper) { this.storeId = storeId; this.dataStoreFactory = dataStoreFactory; this.credentialMapper = credentialMapper; } @Override public String getId() { return storeId; } @Override public DataStoreFactory getDataStoreFactory() { return dataStoreFactory; } @Override public V get(String key) throws IOException { CredentialPO po = credentialMapper.selectByUserId(key); if (po == null) return null; // 从PO组装为StoredCredential对象 StoredCredential credential = new StoredCredential(); credential.setAccessToken(po.getAccessToken()); credential.setRefreshToken(po.getRefreshToken()); credential.setExpirationTimeMilliseconds(po.getExpiresAt()); return (V) credential; } @Override public DataStore<V> set(String key, V value) throws IOException { StoredCredential credential = (StoredCredential) value; CredentialPO po = new CredentialPO(); po.setUserId(key); po.setAccessToken(credential.getAccessToken()); po.setRefreshToken(credential.getRefreshToken()); po.setExpiresAt(credential.getExpirationTimeMilliseconds()); // 存在则更新,不存在则插入 if (credentialMapper.selectByUserId(key) != null) { credentialMapper.updateByUserId(po); } else { credentialMapper.insert(po); } return this; } @Override public DataStore<V> delete(String key) throws IOException { credentialMapper.deleteByUserId(key); return this; } @Override public boolean containsKey(String key) throws IOException { return credentialMapper.selectByUserId(key) != null; } @Override public int size() throws IOException { return credentialMapper.countAll(); } @Override public boolean isEmpty() throws IOException { return size() == 0; } @Override public Set<String> keySet() throws IOException { return credentialMapper.listAllUserIds(); } @Override public Collection<V> values() throws IOException { return credentialMapper.listAll().stream().map(po -> { StoredCredential credential = new StoredCredential(); credential.setAccessToken(po.getAccessToken()); credential.setRefreshToken(po.getRefreshToken()); credential.setExpirationTimeMilliseconds(po.getExpiresAt()); return (V) credential; }).collect(Collectors.toList()); } // 其余非高频方法按需实现即可 @Override public boolean containsValue(Serializable value) throws IOException { throw new UnsupportedOperationException(); } @Override public DataStore<V> clear() throws IOException { throw new UnsupportedOperationException(); } }
内容的提问来源于stack exchange,提问作者RTF
相关产品推荐
相关产品推荐

