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

Spring Authorization Server多因素认证中如何避免RequestCache被覆盖?

解决Spring Authorization Server多因素认证中RequestCache覆盖问题

核心问题是未认证用户访问/verify-otp时,默认RequestCache会缓存该请求,覆盖之前的/oauth2/authorize请求参数,导致OTP验证完成后无法正确重定向到OAuth回调地址。以下是几种可行的解决方案:

方案1:自定义RequestCache排除/verify-otp端点

实现自定义RequestCache,跳过对/verify-otp请求的缓存,保留原有OAuth请求的缓存记录:

import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SkipOtpRequestCache extends HttpSessionRequestCache {
    @Override
    public void saveRequest(HttpServletRequest request, HttpServletResponse response) {
        // 仅缓存非/verify-otp的请求
        if (!"/verify-otp".equals(request.getRequestURI())) {
            super.saveRequest(request, response);
        }
    }
}

在Security配置中替换默认的RequestCache:

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .requestCache(cache -> cache.requestCache(new SkipOtpRequestCache()))
            .formLogin(form -> form
                .loginPage("/login")
                .successHandler((req, res, auth) -> {
                    SkipOtpRequestCache requestCache = new SkipOtpRequestCache();
                    SavedRequest savedRequest = requestCache.getRequest(req, res);
                    if (savedRequest != null) {
                        res.sendRedirect(savedRequest.getRedirectUrl());
                    } else {
                        res.sendRedirect("/");
                    }
                })
            )
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/login", "/verify-otp").permitAll()
                .anyRequest().authenticated()
            );
        return http.build();
    }
}

方案2:允许未认证用户直接访问/verify-otp

调整Security配置,让/verify-otp无需认证即可访问,避免触发默认的认证重定向和缓存覆盖:

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/login", "/verify-otp").permitAll()
                .anyRequest().authenticated()
            )
            .formLogin(form -> form.loginPage("/login"));
        return http.build();
    }
}

同时在/verify-otp的处理逻辑中,自行校验用户是否已完成第一步登录(比如检查会话中是否有临时用户信息),若未完成则手动重定向到/login,避免依赖默认的RequestCache机制。

方案3:将OAuth请求参数存储到会话

在第一次跳转至/login时,把/oauth2/authorize的请求参数存入会话,脱离对RequestCache的依赖:

@Controller
public class AuthController {
    @GetMapping("/login")
    public String login(HttpServletRequest request, HttpSession session) {
        HttpSessionRequestCache requestCache = new HttpSessionRequestCache();
        SavedRequest savedRequest = requestCache.getRequest(request, null);
        // 仅保存OAuth授权请求的跳转地址
        if (savedRequest != null && savedRequest.getRedirectUrl().contains("/oauth2/authorize")) {
            session.setAttribute("oauth_redirect_url", savedRequest.getRedirectUrl());
        }
        return "login";
    }

    @PostMapping("/verify-otp")
    public String verifyOtp(HttpSession session, @RequestParam String otp) {
        // OTP验证逻辑...
        
        String redirectUrl = (String) session.getAttribute("oauth_redirect_url");
        if (redirectUrl != null) {
            session.removeAttribute("oauth_redirect_url");
            return "redirect:" + redirectUrl;
        }
        return "redirect:/";
    }
}

登录成功后,直接从会话中取出存储的OAuth跳转地址完成重定向,不受后续请求缓存的影响。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 05:22:07