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

OAuth2.0中Refresh Token被撤销时Spring/Java应用用户登出方案咨询

管理员作废Refresh Token后用户自动登出的Spring/Java解决方案

针对OAuth2.0场景下管理员作废用户Refresh Token后需用户自动登出的需求,以下是几种可落地的Spring/Java实现方案:

方案一:Refresh Token黑名单+请求拦截校验

这是最通用的方案,核心思路是维护已作废的Refresh Token黑名单,每次请求时校验token是否在黑名单内,若存在则强制登出。

实现步骤

  • 1. 搭建黑名单存储:使用Redis作为共享存储(分布式场景必备),管理员作废Token时,将该Refresh Token存入Redis,并设置与原Token一致的过期时间,避免无效数据堆积。
  • 2. 自定义Token校验拦截器:在Spring Security的过滤器链中添加自定义过滤器,每次请求时提取Token并校验是否在黑名单中,若命中则返回401并触发前端登出。

代码示例

Redis黑名单操作工具类

@Component
public class RefreshTokenBlacklistManager {
    private static final String BLACKLIST_PREFIX = "blacklist:refresh:";
    @Autowired
    private StringRedisTemplate redisTemplate;

    // 作废Token,加入黑名单
    public void revokeToken(String refreshToken, Duration expireTime) {
        redisTemplate.opsForValue().set(BLACKLIST_PREFIX + refreshToken, "revoked", expireTime);
    }

    // 校验Token是否已作废
    public boolean isTokenRevoked(String refreshToken) {
        return redisTemplate.hasKey(BLACKLIST_PREFIX + refreshToken);
    }
}

自定义请求过滤器

@Component
public class RevokedTokenFilter extends OncePerRequestFilter {
    @Autowired
    private RefreshTokenBlacklistManager blacklistManager;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        // 提取请求中的Refresh Token(或Access Token,根据业务场景调整)
        String authHeader = request.getHeader("Authorization");
        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            String token = authHeader.substring(7);
            if (blacklistManager.isTokenRevoked(token)) {
                // 返回401,触发前端登出逻辑
                response.setStatus(HttpStatus.UNAUTHORIZED.value());
                response.setContentType("application/json");
                response.getWriter().write("{\"code\":401,\"msg\":\"Token已被作废,请重新登录\"}");
                return;
            }
        }
        chain.doFilter(request, response);
    }
}

注册过滤器到Spring Security

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private RevokedTokenFilter revokedTokenFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.addFilterBefore(revokedTokenFilter, UsernamePasswordAuthenticationFilter.class)
            // 其他安全配置...
            .authorizeRequests()
            .anyRequest().authenticated();
    }
}

方案二:利用Spring Security OAuth2的Token撤销端点

Spring Security 5.1+支持RFC7009标准的Token撤销端点,管理员可通过调用该端点作废Refresh Token,客户端在刷新Token时捕获异常触发登出。

实现步骤

  • 1. 授权服务器启用Token撤销:配置授权服务器支持Token撤销功能,使用JDBC或Redis存储撤销记录。
  • 2. 客户端处理刷新异常:前端或后端在调用Refresh Token接口时,若收到401/InvalidTokenException,立即清除本地Token并跳转登录页。

代码示例

授权服务器配置Token撤销

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private DataSource dataSource;
    @Autowired
    private TokenStore tokenStore;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.tokenStore(tokenStore)
                 // 启用Token撤销服务
                 .revocationServices(new JdbcTokenRevocationServices(dataSource));
    }
}

客户端刷新Token异常处理(后端示例)

@Service
public class TokenRefreshService {
    @Autowired
    private OAuth2RestTemplate restTemplate;

    public OAuth2AccessToken refreshToken(String refreshToken) {
        try {
            OAuth2AccessToken token = restTemplate.getOAuth2ClientContext().getAccessToken();
            token.setRefreshToken(new DefaultOAuth2RefreshToken(refreshToken));
            return restTemplate.refreshAccessToken();
        } catch (InvalidTokenException e) {
            // Token已被作废,触发登出逻辑
            throw new RuntimeException("Token已失效,请重新登录");
        }
    }
}

方案三:WebSocket实时推送登出通知

对于需要实时性的场景,管理员作废Token时通过WebSocket向用户的在线会话推送登出指令,前端收到通知后立即登出。

实现步骤

  • 1. 配置Spring WebSocket:使用STOMP协议搭建WebSocket服务,支持点对点推送。
  • 2. 管理员作废Token时推送通知:根据用户ID找到其所有在线WebSocket会话,发送登出指令。
  • 3. 前端监听通知并执行登出:前端连接WebSocket,监听对应主题,收到指令后清除本地Token并跳转登录页。

代码示例

WebSocket配置

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        // 启用简单消息代理,处理推送主题
        config.enableSimpleBroker("/user");
        config.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        // 注册WebSocket端点,支持SockJS降级
        registry.addEndpoint("/ws-logout").withSockJS();
    }
}

推送登出通知服务

@Service
public class LogoutPushService {
    @Autowired
    private SimpMessagingTemplate messagingTemplate;

    // 向指定用户推送登出通知
    public void pushLogoutNotification(String userId) {
        // 点对点推送,确保只有目标用户收到
        messagingTemplate.convertAndSendToUser(userId, "/queue/logout", "token_revoked");
    }
}

管理员作废Token时调用

@Service
public class TokenManagementService {
    @Autowired
    private RefreshTokenBlacklistManager blacklistManager;
    @Autowired
    private LogoutPushService logoutPushService;

    public void revokeUserRefreshToken(String userId, String refreshToken, Duration expireTime) {
        // 加入黑名单
        blacklistManager.revokeToken(refreshToken, expireTime);
        // 推送登出通知
        logoutPushService.pushLogoutNotification(userId);
    }
}

注意事项

  • 若使用JWT作为Access Token,由于JWT是无状态的,已发出的Access Token在过期前仍有效,建议缩短Access Token有效期(如15分钟),配合Refresh Token刷新机制降低风险。
  • 分布式场景下,黑名单存储必须使用共享存储(如Redis),不能使用本地内存。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 21:35:22