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
相关产品推荐
相关产品推荐

