Spring Security Spring Boot +百里香叶:......无法显示错误,自动转换到登录页面

tzxcd3kk  于 2023-01-09  发布在  Spring
关注(0)|答案(1)|浏览(178)

我有一个注册页面。字段数据经过验证。当用户注册的控制器方法中捕获到错误并再次调用注册页面时(应该显示错误),会自动重定向到**/login**URL。
org. apache. juli. logging. DirectJDKLog [错误] 19:34:25-路径为[]的上下文中的servlet [dispatcherServlet]的Servlet. service()引发异常[请求处理失败:org.thymeleaf.exceptions.TemplateInputException:模板解析期间出错(模板:"类路径资源[templates/account/register.html]")]具有根本原因的java. lang.非法状态异常:Bean名称"userData"的BindingResult和普通目标对象均不可用作org.springframework.web.servlet.support.BindStatus(BindStatus.java:153)上的请求属性,位于org.springframework.web.servlet.support.RequestContext.getBindStatus(RequestContext.java:926)上

spring:
  thymeleaf:
    enabled: true
    check-template-location: true
    prefix: classpath:/templates/
    mode: HTML
    cache: false
    encoding: UTF-8
    suffix: .html

logging:
  pattern:
    console: '%C{1.yaml.} [%-5level] %d{HH:mm:ss} - %msg%n'
  level:
    org:
      hibernate:
        sql: debug
        type:
          descriptor:
            sql:
              BasicBinder: trace

      springframework:
        web:
          client: trace
        security: trace
  • 注册控制器
@Controller
@RequiredArgsConstructor
public class RegistrationController {

    private final UserService userService;

    @GetMapping("/register")
    public String showRegistrationForm(final Model model){
        UserDataDto user = new UserDataDto();

        model.addAttribute("userData", user);
        return "account/register";
    }

    @PostMapping("/register")
    public String userRegistration(final @Valid UserDataDto userData,
                                   final BindingResult bindingResult,
                                   final Model model){
        if(bindingResult.hasErrors()){

            model.addAttribute("errors", userData);
            return "account/register";
        }
        try {
            userService.register(userData);
        }catch (UserAlreadyExistException e){
            
            bindingResult.rejectValue(
                    "email",
                    "userData.email",
                    "An account already exists for this email."
            );
            model.addAttribute("formreg", userData);
            return "account/register";
        }
        return "redirect:/login";
    }
}
  • 登录页面控制器
@Controller
public class LoginPageController {

    @GetMapping("/login-error")
    public String loginError(Model model) {
        model.addAttribute("loginError", true);
        return "login";
    }

    @GetMapping("login")
    public String getLogin(){
        return "account/login";
    }
}
  • 数据输出
public class UserDataDto {

    @NotEmpty(message = "First name can not be empty")
    private String firstName;

    @NotEmpty(message = "Last name can not be empty")
    private String lastName;

    @NotEmpty(message = "Email can not be empty")
    @Email(message = "Please provide a valid email id")
    private String email;

    @NotEmpty(message = "Password can not be empty")
    private String password;

  ...
}
  • 安全配置
@Configuration
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

        return http
                .authorizeHttpRequests()
                .requestMatchers("/login", "/register").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login").permitAll()
                .defaultSuccessUrl("/home")
                .failureUrl("/login?error=true")
                .and()
                .build();
    }

    @Bean
    public PasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
  • account/register.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<head th:fragment="head">
    <meta charset="utf-8">
    <!-- Test it -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!-- Tell the browser to be responsive to screen width -->
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css"
          integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">

</head>

<body class="hold-transition register-page">
<div class="register-logo">
    <div class="card">
        <div class="card-body register-card-body">
            <p class="login-box-msg"></p>

            <form action="#" th:action="@{/register}" th:object="${userData}" method="post">
                <div th:if="${#fields.hasAnyErrors()}">
                    <ul>
                        <li th:each="err : ${#fields.allErrors()}" th:text="${err}" />
                    </ul>
                </div>
              <!--  <div class="alert alert-danger" th:if="${#fields.hasErrors('*')}">
                    <p th:each="err : ${#fields.errors('*')}" th:text="${err}"></p>
                </div>-->
                <div class="input-group mb-3 w-25">
                    <input type="text" class="form-control" th:field="*{firstName}" placeholder="First Name"
                           th:errorclass="is-invalid">
                    <div class="input-group-append">
                        <div class="input-group-text">
                            <span class="fas fa-user"></span>
                        </div>
                    </div>
                </div>
                <div class="input-group mb-3 w-25">
                    <input type="text" class="form-control" th:field="*{lastName}" placeholder="Last Name"
                           th:errorclass="is-invalid">

                    <div class="input-group-append">
                        <div class="input-group-text">
                            <span class="fas fa-user"></span>
                        </div>
                    </div>
                </div>
                <div class="input-group mb-3 w-25">
                    <input type="email" class="form-control" th:field="*{email}" placeholder="Email"
                           th:errorclass="is-invalid">

                    <div class="input-group-append">
                        <div class="input-group-text">
                            <span class="fas fa-envelope"></span>
                        </div>
                    </div>
                </div>
                <div class="input-group mb-3 w-25">
                    <input type="password" class="form-control" th:field="*{password}" placeholder="Password"
                           th:errorclass="is-invalid">

                    <div class="input-group-append">
                        <div class="input-group-text">
                            <span class="fas fa-lock"></span>
                        </div>
                    </div>
                </div>
                <div class="input-group mb-3 w-25">
                    <input type="password" class="form-control" placeholder="Retype password">
                    <div class="input-group-append">
                        <div class="input-group-text">
                            <span class="fas fa-lock"></span>
                        </div>
                    </div>
                </div>
                <div style="width:150px" ;>
                    <button type="submit" class="btn btn-outline-primary  btn-sm btn-block">Register</button>
                </div>

                <div></div>
                <p>
                <div class="row">
                    <div class="col-8">
                        <div class="icheck-primary">
                            <input type="checkbox" id="agreeTerms" name="terms" value="agree">
                            <label for="agreeTerms">
                                I agree to the <a href="#">terms</a>
                            </label>
                        </div>
                    </div>
                    <!-- /.col -->

                </div>
                </p>

                <!-- /.col -->
            </form>
        </div>
        <a href="login" class="text-center">I already have an Account</a>
    </div>
</div>
<!-- /.form-box -->
</div><!-- /.card -->
</div>

</script>

</body>
</html>
  • account/login.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<head th:fragment="head">
    <meta charset="utf-8">
    <!-- Test it -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!-- Tell the browser to be responsive to screen width -->
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css"
          integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
    <link rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
 </head>
<body class="hold-transition login-page">
<div class="login-box">
    <div class="login-logo">
        <div class="card">
            <div class="card-body login-card-body">
                <p class="login-box-msg">Sign in to start your session</p>
                <p th:if="${loginError}" class="error">Wrong user or password</p>
                <form th:action="@{/login}" method="post">
                    <div th:if="${param.error}">
                        <div class="alert alert-danger">
                            Invalid username or password.
                        </div>
                    </div>
                    <div class="input-group mb-3">
                        <input type="email" class="form-control" name="username" placeholder="Email">
                        <div class="input-group-append">
                            <div class="input-group-text">
                                <span class="fas fa-envelope"></span>
                            </div>
                        </div>
                    </div>
                    <div class="input-group mb-3">
                        <input type="password" name="password" class="form-control" placeholder="Password">
                        <div class="input-group-append">
                            <div class="input-group-text">
                                <span class="fas fa-lock"></span>
                            </div>
                        </div>
                    </div>

                    <div class="row">
                        <div class="col-4">
                            <button type="submit" class="btn btn-primary btn-block">Sign In</button>
                        </div>
                        <!-- /.col -->
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
<!-- Ionicons
<link rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">-->
<!-- icheck bootstrap -->

</body>
</html>
  • pom.xml
<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
 ....
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity6</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

    </dependencies>

我试过了:

  • 控制器
@GetMapping("/handleLoginErrors")
    ModelAndView handleErrors(ModelAndView modelAndView){
        
        modelAndView.setViewName("account/register");
        return modelAndView;

    }
  • 组态
@Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

        return http
                .authorizeHttpRequests()
                .requestMatchers("/login", "/register").permitAll()
                .requestMatchers("/account/**").hasRole("ADMIN")
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login").permitAll()
                .defaultSuccessUrl("/home")
                .failureUrl("/handleLoginErrors").permitAll()
                //.failureUrl("/login?error=true")/**/
                .and()
                .build();
    }

但是,关键是要将错误返回到注册页面,以便用户可以看到他在注册过程中犯了什么错误,也就是说,要将用户返回到注册表单,并在发现这些错误的字段前面指出错误。
谁知道如何显示错误?为什么会自动重定向到/login,而不是您指定的页面?

f1tvaqid

f1tvaqid1#

Spring默认情况下会重定向到login?error(GET),但是对于登录错误,你可以通过指定一个端点(url)来处理登录错误,这可以通过调用failureUrl来完成。

return http
            .authorizeHttpRequests()
            .requestMatchers("/login", "/register").permitAll()
            .anyRequest().authenticated()
            .and()
            .formLogin()
            .loginPage("/login").permitAll()
            .defaultSuccessUrl("/home")
            .failureUrl("/handleLoginErrors")
            .and()
            .build();

还应该添加与handleLoginErrors匹配的控制器方法。

@GetMapping("/handleLoginErrors")
 ModelAndView handleErrors(ModelAndView){
  mv.addObject("loginStatus", "failed");
  mv.setViewName("loginError"); the view name i.e loginError.html
 return mv;

}

Spring还提供了几种通用错误处理机制,更多信息可以在https://www.baeldung.com/exception-handling-for-rest-with-spring中找到。

相关问题