I'm currently working on a Spring Project and I completed a tutorial to allow new users to be registered and stored in a database.
My issue is that the tutorial used a Json request/Postman to add the new user but I am trying to adapt it to allow users to register themselves using details they input into a Thymeleaf HTML registration form.
The following Controller class successfully registers a user using Json/Postman requests:
@RequestMapping(path = "/api/v1/registration")
@RestController
@AllArgsConstructor
public class RegistrationController {
private final RegistrationService registrationService;
@PostMapping
public String register(@RequestBody RegistrationRequest request) {
return registrationService.register(request);
}
@GetMapping(path = "confirm")
public String confirm(@RequestParam("token") String token) {
return registrationService.confirmToken(token);
}
However my below attempt to make it use user imputed values from a Thymeleaf HTML form returns the following on submission of the form:
java.lang.IllegalArgumentException: rawPassword cannot be null
@Controller
@AllArgsConstructor
public class RegistrationController {
private final RegistrationService registrationService;
@GetMapping(path = "confirm")
public String confirm(@RequestParam("token") String token) {
return registrationService.confirmToken(token);
}
@GetMapping("/api/v1/registration/new")
public String ShowRegisterationForm (Model model) {
RegistrationRequest request = new RegistrationRequest();
model.addAttribute("request", request);
return "registration";
}
@PostMapping("/api/v1/registration")
public String registration(@ModelAttribute("request")RegistrationRequest request) {
registrationService.register(request);
return "redirect:/dish";
}
See my registrationRequest class below:
@EqualsAndHashCode
@ToString
@Getter
@NoArgsConstructor
public class RegistrationRequest {
private String restaurantName;
private String email;
private String password;
public RegistrationRequest(String restaurantName, String email, String password) {
super();
this.restaurantName = restaurantName;
this.email = email;
this.password = password;
}
}
See my HTML registration form below:
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Registration</title>
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
crossorigin="anonymous">
</head>
<body>
<!-- create navigation bar ( header) -->
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed"
data-toggle="collapse" data-target="#navbar" aria-expanded="false"
aria-controls="navbar">
<span class="sr-only">Toggle navigation</span> <span
class="icon-bar"></span> <span class="icon-bar"></span> <span
class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#" th:href="@{/}">Registration and
Login Module</a>
</div>
</div>
</nav>
<br>
<br>
<!-- Create HTML registration form -->
<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<!-- success message -->
<div th:if="${param.success}">
<div class="alert alert-info">You've successfully registered
to our awesome app!</div>
</div>
<h1>Registration</h1>
<form th:action="@{/api/v1/registration}" method="post"
th:object="${request}">
<div class="form-group">
<label class="control-label" for="restaurantName"> Restaurant
Name </label> <input id="restaurantName" class="form-control"
th:field="*{restaurantName}" required autofocus="autofocus" />
</div>
<div class="form-group">
<label class="control-label" for="email"> Email </label> <input
id="email" class="form-control" th:field="*{email}" required
autofocus="autofocus" />
</div>
<div class="form-group">
<label class="control-label" for="password"> Password </label> <input
id="password" class="form-control" type="password"
th:field="*{password}" required autofocus="autofocus" />
</div>
<div class="form-group">
<button type="submit" class="btn btn-success">Register</button>
<span>Already registered? <a href="/" th:href="@{/login}">Login
here</a></span>
</div>
</form>
</div>
</div>
</div>
</body>
</html>
See my RegistrationService class below:
@Service
@AllArgsConstructor
public class RegistrationService {
private final AppUserService appUserService;
private final EmailValidator emailValidator;
private final ConfirmationTokenService confirmationTokenService;
private final EmailSender emailSender;
public String register(RegistrationRequest request) {
boolean isValidEmail = emailValidator.test(request.getEmail());
if (!isValidEmail) {
throw new IllegalStateException("email not valid");
}
String token = appUserService.signUpUser(
new AppUser(request.getRestaurantName(), request.getEmail(), request.getPassword(), AppUserRole.USER));
String link = "http://localhost:8080/api/v1/registration/confirm?token=" + token;
emailSender.send(request.getEmail(), buildEmail(request.getRestaurantName(), link));
return token;
}
@Transactional
public String confirmToken(String token) {
ConfirmationToken confirmationToken = confirmationTokenService.getToken(token)
.orElseThrow(() -> new IllegalStateException("token not found"));
if (confirmationToken.getComfirmedAt() != null) {
throw new IllegalStateException("email already confirmed");
}
LocalDateTime expiredAt = confirmationToken.getExpiresAt();
if (expiredAt.isBefore(LocalDateTime.now())) {
throw new IllegalStateException("token expired");
}
confirmationTokenService.setConfirmedAt(token);
appUserService.enableAppUser(confirmationToken.getAppUser().getEmail());
return "confirmed";
}
See my AppUser class below
public class AppUser implements UserDetails {
@Id
// Auto generate primary key
@SequenceGenerator(name = "user_sequence", sequenceName = "user_sequence", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_sequence")
@Column(name = "Rest_ID")
private long id;
@Column(name = "Rest_Name")
private String restaurantName;
@Embedded
private Address address;
@Column(name = "Rest_Phone_Number")
private String phoneNumber;
@Column(name = "Rest_Password")
private String password;
@Column(name = "Rest_Email_Address")
private String email;
@Enumerated(EnumType.STRING)
private AppUserRole appUserRole;
private Boolean locked = false;
// don't enable user until email verification
private Boolean enabled = false;
public AppUser(String restname, String email, String pass, AppUserRole app) {
this.restaurantName = restname;
this.email = email;
this.password = pass;
this.appUserRole = app;
}
public Collection<? extends GrantedAuthority> getAuthorities() {
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(appUserRole.name());
return Collections.singletonList(authority);
}
@Override
public String getUsername() {
return email;
}
@Override
public String getPassword() {
return password;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return !locked;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return enabled;
}
//need reference to restaurant info but one to one relationship
//already defined by openingHour object in opening hour table
@OneToOne(
mappedBy = "appUser"
)
private OpeningHour opening;
}
Please see the Stacktrace of the error
There was an unexpected error (type=Internal Server Error, status=500).
rawPassword cannot be null
java.lang.IllegalArgumentException: rawPassword cannot be null
at org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder.encode(BCryptPasswordEncoder.java:107)
at com.bron.demoJPA.appuser.AppUserService.signUpUser(AppUserService.java:51)
at com.bron.demoJPA.registration.RegistrationService.register(RegistrationService.java:36)
at com.bron.demoJPA.registration.RegistrationService$$FastClassBySpringCGLIB$$12afc64f.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
at com.bron.demoJPA.registration.RegistrationService$$EnhancerBySpringCGLIB$$67a444d4.register(<generated>)
at com.bron.demoJPA.registration.RegistrationController.registration(RegistrationController.java:58)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1067)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:681)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:327)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:115)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:81)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:121)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:115)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:126)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:81)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:105)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:149)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter.doFilterInternal(DefaultLogoutPageGeneratingFilter.java:58)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter.doFilter(DefaultLoginPageGeneratingFilter.java:237)
at org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter.doFilter(DefaultLoginPageGeneratingFilter.java:223)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:218)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:110)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:80)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:55)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:211)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:183)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:540)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:382)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:895)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1722)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
I've spent hours on this but can't work it out. Maybe it is something to do with @RestController vs @Controller?
Any thoughts would be very much appreciated!
If you debug your controller method for /api/v1/registration
you will notice that all of the fields on RegistrationRequest
are null
because there are no setters.
The object is constructed using the no args constructor and then the fields are set using the setters. Since the setters don't exist, all fields, including the password are null
.
You can fix this by simply adding setters to RegistrationRequest
.