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

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()
}

排查思路

  1. Spring Security拦截冲突:你自定义了/login的POST接口,但Spring Security默认会拦截/login的POST请求处理认证,导致你的Controller逻辑根本没执行,直接走框架默认认证流程(而你未配置Security的用户认证服务),最终失败重定向。

    • 解决:要么将Controller的POST映射改为/perform_login,并在SecurityConfig中添加.loginProcessingUrl("/perform_login");要么配置Spring Security的UserDetailsService和密码编码器,完全使用框架认证机制。
  2. 密码匹配逻辑验证:确认User.isMatchingPassword()方法是否正确使用Spring Security的密码编码器(如BCryptPasswordEncoder)。注册时用BCrypt加密的话,登录必须用同一编码器验证,不能直接字符串比对。

  3. CSRF令牌缺失:Spring Security默认开启CSRF保护,你的登录表单未添加CSRF令牌会导致请求被拦截。在form内添加:

    <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
    
  4. Session处理冲突:手动设置Session用户后,Spring Security的SecurityContext未同步,导致访问/home时被判定未认证。应使用Security的AuthenticationManager完成认证,让框架管理上下文。

  5. 日志级别调整:在application.properties中添加日志配置,查看Security详细报错:

    logging.level.org.springframework.security=DEBUG
    

    启动后控制台会输出认证失败的具体原因。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 05:47:04