Spring Boot REST API禁用表单登录成功后重定向及会话管理实现求助
我完全懂你的困扰——用服务器端会话+Cookie替代JWT是完全合理的选择,默认的重定向只是Spring Security的内置行为,咱们只需要自定义认证成功后的处理逻辑就能搞定这个问题。
问题根源
默认的UsernamePasswordAuthenticationFilter在认证成功后,会执行successfulAuthentication方法,其中的默认逻辑是重定向到之前请求的页面(或者默认的/路径)。但你是前后端分离的React应用,显然不需要这个重定向,而是需要返回一个JSON响应告知前端登录状态。
解决方案:重写认证成功/失败的处理逻辑
你需要在自定义的AuthenticationFilter中重写successfulAuthentication和unsuccessfulAuthentication方法,替换默认的重定向行为,直接向前端返回JSON响应。
修改后的AuthenticationFilter代码如下:
public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter { public AuthenticationFilter(){ super.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/login", "POST")); } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { System.out.println("Custom Authentication Filter fired!"); ObjectMapper mapper = new ObjectMapper(); Login login = new Login(); try { login = mapper.readValue(request.getInputStream(), Login.class); } catch (IOException e) { throw new RuntimeException("Failed to parse login request body", e); } UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken( login.getUsername(), login.getPassword() ); return this.getAuthenticationManager().authenticate(token); } // 重写认证成功逻辑,返回JSON响应而非重定向 @Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { // 获取认证后的用户信息 UserDetails userDetails = (UserDetails) authResult.getPrincipal(); // 构造响应内容 Map<String, Object> responseBody = new HashMap<>(); responseBody.put("success", true); responseBody.put("message", "Login successful"); responseBody.put("username", userDetails.getUsername()); // 设置响应头 response.setContentType("application/json"); response.setCharacterEncoding("UTF-8"); // 写入响应 ObjectMapper objectMapper = new ObjectMapper(); objectMapper.writeValue(response.getWriter(), responseBody); // 注意:不要调用父类的successfulAuthentication方法,否则会触发默认重定向 } // 可选:重写认证失败逻辑,返回错误JSON @Override protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException { Map<String, Object> responseBody = new HashMap<>(); responseBody.put("success", false); responseBody.put("message", "Login failed: " + failed.getMessage()); response.setContentType("application/json"); response.setCharacterEncoding("UTF-8"); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.writeValue(response.getWriter(), responseBody); } }
关键补充:跨域Cookie支持配置
因为你的React应用运行在http://localhost:3000,后端在另一个端口,属于跨域场景,要让浏览器自动携带会话Cookie,需要调整CORS配置:
修改corsConfigurationSource方法,添加允许凭证的设置:
@Bean public CorsConfigurationSource corsConfigurationSource(){ CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.setAllowedOrigins(Arrays.asList("http://localhost:3000")); corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); // 新增:允许携带Cookie等凭证 corsConfiguration.setAllowCredentials(true); // 新增:允许必要的请求头,比如Content-Type和CSRF Token corsConfiguration.setAllowedHeaders(Arrays.asList("Content-Type", "X-XSRF-TOKEN")); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", corsConfiguration); return source; }
同时,在Spring Security的configure(HttpSecurity http)中补充会话和CSRF的配置,确保Cookie正常工作:
http.sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) // 按需创建会话 .and() .csrf() .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); // 允许前端读取CSRF Token(如果需要)
前端React的注意事项
在发送登录请求时,必须设置credentials: 'include',这样浏览器才会自动携带会话Cookie:
// 示例React登录请求 fetch('http://localhost:8080/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', // 关键:携带Cookie body: JSON.stringify({ username: 'your-username', password: 'your-password' }) }) .then(res => res.json()) .then(data => { if (data.success) { // 处理登录成功逻辑,比如跳转到首页 } else { // 处理登录失败逻辑,比如提示错误 } });
这样修改后,登录成功时后端会直接返回JSON响应,不再重定向到/,同时会话Cookie会被浏览器保存,后续请求只要携带这个Cookie就能保持登录状态。
内容的提问来源于stack exchange,提问作者user16422658

