Spring Security登录接口测试返回401而非200问题求助
咱们一步步拆解你遇到的问题,逐个解决:
1. 测试类注解冲突导致安全配置未加载
你同时使用了@DataJpaTest和@SpringBootTest,但@DataJpaTest是切片测试注解——它只会加载与JPA相关的Bean,不会完整加载你的WebSecurityConfig等Web层配置。这直接导致你的Spring Security规则没生效,/api/login并没有被允许匿名访问,所以返回401。
修复方案:
去掉@DataJpaTest,只保留@SpringBootTest和@AutoConfigureMockMvc,这样才能加载完整的应用上下文,包括安全配置:
@RunWith(SpringRunner.class) @SpringBootTest @AutoConfigureMockMvc public class AuthenticationTest { // 测试代码保持不变 }
2. Form Login路径配置不匹配
在你的WebSecurityConfig中,.loginPage("/api/login")是指定登录页面的GET请求路径,但Spring Security默认的登录处理(POST提交凭证)路径是/login。你的测试是POST到/api/login,但这个路径并没有被配置成登录处理端点,所以请求被当成了需要认证的普通接口,返回401。
修复方案:
添加loginProcessingUrl("/api/login")来指定处理登录POST请求的路径:
@Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/api/login").permitAll() .antMatchers("/api/registration").permitAll() .anyRequest().authenticated() .and() .csrf().disable() .exceptionHandling().authenticationEntryPoint(new Http403ForbiddenEntryPoint()) .and() .formLogin() .successHandler(authenticationSuccessHandler) .loginPage("/api/login") // 处理GET请求的登录页面路径 .loginProcessingUrl("/api/login") // 处理POST登录请求的路径 .failureHandler(new SimpleUrlAuthenticationFailureHandler()) .and() .logout() .logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler(HttpStatus.OK)); }
3. 密码编码与凭证匹配问题
你配置了BCryptPasswordEncoder,但要注意:BCrypt要求存储的是加密后的哈希值,而不是明文密码。如果你的UserDetailsServiceImpl返回的用户密码是明文,或者application.properties里的配置是明文,都会导致凭证验证失败。
快速验证方案:
可以临时在测试中生成加密后的密码,替换到用户数据中:
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); System.out.println(encoder.encode("password"));
或者直接在configureGlobal中配置内存用户来测试:
@Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth .inMemoryAuthentication() .passwordEncoder(passwordEncoder()) .withUser("user") .password(passwordEncoder().encode("password")) .roles("USER"); }
4. 日志中Handler为null的问题
从日志里的Handler: Type = null可以看出,请求根本没被Spring Security的登录过滤器拦截,本质还是前面的上下文加载或路径配置问题,解决前两个问题后这个现象会自动消失。
按照上面的步骤调整后,再运行测试应该就能拿到200状态码了。
内容的提问来源于stack exchange,提问作者Denys Bondarenko

