需求
a、b、c 系统通过 sso 服务实现登录
A、B、C 系统分别获取 Atoken、Btoken、Ctoken 三个 token
其中某一个系统主动登出后,其他两个系统也登出
至此全部 Atoken、Btoken、Ctoken 失效
记录token
pom 文件引入依赖
Redis数据库依赖
hutool:用于解析token
<dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-data-redis</artifactid></dependency><dependency> <groupid>cn.hutool</groupid> <artifactid>hutool-all</artifactid> <version>5.7.13</version></dependency>
token 存储类 实现 AuthJdbcTokenStore
TokenStore 继承 JdbcTokenStore
使用登录用户的用户名 username 做 Redis 的 key
因为用户登录的系统会有多个,所以 value 使用 Redis 的列表类型来存储 token
设置有效时间,保证不少于 list 里 token 的最大有效时间
@Component
public class AuthJdbcTokenStore extends JdbcTokenStore {
public static final String USER_HAVE_TOKEN = "user-tokens:";
@Resource
RedisTemplate redisTemplate;
public AuthJdbcTokenStore(DataSource connectionFactory) {
super(connectionFactory);
}
@Override
public void storeAccessToken(OAuth3AccessToken token, OAuth3Authentication authentication) {
super.storeAccessToken(token, authentication);
if (Optional.ofNullable(authentication.getUserAuthentication()).isPresent()) {
User user = (User) authentication.getUserAuthentication().getPrincipal();
String userTokensKey = USER_HAVE_TOKEN + user.getUsername();
String tokenValue = token.getValue();
redisTemplate.opsForList().leftPush(userTokensKey, tokenValue);
Long seconds = redisTemplate.opsForValue().getOperations().getExpire(userTokensKey);
Long tokenExpTime = getExpTime(tokenValue);
Long expTime = seconds <p>oauth_access_token 使用 JdbcTokenStore 存储 token 需要新增表</p><pre class="brush:sql;">CREATE TABLE `oauth_access_token` (
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`token_id` varchar(255) DEFAULT NULL,
`token` blob,
`authentication_id` varchar(255) DEFAULT NULL,
`user_name` varchar(255) DEFAULT NULL,
`client_id` varchar(255) DEFAULT NULL,
`authentication` blob,
`refresh_token` varchar(255) DEFAULT NULL,
UNIQUE KEY `authentication_id` (`authentication_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
AuthorizationServerConfigurerAdapter 使用 AuthJdbcTokenStore 做 token 存储
-
引入 DataSource,因为 JdbcTokenStore 的构造方法必须传入 DataSource
大模型Token成本节约下载大模型 Token 成本节约工具。在请求到达大模型之前自动压缩 prompt 和上下文,减少 60-95% 的 token 消耗,直接降低 API 成本。支持 Claude/OpenAI/Gemini 等主流模型,提供代理模式、CLI 包装、Python SDK 和 MCP Server 四种接入方式。内置一键安...
创建按 TokenStore,用 AuthJdbcTokenStore 实现
tokenServices 添加 TokenStore
endpoints 添加 tokenServices
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private DataSource dataSource;
...
@Bean
public TokenStore tokenStore() {
JdbcTokenStore tokenStore = new AuthJdbcTokenStore(dataSource);
return tokenStore;
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(tokenStore());
endpoints
.authenticationManager(authenticationManager)
.tokenServices(tokenServices)
.accessTokenConverter(converter)
;
}
...
}
清除token
继承 SimpleUrlLogoutSuccessHandler
获取用户名 userName
获取登录时存储在 Redis 的 token 列表
token 字符串转换成 OAuth3AccessToken
使用 tokenStore 删除 token
@Component
public class AuthLogoutSuccessHandler1 extends SimpleUrlLogoutSuccessHandler {
String USER_HAVE_TOKEN = AuthJdbcTokenStore.USER_HAVE_TOKEN;
@Resource
RedisTemplate redisTemplate;
@Resource
TokenStore tokenStore;
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
if (!Objects.isNull(authentication)) {
String userName = authentication.getName();
String userTokensKey = USER_HAVE_TOKEN + userName;
Long size = redisTemplate.opsForList().size(userTokensKey);
List<string> list = redisTemplate.opsForList().range(userTokensKey, 0, size);
for (String tokenValue : list) {
OAuth3AccessToken token = tokenStore.readAccessToken(tokenValue);
if (Objects.nonNull(token)) {
tokenStore.removeAccessToken(token);
}
}
redisTemplate.delete(userTokensKey);
super.handle(request, response, authentication);
}
}
}</string>
解决登出时长过长
场景:项目运行一段时间后,发现登出时间越来越慢
问题:通过 debug 发现耗时主要在删除 token 那一段
tokenStore.removeAccessToken(token);
原因:随着时间推移,token 越来越多,token 存储表 oauth_access_token 变得异常的大,所以删除效率非常差
解决办法:使用其他 TokenStore,或者清除 oauth_access_token 的表数据










