You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于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() == 0
  • keySet():返回数据库中所有凭证的用户标识key的集合
  • values():返回数据库中所有存储的StoredCredential对象集合
  • containsValue(Serializable value):遍历所有存储的凭证,判断是否存在匹配项,该方法调用频率极低,若业务无需要也可直接抛出UnsupportedOperationException
  • clear():清空所有凭证记录,按需实现即可,无需要也可直接抛出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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 05:06:08