Spring Boot登录异常:凭证正确仍重定向至login?error求助
login?error排查求助 我是编程新手,参与小组练习项目,技术栈为Java MVC、Spring Boot、Thymeleaf、SQL数据库,使用Spring Security做认证。用户注册正常(用户名和哈希密码存入SQL),但登录时即便账号密码正确,页面仍重定向到login?error,网络面板、IDE无报错,调试也没发现问题,自查逻辑无果,求排查思路。
SecurityConfig配置
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers( "/register").permitAll() // 未认证用户可访问注册页 .antMatchers("/styles/**").permitAll() .antMatchers("/login*").permitAll() .antMatchers("/home").authenticated() // /home需要认证 .anyRequest().authenticated() // 其他所有请求都需要认证 .and() .formLogin() .loginPage("/login").permitAll() // 自定义登录页 .defaultSuccessUrl("/home").permitAll() .and() .logout().logoutSuccessUrl("/login?logout").permitAll(); // 登出后跳转 } }
AuthenticationController代码
@GetMapping("/login") public String displayLoginForm(Model model) { model.addAttribute("loginFormDTO", new LoginFormDTO()); model.addAttribute("title", "Log In"); return "login"; } @PostMapping("/login") public String processLoginForm(@ModelAttribute @Valid LoginFormDTO loginFormDTO, Errors errors, HttpServletRequest request, Model model) { if (errors.hasErrors()) { model.addAttribute("title", "Log In"); return "login"; } User theUser = userRepository.findByUsername(loginFormDTO.getUsername()); if (theUser == null) { errors.rejectValue("username", "user.invalid", "The given username does not exist"); model.addAttribute("title", "Log In"); return "login"; } String password = loginFormDTO.getPassword(); if (!theUser.isMatchingPassword(password)) { errors.rejectValue("password", "password.invalid", "Invalid password"); model.addAttribute("title", "Log In"); return "login"; } setUserInSession(request.getSession(), theUser); return "home"; } @GetMapping("/home") public String displayHomepage(Model model, HttpServletRequest request) { String username = getUserFromSession(request.getSession()).getUsername(); model.addAttribute("username", username); return "home"; }
login.html模板
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org/" lang="en" > <head th:replace="fragments :: head"></head> <title>Login</title> <body class="container"> <form method="post" th:action="@{/login}" > <div> <label th:for="username">Username <input class="form-control" th:field="${loginFormDTO.username}" type="text"> </label> <br> <br> <label th:for="password">Password <input class="form-control" th:field="${loginFormDTO.password}" type="password"> </label> </div> <br> <input type="submit" class="btn btn-primary" value="Log In"/> </form> </body> </html>
home.html模板
<!DOCTYPE html> <html lang="en" xmlns:th="https://www.thymeleaf.org"> <head> <meta charset="UTF-8"/> <meta name="viewport" content="width=device-width"> <title>Homepage</title> </head> <body> <script th:src="@{/script.js}"></script> <h1>My Recipe Manager Homepage</h1> <div id="container"> <!-- List of recipes will be added here dynamically from the JS static file--> </div> </body> </html>
build.gradle配置
plugins { id 'java' id 'org.springframework.boot' version '2.7.13' id 'io.spring.dependency-management' version '1.0.15.RELEASE' } group = 'org.liftoff.DigitalRecipeManager' version = '0.0.1-SNAPSHOT' java { sourceCompatibility = '11' } repositories { mavenCentral() } dependencies { implementation 'org.springframework.security:spring-security-crypto' implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.security:spring-security-test' implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.projectlombok:lombok:1.18.22' implementation 'de.vinado.spring:dkim-javamail:1.2.2' developmentOnly 'org.springframework.boot:spring-boot-devtools' testImplementation 'org.springframework.boot:spring-boot-starter-test' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'mysql:mysql-connector-java:8.0.33' } tasks.named('test') { useJUnitPlatform() }
排查思路
Spring Security拦截冲突:你自定义了
/login的POST接口,但Spring Security默认会拦截/login的POST请求处理认证,导致你的Controller逻辑根本没执行,直接走框架默认认证流程(而你未配置Security的用户认证服务),最终失败重定向。- 解决:要么将Controller的POST映射改为
/perform_login,并在SecurityConfig中添加.loginProcessingUrl("/perform_login");要么配置Spring Security的UserDetailsService和密码编码器,完全使用框架认证机制。
- 解决:要么将Controller的POST映射改为
密码匹配逻辑验证:确认
User.isMatchingPassword()方法是否正确使用Spring Security的密码编码器(如BCryptPasswordEncoder)。注册时用BCrypt加密的话,登录必须用同一编码器验证,不能直接字符串比对。CSRF令牌缺失:Spring Security默认开启CSRF保护,你的登录表单未添加CSRF令牌会导致请求被拦截。在form内添加:
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>Session处理冲突:手动设置Session用户后,Spring Security的SecurityContext未同步,导致访问
/home时被判定未认证。应使用Security的AuthenticationManager完成认证,让框架管理上下文。日志级别调整:在
application.properties中添加日志配置,查看Security详细报错:logging.level.org.springframework.security=DEBUG启动后控制台会输出认证失败的具体原因。
内容的提问来源于stack exchange,提问作者lu_ray_waldemer

