自定义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
相关产品推荐
相关产品推荐

