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

自定义ClientDetailsService(含数据库场景)如何存储与获取Token?

嘿,这个问题我刚好熟!首先得掰扯清楚一个关键点:ClientDetailsService和Token存储完全是两个独立的活儿——前者只管校验客户端的身份信息(比如client_id、client_secret这些),而Token的存、取、删是由TokenStore接口负责的。下面给你两种基于数据库实现Token存储的方案,按需选就行:

方案1:直接用Spring Security OAuth提供的JdbcTokenStore(快速落地)

如果不想自己造轮子,Spring已经给我们封装好了基于JDBC的Token存储实现,几步就能搞定:

  • 第一步:创建数据库表
    Spring Security OAuth有一套默认的Token存储表结构,先在数据库里建好这些表:
-- 存储access_token
CREATE TABLE oauth_access_token (
  token_id VARCHAR(256) DEFAULT NULL,
  token BLOB,
  authentication_id VARCHAR(256) NOT NULL,
  user_name VARCHAR(256) DEFAULT NULL,
  client_id VARCHAR(256) DEFAULT NULL,
  authentication BLOB,
  refresh_token VARCHAR(256) DEFAULT NULL,
  PRIMARY KEY (authentication_id)
);

-- 存储refresh_token
CREATE TABLE oauth_refresh_token (
  token_id VARCHAR(256) DEFAULT NULL,
  token BLOB,
  authentication BLOB
);

-- 如果你还没建客户端信息表,也可以加上(你的clientService应该会操作这个表)
CREATE TABLE oauth_client_details (
  client_id VARCHAR(256) NOT NULL,
  resource_ids VARCHAR(256) DEFAULT NULL,
  client_secret VARCHAR(256) DEFAULT NULL,
  scope VARCHAR(256) DEFAULT NULL,
  authorized_grant_types VARCHAR(256) DEFAULT NULL,
  web_server_redirect_uri VARCHAR(256) DEFAULT NULL,
  authorities VARCHAR(256) DEFAULT NULL,
  access_token_validity INTEGER DEFAULT NULL,
  refresh_token_validity INTEGER DEFAULT NULL,
  additional_information VARCHAR(4096) DEFAULT NULL,
  autoapprove VARCHAR(256) DEFAULT NULL,
  PRIMARY KEY (client_id)
);
  • 第二步:配置JdbcTokenStore
    在你的授权服务器配置类(继承AuthorizationServerConfigurerAdapter)里,注入数据源并配置TokenStore:
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private DataSource dataSource;

    @Autowired
    private ClientDetailsService clientService; // 你自定义的ClientDetailsService

    // 创建JdbcTokenStore实例
    @Bean
    public TokenStore tokenStore() {
        return new JdbcTokenStore(dataSource);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // 指定使用你自定义的clientService
        clients.withClientDetails(clientService);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        // 把TokenStore绑定到授权服务器
        endpoints.tokenStore(tokenStore())
                 .clientDetailsService(clientService);
        // 还可以按需添加授权管理器、令牌增强器等配置
    }
}

这样授权服务器生成Token时会自动存入数据库,验证Token时也会从数据库读取,完全不用自己写存储逻辑。

方案2:自定义TokenStore(按需定制)

如果默认的表结构或逻辑满足不了你的需求(比如要加自定义字段、修改存储规则),可以自己实现TokenStore接口:

  • 第一步:实现TokenStore核心方法
    重点实现存取access_token和refresh_token的方法,举个简单例子:
@Service
public class CustomJdbcTokenStore implements TokenStore {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
        // 自定义access_token存储逻辑,比如存入自己设计的表
        String tokenId = token.getValue();
        String sql = "INSERT INTO custom_access_token (token_id, token_value, expires_at, client_id, username) VALUES (?, ?, ?, ?, ?)";
        jdbcTemplate.update(sql,
                tokenId,
                token.getValue(),
                new Date(token.getExpiresIn() * 1000 + System.currentTimeMillis()),
                authentication.getOAuth2Request().getClientId(),
                authentication.getName());
    }

    @Override
    public OAuth2AccessToken readAccessToken(String tokenValue) {
        // 根据token值从数据库读取并封装成OAuth2AccessToken对象
        String sql = "SELECT token_value, expires_at FROM custom_access_token WHERE token_id = ?";
        return jdbcTemplate.queryForObject(sql, new Object[]{tokenValue}, (rs, rowNum) -> {
            SimpleOAuth2AccessToken token = new SimpleOAuth2AccessToken(rs.getString("token_value"));
            token.setExpiration(rs.getTimestamp("expires_at"));
            return token;
        });
    }

    // 还要实现removeAccessToken、storeRefreshToken、readRefreshToken、removeRefreshToken等方法,按需完成逻辑
}
  • 第二步:配置使用自定义TokenStore
    在授权服务器配置类里替换TokenStore的Bean即可:
@Bean
public TokenStore tokenStore() {
    return new CustomJdbcTokenStore();
}
关于Token的获取与验证
  • 当客户端通过授权流程(比如密码模式、授权码模式)请求Token时,授权服务器会自动调用TokenStore的storeAccessToken和storeRefreshToken方法,把生成的Token存入数据库。
  • 客户端后续用access_token访问资源服务器时,资源服务器会通过TokenStore的readAccessToken方法读取Token,验证其是否合法、是否过期。

内容的提问来源于stack exchange,提问作者wangbin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 03:34:50