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

Spring Security问题:手动添加UsernamePasswordAuthenticationFilter后登录触发重定向

Spring Security 登录后重定向导致403问题解决

我正在学习Spring Security,计划开发独立前端与Spring Web后端交互。目前在用户名/密码登录时遇到问题:凭证验证通过,但返回403 Forbidden错误,推测与登录后的重定向行为有关。

当前配置

SecurityFilterChain 基础配置

http.csrf().disable()
        .formLogin().disable()
        .authorizeRequests()
        .antMatchers("/login", "/error").permitAll()
        .anyRequest().authenticated();

自定义用户名密码过滤器

由于禁用默认表单登录后,UsernamePasswordAuthenticationFilter不存在,因此手动添加了自定义验证逻辑的过滤器:

UsernamePasswordAuthenticationFilter filter =
    new UsernamePasswordAuthenticationFilter(authentication -> {
    String username = authentication.getPrincipal().toString();
    String password = authentication.getCredentials().toString();

    if (username.isEmpty() || password.isEmpty()) {
        throw new BadCredentialsException("Username or password not specified");
    }

    if (!username.equals("u") || !password.equals("p")) {
        throw new BadCredentialsException("Wrong username or password");
    }

    return new UsernamePasswordAuthenticationToken(username, null);
});

http.addFilter(filter);

调试日志

[nio-8079-exec-3] o.s.security.web.FilterChainProxy        : Securing POST /login?username=u&password=p
[nio-8079-exec-3] s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext
[nio-8079-exec-3] w.a.UsernamePasswordAuthenticationFilter : Set SecurityContextHolder to UsernamePasswordAuthenticationToken [Principal=u, Credentials=[PROTECTED], Authenticated=false, Details=null, Granted Authorities=[]]
[nio-8079-exec-3] o.s.s.web.DefaultRedirectStrategy        : Redirecting to http://localhost:8079/
[nio-8079-exec-3] w.c.HttpSessionSecurityContextRepository : Stored SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=u, Credentials=[PROTECTED], Authenticated=false, Details=null, Granted Authorities=[]]] to HttpSession [org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper$HttpSessionWrapper@7ba6bfb2]
[nio-8079-exec-3] w.c.HttpSessionSecurityContextRepository : Stored SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=u, Credentials=[PROTECTED], Authenticated=false, Details=null, Granted Authorities=[]]] to HttpSession [org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper$HttpSessionWrapper@7ba6bfb2]
[nio-8079-exec-3] s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request
[nio-8079-exec-4] o.s.security.web.FilterChainProxy        : Securing POST /
[nio-8079-exec-4] w.c.HttpSessionSecurityContextRepository : Retrieved SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=u, Credentials=[PROTECTED], Authenticated=false, Details=null, Granted Authorities=[]]]
[nio-8079-exec-4] s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=u, Credentials=[PROTECTED], Authenticated=false, Details=null, Granted Authorities=[]]]
[nio-8079-exec-4] o.s.s.w.s.HttpSessionRequestCache        : Loaded matching saved request http://localhost:8079/
[nio-8079-exec-4] o.s.s.a.dao.DaoAuthenticationProvider    : Failed to authenticate since no credentials provided
[nio-8079-exec-4] o.s.s.w.s.HttpSessionRequestCache        : Saved request http://localhost:8079/ to session
[nio-8079-exec-4] o.s.s.w.a.Http403ForbiddenEntryPoint     : Pre-authenticated entry point called. Rejecting access
[nio-8079-exec-4] w.c.HttpSessionSecurityContextRepository : Did not store empty SecurityContext
[nio-8079-exec-4] w.c.HttpSessionSecurityContextRepository : Did not store empty SecurityContext
[nio-8079-exec-4] s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request
[nio-8079-exec-4] o.s.security.web.FilterChainProxy        : Securing POST /error
[nio-8079-exec-4] s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext
[nio-8079-exec-4] o.s.s.w.a.AnonymousAuthenticationFilter  : Set SecurityContextHolder to anonymous SecurityContext
[nio-8079-exec-4] o.s.security.web.FilterChainProxy        : Secured POST /error
[nio-8079-exec-4] w.c.HttpSessionSecurityContextRepository : Did not store anonymous SecurityContext
[nio-8079-exec-4] w.c.HttpSessionSecurityContextRepository : Did not store anonymous SecurityContext
[nio-8079-exec-4] s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request

问题分析与解决

从日志能定位两个关键问题:

  • 生成的UsernamePasswordAuthenticationToken未标记为已认证(Authenticated=false),且无任何权限,导致重定向到根路径/时被拦截返回403。
  • 默认的UsernamePasswordAuthenticationFilter在认证成功后会执行重定向,这和前后端分离场景的需求冲突。

按以下步骤修复:

1. 正确生成已认证的AuthenticationToken

Spring Security中,只有带GrantedAuthority参数的UsernamePasswordAuthenticationToken构造器会自动将token标记为已认证。修改token生成逻辑:

List<GrantedAuthority> authorities = Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER"));
return new UsernamePasswordAuthenticationToken(username, null, authorities);

2. 自定义认证成功处理器,取消重定向

创建一个AuthenticationSuccessHandler,直接返回JSON响应而非重定向:

AuthenticationSuccessHandler successHandler = (request, response, authentication) -> {
    response.setContentType("application/json;charset=UTF-8");
    response.setStatus(HttpServletResponse.SC_OK);
    response.getWriter().write("{\"code\":200,\"message\":\"登录成功\"}");
};

3. 给过滤器绑定自定义成功处理器

将成功处理器设置到过滤器中,确保认证成功后执行自定义逻辑:

// 原过滤器逻辑保留,添加以下配置
filter.setAuthenticationSuccessHandler(successHandler);
// 若你的登录请求路径不是默认的/login,可手动设置:
// filter.setFilterProcessesUrl("/api/login");

http.addFilter(filter);

4. 可选:禁用请求缓存

如果不需要Spring Security保存未认证前的请求,可禁用请求缓存彻底避免重定向触发:

http.requestCache().requestCache(new NullRequestCache());

调整完成后,登录成功会直接返回200状态码的JSON响应,不会触发重定向,同时token已正确标记为已认证,后续接口请求可正常通过权限校验。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 05:39:17