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

Spring Security Reactive:如何在WebFilter中重启认证流程

WebFlux + OAuth2 Login 处理JWT过期重定向的简洁实现

一、自定义JWT过期处理的认证入口点

写一个ServerAuthenticationEntryPoint实现,专门捕获JWT过期、无效这类认证异常,触发OAuth2登录流程同时缓存原始请求,解决认证后无法返回原页面的问题:

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.server.resource.web.server.BearerTokenAuthenticationEntryPoint;
import org.springframework.security.web.server.ServerAuthenticationEntryPoint;
import org.springframework.security.web.server.authentication.RedirectServerAuthenticationEntryPoint;
import org.springframework.security.web.server.savedrequest.WebSessionServerRequestCache;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

public class JwtExpirationAuthenticationEntryPoint implements ServerAuthenticationEntryPoint {

    private final ServerAuthenticationEntryPoint defaultEntryPoint = new BearerTokenAuthenticationEntryPoint();
    private final RedirectServerAuthenticationEntryPoint oauth2LoginRedirector;
    private final WebSessionServerRequestCache requestCache = new WebSessionServerRequestCache();

    // 构造方法传入OAuth2授权端点路径,格式是/oauth2/authorization/你的客户端ID
    public JwtExpirationAuthenticationEntryPoint(String oauth2AuthorizationUri) {
        this.oauth2LoginRedirector = new RedirectServerAuthenticationEntryPoint(oauth2AuthorizationUri);
    }

    @Override
    public Mono<Void> commence(ServerWebExchange exchange, AuthenticationException ex) {
        // 精准匹配JWT相关的认证异常(过期、签名无效等)
        boolean isJwtAuthException = ex.getClass().getName().contains("BearerTokenAuthenticationException")
                || (ex.getCause() != null && ex.getCause().getClass().getName().contains("JwtException"));

        if (isJwtAuthException) {
            // 先缓存当前请求,认证完成后自动跳转回原页面
            return requestCache.saveRequest(exchange)
                    .then(oauth2LoginRedirector.commence(exchange, ex));
        }
        // 其他认证异常交给默认逻辑处理(返回401+WWW-Authenticate头)
        return defaultEntryPoint.commence(exchange, ex);
    }
}

二、配置Spring Security

在Security配置类里替换默认的认证入口点,把自定义的JWT过期处理器挂上去:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.savedrequest.WebSessionServerRequestCache;

@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
        http
            .authorizeExchange(exchanges -> exchanges
                .anyExchange().authenticated()
            )
            .oauth2Login() // 这里可继续配置OAuth2客户端细节,比如跳转后的处理逻辑
            .and()
            .oauth2ResourceServer(oauth2 -> oauth2
                .jwt() // 配置JWT解析参数,比如JWK地址,省略具体配置
                .and()
                // 替换成自定义认证入口点,传入你的OAuth2授权端点路径
                .authenticationEntryPoint(new JwtExpirationAuthenticationEntryPoint("/oauth2/authorization/gateway"))
            )
            // 显式指定请求缓存,确保原始请求能被正确保存
            .requestCache(cache -> cache.requestCache(new WebSessionServerRequestCache()));

        return http.build();
    }
}

三、核心要点

  • 缓存原始请求:之前手动重定向丢失原页面的问题,就是因为没先调用requestCache.saveRequest,现在先缓存再跳转,认证完成后Spring Security会自动读取缓存跳回原请求。
  • 精准异常捕获:只针对JWT相关的异常触发重定向,其他认证异常(比如未携带令牌)还是用默认的401响应逻辑。
  • 授权端点路径:/oauth2/authorization/{clientId}里的clientId要和你application.yml中配置的OAuth2客户端ID完全一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 08:02:34