Spring Boot Security登录异常:HttpSession无SecurityContext,权限被拒
问题场景
基于Spring Boot 3.1.1、JDK 17、JSF开发的war包部署在Tomcat 10.1.x环境中,xhtml文件存放于WEB-INF目录下。使用正确用户名/密码在login.xhtml登录后,应用停留在登录页,无法跳转到默认成功页面test.xhtml。安全日志显示未找到HttpSession中的SecurityContext,最终权限被判定为ROLE_ANONYMOUS,触发Access Denied异常。
关键日志信息
533-04:00 DEBUG 3980 --- [nio-8080-exec-8] o.s.security.web.FilterChainProxy : Secured POST /login
543-04:00 DEBUG 3980 --- [nio-8080-exec-8] o.s.web.servlet.DispatcherServlet : POST "/sbwar2/login", parameters={username:[user], password:[password], _csrf:[9cqHDzmNlsgTvrwCjfBK1gKrEB8YVEBLfuM0Te5Q3Lhh1DjTwq-yPA-49Pk-2t007t1-4TqbPScvN3FmG4YMfNpiut5Rtlvi]}
549-04:00 DEBUG 3980 --- [nio-8080-exec-8] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [classpath [META-INF/resources/], classpath [resources/], classpath [static/], classpath [public/], ServletContext [/]]
604-04:00 DEBUG 3980 --- [nio-8080-exec-8] o.s.w.s.r.ResourceHttpRequestHandler : Resource not found
608-04:00 DEBUG 3980 --- [nio-8080-exec-8] o.s.web.servlet.DispatcherServlet : Completed 404 NOT_FOUND
609-04:00 TRACE 3980 --- [nio-8080-exec-8] w.c.HttpSessionSecurityContextRepository : Did not find SecurityContext in HttpSession 1204F5B756E65B39DA16CFE184630C21 using the SPRING_SECURITY_CONTEXT session attribute
610-04:00 TRACE 3980 --- [nio-8080-exec-8] .s.s.w.c.SupplierDeferredSecurityContext : Created SecurityContextImpl [Null authentication]
614-04:00 TRACE 3980 --- [nio-8080-exec-8] o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to AnonymousAuthenticationToken [Principal=anonymousUser, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=1204F5B756E65B39DA16CFE184630C21], Granted Authorities=[ROLE_ANONYMOUS]]
651-04:00 TRACE 3980 --- [nio-8080-exec-8] o.s.s.w.a.ExceptionTranslationFilter : Sending AnonymousAuthenticationToken [Principal=anonymousUser, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=1204F5B756E65B39DA16CFE184630C21], Granted Authorities=[ROLE_ANONYMOUS]] to authentication entry point since access is denied
org.springframework.security.access.AccessDeniedException: Access Denied
at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:98) ~[spring-security-web-6.1.1.jar:6.1.1]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ......................................
相关代码
WebSecurityConfig.java
@Configuration @EnableWebSecurity public class WebSecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests((requests) -> requests .requestMatchers("/", "/home","/jakarta.faces.resource/**", "/login", "/login.xhtml").permitAll() .anyRequest().authenticated() ) .formLogin((form) -> form .loginPage("/login.xhtml") .permitAll() .defaultSuccessUrl("/test.xhtml", true) .failureUrl("/login.xhtml?error-true") ) .cors(cors -> cors.disable()) .logout((logout) -> logout.permitAll()); return http.build(); } @Bean public UserDetailsService users() { UserDetails user = User.builder() .username("user") .password("password") .roles("USER") .build(); return new InMemoryUserDetailsManager(user); } }
login.xhtml
<form action="${request.contextPath}/login" method="POST"> <div align="center"><h:outputLabel styleClass="fa fa-2x" value="Sign in"/></div><br/> <div align="center"> <h:outputLabel class="alert alert-danger" value="Invalid user name or password" rendered="${not empty SPRING_SECURITY_LAST_EXCEPTION.message}"/> </div> <div class="input-group input-sm"> <label class="input-group-addon" for="username"><i class="fa fa-user"></i></label> <input type="text" class="form-control" id="username" name="username" placeholder="Username" required="true" autofocus="true"/> </div> <div class="input-group input-sm"> <label class="input-group-addon" for="password"><i class="fa fa-lock"></i></label> <input type="password" class="form-control" id="password" name="password" placeholder="Password" required="true"/> </div> <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" /> <div class="form-group"> <input type="submit" class="btn btn-block btn-primary btn-default" value="Log in"/> </div> </form>
test.xhtml
<h:body> This is a test </h:body>
尝试过程及问题
尝试添加视图控制器并将login.xhtml重命名为signin.xhtml:
@Configuration public class MvcConfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/login").setViewName("login"); } }
此时访问/login出现405 METHOD_NOT_ALLOWED错误:
DEBUG 11512 --- [nio-8080-exec-7] o.s.security.web.FilterChainProxy : Secured POST /login
DEBUG 11512 --- [nio-8080-exec-7] o.s.web.servlet.DispatcherServlet : POST "/sbwar2/login", parameters={username:[user], password:[password],
......
WARN 11512 --- [nio-8080-exec-7] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' is not supported]
DEBUG 11512 --- [nio-8080-exec-7] o.s.web.servlet.DispatcherServlet : Completed 405 METHOD_NOT_ALLOWED
最终解决方法
添加如下控制器类:
@Controller public class LoginController { @GetMapping("/login") String login() { return "signin.xhtml"; } }
内容的提问来源于stack exchange,提问作者dev

