我有一个注册页面。字段数据经过验证。当用户注册的控制器方法中捕获到错误并再次调用注册页面时(应该显示错误),会自动重定向到**/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,而不是您指定的页面?
1条答案
按热度按时间f1tvaqid1#
Spring默认情况下会重定向到login?error(GET),但是对于登录错误,你可以通过指定一个端点(url)来处理登录错误,这可以通过调用
failureUrl
来完成。还应该添加与handleLoginErrors匹配的控制器方法。
Spring还提供了几种通用错误处理机制,更多信息可以在https://www.baeldung.com/exception-handling-for-rest-with-spring中找到。