I have built an application by using Spring Boot and Thymeleaf. My application works as supposed in my localhost, but when I package it as a .war and deploy it in a test tomcat server, it prompts the login page and then either redirects me to the error page or brings me back to the login page.
I have tried multiple things, and I think that there is an issue with the way I am handling the formLogin()
inside my SecurityConfig.java class. More specifically, since Tomcat adds the base-url (e.g. from localhost:8080
to serverUrl:8080/reservation
) when uploading my app, the loginProcessingUrl class probably fails to identify the "/process-login" class located on the login.html page.
Please find below my SecurityConfig.java class
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private UserService userService;
private CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;
private AccessDeniedHandler accessDeniedHandler;
@Autowired
public SecurityConfig(UserService userService, CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler, AccessDeniedHandler accessDeniedHandler) {
this.userService = userService;
this.customAuthenticationSuccessHandler = customAuthenticationSuccessHandler;
this.accessDeniedHandler = accessDeniedHandler;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login-form-page","/resources/**").permitAll()
.antMatchers("/", "/new-reservation", "/your-reservations","/all-reservations",
"/change-password").hasAnyRole("ADMIN","EMPLOYEE","MANAGER")
.antMatchers("/users","/user-reservations","/arrival-date","/duplicate-reservations","/all-reservations","/registration**")
.hasAnyRole("ADMIN").and()
.formLogin()
.loginPage("/login-form-page")
.loginProcessingUrl("/process-login")
.successHandler(customAuthenticationSuccessHandler)
.permitAll()
.and()
.logout()
.logoutUrl("/login-form-page")
.permitAll()
.and()
.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
}
@Override
public void configure(WebSecurity web) {
web.ignoring()
.antMatchers("/resources/**", "/static/**");
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider auth = new DaoAuthenticationProvider();
auth.setUserDetailsService(userService);
auth.setPasswordEncoder(passwordEncoder());
return auth;
}
And here is a small sample of the login.html page.
<div class="form-container sign-in-container">
<form id="loginForm" name="regForm" th:action="@{/process-login}"
method="POST">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
<h1>Sign In</h1>
<!-- Login Error -->
<div th:if="${param.error}"
class="alert alert-danger col-xs-10">Wrong email and/or password</div>
<!-- Logout notify -->
<div th:if="${param.logout}"
class="alert alert-success 1 col-xs-10">You
have been logged out.</div>
All the .html pages are located in
-resources
-templates
Lastly, the only error I see in the logs is the following
DEBUG o.s.w.s.r.ResourceHttpRequestHandler - Resource not found
For anyone interested, I managed to solve the above issue. It seems like it was not a misconfiguration in the loginProcessingUrl()
class. Instead, the issue was the way a remote server handles the JSESSIONID
and csrf
.
More specifically, what I had to do is
cookieServerCsrfTokenRepository.setCookieHttpOnly(false);
http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
@Bean
public ServletContextInitializer servletContextInitializer(@Value("${secure.cookie}") boolean secure) {
return new ServletContextInitializer() {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.getSessionCookieConfig().setSecure(secure);
}
};
}
The secure.cookie
value needs to be set (in the application.properties
to true if you intend to utilise the HTTPs protocol.