I'm using Spring-MVC and Spring-Security to develop a web application.
I'm using custom login with an AuthenticationProvider which in turn uses a UserDetailsService to match the data from the login form with the ones in the database.
I want to thrown 2 exceptions in the AuthenticationProvider, the first when the username is not present in the db, the other when the password is different.
What I'd like to do is to show in my web page the error message of the throwned exception (Wrong username or Wrong password), but I don't know where to use the catch block because the login flow is managed by Spring-Security
AuthenticationProvider
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Autowired
CustomUserDetailsService userDetails;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
Customer customer = userDetails.loadUserByUsername(username);
if(customer == null) {
throw new BadCredentialsException("Wrong username");
}
if(!password.equals(customer.getPassword())) {
throw new BadCredentialsException("Wrong password");
}
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority(customer.getRole()));
return new UsernamePasswordAuthenticationToken(customer, password, authorities);
}
@Override
public boolean supports(Class<?> clazz) {
return clazz.equals(UsernamePasswordAuthenticationToken.class);
}
}
Login page
[...]
<div class="form">
<h2>Login</h2>
<form th:action="@{/login}" method="POST" th:object="${customer}">
<input type="text" placeholder="Username" name="username" th:field="*{username}"/>
<input type="password" placeholder="Password" name="password" th:field="*{password}"/>
<button type="submit">Login</button>
</form>
</div>
[...]
spring-security.xml
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-4.0.xsd">
<http pattern="/resources/**" security="none" />
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/user/**" access="hasRole('USER')" />
<form-login authentication-failure-url="/login" login-page="/login"
login-processing-url="/login" default-target-url="/user" />
<logout invalidate-session="true" success-handler-ref="logoutSuccessHandler" />
</http>
<authentication-manager>
<authentication-provider ref="customAuthenticationProvider" />
</authentication-manager>
</beans:beans>
When using Thymeleaf, you can "catch" the authentication exception like this:
<p th:if="${param.error != null and session['SPRING_SECURITY_LAST_EXCEPTION'] != null}"
th:text="${session['SPRING_SECURITY_LAST_EXCEPTION'].message}">
Wrong username or password
</p>
You could probably use the standard DaoAuthenticationProvider instead of a custom class:
@Bean
public DaoAuthenticationProvider customAuthenticationProvider()
{
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setHideUserNotFoundExceptions(false);
return provider;
}
Note that it is generally not recommended to tell whether the username exists in the system when authentication fails.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With