Spring Boot 1.5.7 OAuth(password授权)下延长Access Token过期时间方法
实现Spring Boot OAuth2 Access Token滑动过期(闲置过期)
针对你的需求——让Access Token仅在连续15分钟未被调用时过期(也就是滑动过期),在Spring Boot 1.5.7 + Spring Security OAuth 2.x的环境下,可以通过自定义TokenStore来实现核心逻辑,下面是具体的步骤和代码示例:
核心思路
默认的OAuth2 Access Token是固定有效期,不管是否被使用都会按时过期。要实现“闲置过期”,我们需要在每次API调用验证token时,动态更新token的过期时间(重置为当前时间+15分钟)。这就需要我们重写TokenStore的token读取逻辑,在读取token时触发过期时间的更新。
步骤1:自定义TokenStore
根据你使用的token存储方式(内存/数据库),选择对应的父类进行扩展:
方式1:内存存储(单实例场景)
继承InMemoryTokenStore,重写readAccessToken方法,在读取token时更新过期时间:
import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.provider.token.InMemoryTokenStore; import java.util.Date; public class SlidingExpirationTokenStore extends InMemoryTokenStore { // Access Token闲置有效期:15分钟(900秒) private final int idleValiditySeconds = 900; // Access Token最大有效期:可选,比如24小时,防止token永久有效 private final int maxValiditySeconds = 86400; @Override public OAuth2AccessToken readAccessToken(String tokenValue) { OAuth2AccessToken token = super.readAccessToken(tokenValue); if (token != null && !token.isExpired()) { // 获取token创建时间(需要配合TokenEnhancer添加该字段) Date creationTime = new Date((Long) token.getAdditionalInformation().get("created")); long timeElapsed = System.currentTimeMillis() - creationTime.getTime(); // 未超过最大有效期时,更新闲置过期时间 if (timeElapsed < maxValiditySeconds * 1000) { Date newExpiration = new Date(System.currentTimeMillis() + idleValiditySeconds * 1000); token.setExpiration(newExpiration); // 重新存储更新后的token storeAccessToken(token, token.getAuthentication()); } } return token; } }
方式2:数据库存储(分布式场景)
如果是多实例部署,建议使用JdbcTokenStore,重写方法时更新数据库中的过期时间:
import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.provider.token.JdbcTokenStore; import javax.sql.DataSource; import java.util.Date; public class SlidingJdbcTokenStore extends JdbcTokenStore { private final int idleValiditySeconds = 900; private final int maxValiditySeconds = 86400; public SlidingJdbcTokenStore(DataSource dataSource) { super(dataSource); } @Override public OAuth2AccessToken readAccessToken(String tokenValue) { OAuth2AccessToken token = super.readAccessToken(tokenValue); if (token != null && !token.isExpired()) { Date creationTime = new Date((Long) token.getAdditionalInformation().get("created")); long timeElapsed = System.currentTimeMillis() - creationTime.getTime(); if (timeElapsed < maxValiditySeconds * 1000) { Date newExpiration = new Date(System.currentTimeMillis() + idleValiditySeconds * 1000); token.setExpiration(newExpiration); // 更新数据库中的过期时间字段 getJdbcTemplate().update( "UPDATE oauth_access_token SET expiration = ? WHERE token_id = ?", newExpiration, extractTokenKey(tokenValue)); } } return token; } }
步骤2:添加TokenEnhancer记录创建时间
要判断token是否超过最大有效期,我们需要在token创建时记录它的生成时间,这可以通过自定义TokenEnhancer实现:
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.token.TokenEnhancer; import java.util.HashMap; import java.util.Map; public class CustomTokenEnhancer implements TokenEnhancer { @Override public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) { Map<String, Object> additionalInfo = new HashMap<>(); // 添加token创建时间戳 additionalInfo.put("created", System.currentTimeMillis()); ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo); return accessToken; } }
步骤3:配置授权服务器
在你的AuthorizationServerConfig中,替换默认的TokenStore,并配置TokenEnhancer:
@Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private AuthenticationManager authenticationManager; @Autowired private UserDetailsService userDetailsService; @Autowired(required = false) private DataSource dataSource; // 数据库存储时需要 // 配置自定义TokenStore @Bean public TokenStore tokenStore() { // 单实例用内存存储 // return new SlidingExpirationTokenStore(); // 分布式用数据库存储 return new SlidingJdbcTokenStore(dataSource); } // 配置TokenEnhancerChain @Bean public TokenEnhancer tokenEnhancer() { return new CustomTokenEnhancer(); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain(); tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer())); endpoints .authenticationManager(authenticationManager) .userDetailsService(userDetailsService) .tokenStore(tokenStore()) .tokenEnhancer(tokenEnhancerChain); } // 其他必要配置:客户端信息、密码编码器等 // ... }
注意事项
- 安全风险控制:设置最大有效期很重要,避免用户持续调用API导致token永久有效,建议根据业务场景设置合理的最大有效期(比如24小时)。
- 分布式场景一致性:如果使用数据库存储,要确保所有实例都连接同一个数据库,避免token状态不一致。
- 性能考虑:每次API调用都会触发token过期时间的更新,对于高并发场景,可以考虑优化数据库更新逻辑(比如设置阈值,只有当剩余有效期小于5分钟时才更新)。
内容的提问来源于stack exchange,提问作者Perumal Ramasamy
相关产品推荐
相关产品推荐

